Showing posts with label firefly. Show all posts
Showing posts with label firefly. Show all posts

Tuesday, February 2, 2010

Jar-O-Fireflies (Mark II)


I really enjoyed the process of making my first 'Jar-O-Fireflies' but I was a little disappointed with the performance (i.e. the software), especially the obvious flicker when the lights were pulsing on and off. I've learned a lot more about how to program the AVR uCs over the last few months (but still just the basics really). So, I decided to re-write the software and make a smaller, battery powered, version. I'm going to make another and put it in an empty Nuttela jar for the girls to use as a night light (I'm sure the girls will help me empty the jar).

The software took a little while to sort out but the hardware, on the other hand, only took about 2 hours to put together. Most of that was due to the fiddlyness of using magnet wire for the LED leads.

Below is a little video of it working. Yes, we have 'pet' ants at the moment - at least it was interested in what was going on...



The parts:
  • some magnet wire
  • coin cell holder (and coin cell to go with it)
  • perf board - I used a board with a semi breadboard layout (see picture below), but it wasn't much more help than a standard one other than having a common power and ground rail to connect everything to.
  • switch (one that fits onto a PCB/perf board)
  • 8-pin dip
  • 5 green LEDs (3mm)
  • ATtiny13

This is the layout I used (for the battery holder, DIP socket and switch):



The trickiest part was soldering the magnet wire onto the LEDs. I used a third-hand to hold all the pieces together. It helps that the magnet wire is quite stiff and bendable even though it's thin.


As you can see from the photo. After soldering on the wire I twisted the two strands together. After doing the 5 LEDs I realised that it'd be really nice to have 2 different colours of wire so that the positive and negative leads were obvious... oops. I ended up testing each pair of wires with my LED tester to determine which lead was positive and which was negative.

Then it's was just a matter of soldering on the LED leads to the board:


Then I glued everything onto the jar lid (using a hot glue gun):



Et voila!



Code
:

I've just started using code.google.com as a code repository. The google site supports subversion source control (and there's a good plug-in for eclipse called subversive). I'd been going back and forth between computers and it was turning into quite a headache trying to get the code in sync. Now I can just push and pull from the code repository.

Here's the location of the firefly code:
http://code.google.com/p/pboardman-avr/source/browse/firefly_jar/firefly_jar.c

Google code is no-more.  I've moved all the code to github.

https://github.com/paulboardman/avr/blob/master/firefly_jar/firefly_jar.c

This may change in the future as I tinker a bit more, so I'm including below the version of the code that was running when I wrote this post. This version required setting the fuse bits for the ATtiny13 to make it run at 8MHz. I also wrote a better software PWM routine (see the Atmel technote: "Low-Jitter Multi Channel Software PWM". Warning: PDF link).

There are a few enhancements that I have in mind like actually using a random number generator for generating random numbers... The reason I didn't was that I had trouble coming up with a method for generating a seed (without a differing seed, the pseudo random number generator would give the same sequence each time the AVR was switched on). My latest project (post forthcoming) saves the seed in EEPROM and increments it each time the AVR is booted up - this way the random number generator produces a different sequence each time. Anyway, here's the code:

/*
 * firefly_jar.c
 *
 * Distributed under Creative Commons 3.0 -- Attib & Share Alike
 *
 *  Created on: Feb 2, 2010
 *      Author: PaulBo
 */
#import <util/delay.h>
#import <avr/io.h>
#import <avr/interrupt.h>

#ifndef F_CPU
    #define F_CPU 8000000UL
#endif

//LED pin definitions
#define LED1    PB0
#define LED2    PB1
#define LED3    PB2
#define LED4    PB3
#define LED5    PB4
#define N_LEDS  5
#define ALL_LEDS (1 << LED1) | (1 << LED2) | (1 << LED3) | (1 << LED4) | (1 << LED5)

//Max value for LED brightness
#define MAX     100

#define PULSE_UP   1
#define PULSE_DOWN 0

volatile unsigned char buffer[N_LEDS];

//counter for use in updateLedState() and pulseLeds()
unsigned char i;

//structure for storing data on each LED
struct ledData {
    unsigned char mBrightness;
    unsigned int  mTime;
    unsigned char mPin;
    unsigned char mPulseDirection;
};

//set up some initial values
struct ledData led_data[] = {
        {0, 1000,  LED1, PULSE_DOWN},
        {0, 10, LED2, PULSE_DOWN},
        {0, 500, LED3, PULSE_DOWN},
        {0, 50, LED4, PULSE_DOWN},
        {0, 150,  LED5, PULSE_DOWN}
};

//return a random time interval from 0 to 255
int getTime()
{
    return TCNT0;//just read the current timer/counter value
}

void updateLedState()
{
    for(i = 0; i < N_LEDS; i++)
    {
        switch(led_data[i].mBrightness)
        {
            case MAX://led is on
                if(--led_data[i].mTime == 0)
                {
                    //decrement the brightness, this puts the LED in a
                    //pulse state
                    led_data[i].mBrightness--;

                    //specify the "down" direction for pulsing
                    led_data[i].mPulseDirection = PULSE_DOWN;
                }
                break;
            case 0://led is off
                if(--led_data[i].mTime == 0)
                {
                    //increment the brightness,this puts the LED in
                    //a pulse state
                    led_data[i].mBrightness++;

                    //specify the "up" direction for pulsing
                    led_data[i].mPulseDirection = PULSE_UP;

                    //set the ON time
                    led_data[i].mTime = getTime() + 1;
                }
                break;
            default: //pulse state
                if(led_data[i].mPulseDirection == PULSE_UP)
                {
                    led_data[i].mBrightness++;
                }
                else
                {
                    if(--led_data[i].mBrightness == 0)
                    {
                        //set the OFF time - make this longer than the on time
                        led_data[i].mTime = (getTime() + 1) * 5;
                    }
                }
                break;
        }
    }
}

