Avatar
Jar of Fire Fly's ATtiny85
Aug 29th, 2021 | by: Sherm Stebbins | Views 212
Views 212

I've made many of these and given them away.. People keep asking for them.. So I keep making them..

They are a simulation of fire fly's in a Jar. Using Arduino Nano to program the ATtiny85. I use Charlie Plexing to get more lights lit then there are pins. This project is designed to be self contained and charge with solar panel. The electronics are put in 3d printed antique wood looking crate on top of the glass jar. I also use, to give it that special touch, some 3d printed fire flys with cut outs for SMD LED. With UV clear glue mixed with glow in the dark powder to attach LED to fire fly and it gives it that extra luminescence and realism. Then if you tilt the Jar the fire flys go crazy and all light up quickly.. Then they calm down. back to there normal simulated lighting.

ATtiny85, LM358 Op Amp, and tilt switch on proto board
TP4056 added battery charging and protection.
Solar Panel Added.
Fire fly's wired and LED's glued on with phosphor and UV glue.
3d Printing electronics case for jar.
Completed Jar of fire flys
Completed Jar of fire flys, lit
Completed Jar of fire flys, lit showing solar panel.
Schematics
Parts for this project.
  • Attiny85
  • 8 pin dip connector (unless your using SMD)
  • Arduino to program Attiny85 ( I use Nano, but most any can be used).
  • 5 1k resistors
  • 3 100k resistors (Used with Op Amp, Optional)
  • 2 10k resistors (Used with Op Amp, Optional)
  • 1 220k resistor (Used with Op Amp, Optional)
  • 2 100nf capacitors (1 used with Op Amp, Optional)
  • 6 led lights
  • 1 TP4056 (optional for Solar)
  • 1 5v solar panel ( optional, I use the 60mmx60mm)
  • 1 mosfet or transister ( I use the 2N7002 or 2N7000)
  • Tilt Switch
  • Op Amp ( optional, I use the LM358)
  • 1 glass Jar
  • 1 Lithium Battery ( sometimes I use Lipo 400ma)
  • 1 5817 Schottky diode (Optional, used with solar)

 

 


/*
Fireflies  

ATTiny85

Sherman Stebbins

Modified by Sherman Stebbins 11/5/17
updated 11-18-17 added sleep random to save power.
updated 7-1-21 updated for tilt switch
updated 7-31-21 added pinMode(piezoPin,INPUT_PULLUP); or 
DDRB &= ~(1 << DDRB4);  //set PB4 as input
PORTB = (1<<PB4);  //activate pull-up resistor for PB4

*/

// ATMEL ATTINY 25/45/85 / ARDUINO
//
//                           +-/-+
//          Ain0 (D 5) PB5  1|    |8  Vcc
// pinD     Ain3 (D 3) PB3  2|    |7  PB2 (D 2) Ain1 pinC
// piezoPin Ain2 (D 4) PB4  3|    |6  PB1 (D 1) pwm1 pinB
//                     GND  4|    |5  PB0 (D 0) pwm0 pinA
//                           +----+

#include <avr/sleep.h>    // Sleep Modes
#include <avr/power.h>    // Power management
#include <avr/wdt.h>      // Watchdog timer

int delaytime = 7;  //set delay time here, 1-9 (9 is max 8 seconds, 8 is 4 seconds, 7 is 2 seconds, 6 is 1 second, 5 is .5 seconds, 4 is .25 second etc)

// Pin assignments (3 Pin set up for 6 Fire Fly's)
const byte pinA = 0;
const byte pinB = 1;
const byte pinC = 2;
const byte piezoPin = 4;  // analog pin 2 */

// LED variables
byte currentLED;
byte previousLED = -1;
byte rampUpSpeed;
byte rampDownSpeed;
byte numBlinks;
int counter;

// Piezo variables
const int piezoBufferSize = 10;
int piezoValues[piezoBufferSize];
int piezoThreshold = 1; //originally 1
int piezoCounter = 0;

int randExcitedCounter=0;
int randExcited = 5;

// Behavior states
const byte BEGIN = 0;
const byte RAMP_UP = 1;
const byte BLINKING = 2;
const byte RAMP_DOWN = 3;
const byte EXCITED = 4;
byte CURRENT_ACTION = BEGIN;

void setup() {
  resetWatchdog (delaytime);  // do this first in case WDT fires
  // Flush any stray current from the ports
  randomSeed(analogRead(2));
  //Added for newer makes without external Pullup for tilt switch
  pinMode(piezoPin,INPUT_PULLUP); 
  turnOffAll();
}

void loop() {
  switch(CURRENT_ACTION) {
    
    // BEGIN =========================================
    // + Set up all the variables to flash new LED
    case BEGIN:
      // Choose a new LED
      previousLED = currentLED;
      
      do {
        currentLED = (int)random(1,7); //originally 1,6 but one light would not turn on 
      } while(currentLED == previousLED);
      //currentLED = 6;

      //added 11-18-2017 sleep
      random(5,9); //changed from 8 to 9 on 7/6/21
      goToSleep (delaytime);
      
      // Number of times to blink
      numBlinks = (int)random(1,6);
      
      // Fade in and fade out speed
      rampUpSpeed = random(6); //ORIGINALLY 6
      rampDownSpeed = rampUpSpeed*2;
      
      counter = 0;

      /////added 10-3-2019/// random excited
      if(randExcited == randExcitedCounter){
        CURRENT_ACTION = EXCITED;
        randExcitedCounter=0;
        randExcited = (int)random(17,25);//15,25);
      }else{
        CURRENT_ACTION = RAMP_UP;        
      }
      randExcitedCounter++;        
      //CURRENT_ACTION = RAMP_UP;
      /////////end random excited/////
      
      //CURRENT_ACTION = RAMP_DOWN;
      //counter = 255;
      break;
      
    // RAMP UP ========================================
    // + Fade in the LED
    case RAMP_UP:
      analogOn(currentLED, counter);
      delay(rampUpSpeed);
      
      if(counter >= 255) {
        CURRENT_ACTION = BLINKING;
        counter = 0;
      } else {
        counter++;
      }      
      break;
      
    // BLINKING ====================================
    // + Blink the LED a certain number of times
    case BLINKING:
      analogOn(currentLED, 0);
      delay(random(10,50));//ORIGINALLY 10,50
      analogOn(currentLED, 255);
      delay(random(30,100));//ORIGINALLY 30,100
      
      if(numBlinks > 0) {
        numBlinks--;
      } else {
        CURRENT_ACTION = RAMP_DOWN;
        counter = 255;
      }
      
      break;
      
    // RAMP DOWN ===========================================
    // + Fade out the LED
    case RAMP_DOWN:
      analogOn(currentLED, counter);
      delay(rampDownSpeed);
      
      if(counter == 0) {
        CURRENT_ACTION = BEGIN; 
        //delay(2000);       
      } else {
        counter--;
      }      
      break;
      
    // EXCITED =========================================
    // + Flash each LED, one at a time, six times in a row
    case EXCITED:
      for(int j=0; j<6; j++) {
        for(int i=1; i<=6; i++) {
          turnOn(i);
          delay(50);
          turnOff(i);
          delay(50);
        }
      }
      CURRENT_ACTION = BEGIN;
      break;
  }
  
  //checkPiezo();
  if (digitalRead(4)==LOW){   //LOW){
    CURRENT_ACTION = EXCITED;
  }
}

// Turn on a single LED to full brightness
void turnOn(byte led) {
  analogOn(led, 255);
}

// Turn off an LED completely
void turnOff(byte led) {
  analogOn(led, 0);
}

void turnOffAll() {
  digitalWrite(pinA, LOW);
  digitalWrite(pinB, LOW);
  digitalWrite(pinC, LOW);
}

// Write an analog value to an LED
void analogOn(byte led, byte value) {
  switch(led) {
    case 1:
      pinMode(pinA, OUTPUT);
      pinMode(pinB, OUTPUT);
      pinMode(pinC, INPUT);
      
      digitalWrite(pinA, LOW);
      analogWrite(pinB, value);
      break;
    case 2:
      pinMode(pinA, OUTPUT);
      pinMode(pinB, OUTPUT);
      pinMode(pinC, INPUT);
      
      digitalWrite(pinB, LOW);
      analogWrite(pinA, value);
      break;
    case 3:
      pinMode(pinA, INPUT);
      pinMode(pinB, OUTPUT);
      pinMode(pinC, OUTPUT);
      
      digitalWrite(pinC, HIGH);
      analogWrite(pinB, 255-value);
      break;
    case 4:
      pinMode(pinA, INPUT);
      pinMode(pinB, OUTPUT);
      pinMode(pinC, OUTPUT);
      
      digitalWrite(pinC, LOW);
      analogWrite(pinB, value);
      break;
    case 5:
      pinMode(pinA, OUTPUT);
      pinMode(pinB, INPUT);
      pinMode(pinC, OUTPUT);
      
      digitalWrite(pinC, HIGH);
      analogWrite(pinA, 255-value);
      break;
    case 6:
      pinMode(pinA, OUTPUT);
      pinMode(pinB, INPUT);
      pinMode(pinC, OUTPUT);
      
      digitalWrite(pinC, LOW);
      analogWrite(pinA, value);
      break;
  }
}