void init_timers()
{
    TIMSK0 = (1 << TOIE0);         // enable overflow interrupt
    TCCR0B = (1 << CS00);          // start timer, no prescale

    //enable interrupts
    sei();
}

void init_io()
{
    //set all LED pins as outputs
    DDRB |= ALL_LEDS;
    PORTB &= ~(ALL_LEDS); //off to start
}

void setup()
 {
    init_io();
    init_timers();
}

int main(void)
{
    setup();

    //infinite loop
    while(1)
    {
        updateLedState();
        _delay_ms(10);
    }
}

/*
 * Timer/Counter overflow interrupt. This is called each time
 * the counter overflows (255 counts/cycles).
 */
ISR(TIM0_OVF_vect)
{
    //static variables maintain state from one call to the next
    static unsigned char sPortBmask = ALL_LEDS;
    static unsigned char sCounter = 255;

    //set port pins straight away (no waiting for processing)
    PORTB = sPortBmask;

    //this counter will overflow back to 0 after reaching 255.
    //So we end up adjusting the LED states for every 256 overflows.
    if(++sCounter == 0)
    {
        for(i = 0; i < N_LEDS; i++)
        {
            buffer[i] = led_data[i].mBrightness;
        }
        //set all pins to high
        sPortBmask = ALL_LEDS;
    }
    //this loop is considered for every overflow interrupt.
    //this is the software PWM.
    if(buffer[0] == sCounter) sPortBmask &= ~(1 << led_data[0].mPin);
    if(buffer[1] == sCounter) sPortBmask &= ~(1 << led_data[1].mPin);
    if(buffer[2] == sCounter) sPortBmask &= ~(1 << led_data[2].mPin);
    if(buffer[3] == sCounter) sPortBmask &= ~(1 << led_data[3].mPin);
    if(buffer[4] == sCounter) sPortBmask &= ~(1 << led_data[4].mPin);
}

Wednesday, June 17, 2009

Solar powered fireflies

I've been wanting to make something solar powered for some time & figured the firefly kit would be perfect to upgrade to solar power.

I just came across some cheap, second hand, solar powered light fixtures from allelectronics, which is a great place to pick up bits and bats for your electronic projects. Here's the actual solar lights I bought - solar-cell w/charging circuit. As the description says, they come with two solar panels, an LDR, a charging circuit (including - old - batteries) and some LEDs (one LED with this particular one and three with a different product).



As you can see from the pictures, all the components are easily acessible; these seemed perfect for hacking.

My first attempt was pretty simple, I just de-soldered the battery wires from my old JarOFireflies project, snipped the leads to the LED on the solar light kit and soldered the two kits together (the leads to the LED actually have a + and - designation printed on the board).


The LED wires are solid core and quite thick so there's no worry about them snapping under the strain and it made positioning the firefly board easier too. Speaking of which, I cut down the board to a more reasonable size for this project.

I didn't know if the batteries on the solar kit were duff so I left the hybrid out in the sunshine for a full day to charge then brought it indoors to see if it works:


And it does! What a pleasant surprise for something to work first time!

I think I'll cut a hole in the original jar's lid and place the kit in it more securely. Might be nice just to put it out in the garden to confuse the next door neighbours cats who seem to prefer our back yard to their own...

Saturday, May 2, 2009

Jar o' Fireflies

I've seen a few good examples of micro controller controlled fireflies "in a jar" and I wanted to try this for myself. This is by no means a new idea (just take a look at this youtube search and you'll see tons of examples) but I like the idea and I'd already got the basic set-up (see my previous post on AVR programming). All I needed to do was work out the programming (the wiring seems pretty obvious - wire LEDs to uC, wire uC to battery).

What I wanted to do was use all the ATtiny13's 6 output pins to control 6 "fireflies". I'd be happy with just randomly pulsing green LEDs, but I thought it was important to have the LEDs pulse on and off rather than just switch on an off. Here's a youtube video showing fireflies in New York as an example of how the real thing looks. I have a very fond memory of sitting out on a boulder with my brother in Nepal and looking out at a hillside with thousands of fireflies blinking away; we stayed there for ages captivated by the hypnotic display.

So, the standard way to create pulses with an AVR seems to be with pulse width modulation (PWM). Unfortunately mine only has 2 PWM outputs and I wanted to use the parts on hand. A quick search brought up a few possible solutions, but this post on Kevin Mehall's blog was the winner for me. I ended up implementing a software PWM solution based on the code Kevin made public - thanks, Kevin! I've not quite worked out the best way to post code on blogger, when I do I'll post the actual code.

Here's an overview of the parts that went into the project:


The jar was found in the local Savers, the perf board was bought on eBay as were the IC socket, uC, green LEDs and battery pack (all in batches of 10 or more). The wire on the bottom left was scavenged from a broken kids princess wand. The hair clip was an interloper.

My lovely missus decided to improve my project by drawing a few fireflies on shrinkydinks so that I could glue them onto the LEDs after the project was finished:



Here's some photo pr0n of the project in various stages of completion:





And here's something we bought to scare the fireflies:


We got these from Lowes; I think the kids enjoy the venus fly traps much more than the fireflies... c'est la vie.

And, finally, a little video. The flicker isn't actually as bad in real life as it appears in the video, hopefully I'll find a way to remove it completely.