void checkPiezo() {
  if(piezoCounter < piezoBufferSize) {
    // Take a reading
    //pinMode(piezoPin,INPUT);
    int val = analogRead(piezoPin);
    piezoValues[piezoCounter] = val;
  } else {
    // Find average
    int avg = 0;
    
    for(int i=0; i<piezoBufferSize; i++)
      avg += piezoValues[i];
      
    avg = avg/piezoBufferSize;
    
    if(avg >= piezoThreshold) {
      for(int j=0; j<6; j++) {
        for(int i=1; i<=6; i++) {
          turnOn(i);
          delay(50);
          turnOff(i);
          delay(50);
        }
      }
    }
    
    // Reset counter
    piezoCounter = 0;
  }
  
  piezoCounter++;
}

void goToSleep (int dt)
  {
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);
  ADCSRA = 0;            // turn off ADC
  power_all_disable ();  // power off ADC, Timer 0 and 1, serial interface
  noInterrupts ();       // timed sequence coming up
  resetWatchdog (dt);      // get watchdog ready
  sleep_enable ();       // ready to sleep
  interrupts ();         // interrupts are required now
  sleep_cpu ();          // sleep                
  sleep_disable ();      // precaution
  //ADCSRA = 1; //hope it works -- no luck
  power_all_enable ();   // power everything back on
  }  // end of goToSleep
void resetWatchdog (int dt)
  {
  // clear various "reset" flags
  MCUSR = 0;     
  // allow changes, disable reset, clear existing interrupt
  WDTCR = bit (WDCE) | bit (WDE) | bit (WDIF);
  // set interrupt mode and an interval (WDE must be changed from 1 to 0 here)

  switch(dt){
    case 1:
      WDTCR = bit (WDIE) | bit (WDP0); // 32 milli seconds
      break;
    case 2:
      WDTCR = bit (WDIE) | bit (WDP1); // 64 milli seconds
      break;
    case 3:
      WDTCR = bit (WDIE) | bit (WDP1) | bit (WDP0); // .125 second
      break;
    case 4:
      WDTCR = bit (WDIE) | bit (WDP2); //.25 seconds
      break;
    case 5:
      WDTCR = bit (WDIE) | bit (WDP2) | bit (WDP0); // .5 seconds
      break;
    case 6:
      WDTCR = bit (WDIE) | bit (WDP2) | bit (WDP1); // 1 second
      break;
    case 7:
      WDTCR = bit (WDIE) | bit (WDP2) | bit (WDP1) | bit (WDP0); // 2 seconds
      break;
    case 8:
      WDTCR = bit (WDIE) | bit (WDP3); // 4 seconds
      break;
    case 9:
      WDTCR = bit (WDIE) | bit (WDP3) | bit (WDP0); // set WDIE, and 8 seconds delay
      break;
    default:
      break;
  }
  // pat the dog
  wdt_reset();  
}  // end of resetWatchdog

ISR (PCINT0_vect) 
 {
 // do something interesting here
 }  // end of PCINT0_vect
 
// watchdog interrupt
ISR (WDT_vect) 
{
   wdt_disable();  // disable watchdog
}  // end of WDT_vect


This project Has Optional add On's, you don't need to but I do include in the schematics to add Solar panel and charging for the Jar of fire flys. The schematics include 3 ports used on ATtiny. The code is also set for 3 ports. I use ports 0,1, and 2 (since they are next to each other). I first solder the Op Amp to the proto board with resisters and hooked pin 2 to Solar Positive via 1k resistor. Connect Diode to positive of Solar Panel, then to TP4056. Connect Positive and Negative OUT to Op Amp. Follow the schematics for the rest of the Op amp and Mosfet. I used an 8 pin dip connector for ATtiny85. I tied the drain of the Mosfet to use for ground of ATiny85. Without the ATtiny in its slot I could then hook up battery and make sure it powered up and by measuring Pin 8 and 4 on the dip connector. If you have the solar panel covered (in dark) then you should have power to these two pins. When you put light to the solar panel, you should not have voltage on pins 8 and 4.  If all good, program ATtiny85 with code provided. (see http://solosodium.github.io/2017-08-07-program-attint85-with-arduino-nano for how to program ATtiny85 with Nano )

Then finish adding 1k resistors to Pins 5, 6, and 7 on ATtiny for lights. I then soldered Female dupont cable connectors to the resister (3 port), I hooked the Charlie Plexed LED's to Male Cable connector (or you can just solder the wires to the resistors as per schematics). 

 

I used and Ender3 to print the electronics Box on top and 6 fire flys. (STL files included). I used UV Resin and Glow in the Dark powder to make a glow in the dark glue for holding LED onto fire fly adding to its realism.

Notice that not all resistors and Mosfet are not seen on top of the proto board.. That is because I decided to use SMD components underneath.

Hidden Resistors and Mosfet on under side of Proto board

Assemble and enjoy..

Assembled

Enjoy.......     

Sherm

0 Comments

Login or Sign Up to post comments on this tutorial.