Find all your DIY electronics in the MakerShed. 3D Printing, Kits, Arduino, Raspberry Pi, Books & more!

This is a trivial little project to create the appearance of something carnivorous… lurking in the dark… with intent. Half a dozen of these in the bushes or windows gives some creepy background ambiance.make_halloweenbadgeV2 (1)

A very basic Atmel microprocessor, the ATTiny45, is programmed to make two LEDs slowly dim, brighten, and occasionally “blink” by turning the LEDs off and on. The blinking interval is pseudo random, controlled by an eight-bit linear feedback shift register (LFSR — see the Wikipedia article describing them. There’s a minimum blink time, so that the LEDs don’t turn off more often than every three seconds or so.

Brightness is controlled using the Pulse Width Modulation (PWM) facility of the ATTiny45. An interrupt is generated by an internal timer overflow about eight times a second. The interrupt handler sets a tick_flag and checks to see if it’s time to blink. If it is, it sets a blink_flag as well.

The main loop checks the flags, increases or decreases brightness on each tick by modifying a comparator register, and blinks each time the blink_flag is set. See the code file BE-45.c for details. BE-45.c and a Makefile are in the Girhub repository here:  https://github.com/BradBrand/BlinkingEyes

Blinking Eyes schematic

The Blinking Eyes schematic with an optional on-off switch.

The circuit is trivial. In addition to the programmed ATTiny45, there are two LEDs with their attendant resistors and a capacitor to smooth the power provided by a 2032 coin battery.

The links in the parts list are for examples only.  There are many cheaper options available, particularly on eBay.

To show the simplicity of the circuit, I wired up an example on a breadboard. It’s just the microprocessor, two resistors, two LEDs, and the capacitor (see the picture below). The resistors are connected to pins 5 and 6 on one side and to the positive (longer) leg of the LEDs on the other. The negative (shorter) leg of the LEDs is attached to ground.

Power is provided by a 2023 battery wired up throwie style; a capacitor connected to power and ground keeps the power clean. Two wires for power (pin 8) and ground (pin 4) supply the microprocessor, and assuming that it’s already programmed, this is all that’s needed.

The board is actually the most expensive component. Since I didn’t want custom-made boards in quantity, I decide to use the Evil Mad Scientist ATMegaXX8 target board. It’s relatively cheap in quantity, and there’s a good blog article about adapting various breakout boards for alternate processors.

Screen Shot 2013-10-17 at 3.32.52 PM

A breadboard version shows the simplicity of the circuit.

The board also makes adding a six-pin programming header very convenient which let me use my Adafruit USBTinyISP to program the microprocessor. Three wires are all that’s needed to turn it into the perfect ATTiny45 development board.

Below are the step-by-step instructions for creating a more permanent version that will better stand up to being cable-tied to the bushes.

Steps

Step #1: Gather up the parts and solder in the socket and LEDs.

PrevNext
Spooky Blinking EyesSpooky Blinking EyesSpooky Blinking EyesSpooky Blinking Eyes
  • There are only eleven components in this project, and three of them are optional. The 3v, 2032 battery-based, throwie-wired power supply is on the left, and the breakout board is on the bottom. The rest are Top row: Two red LEDs, with forward voltage of 1.9-2.0 at 20 mA. Middle row: Two 68 ohm resistors (56 ohm would be better) and the optional push-button switch. Bottom Row: The ATTiny45, an optional socket, an optional 6-pin programming header, and a 100nF (0.1uF) capacitor.
  • Solder the 8-pin socket in first. This will serve as a reference for wiring everything else up. Pins 5, 6, and 7 (the right three upper pins) align with SCK, MISO, and MOSI, and that takes care of three of the four ISP logic connections. Pin 1 (reset) is the fourth; we'll fix that later. Pin 8 (upper left) is power. Pin 4 (lower right) is ground. Pins 5 and 6 (upper right) are also the Output Compare pins (OC0A and OC0B. These are part of the Pulse Width Modulation facility. The output is controlled by software writing to the control registers OCR0A and OCR0B.
  • Next, wire up the LEDs. Pins 5 and 6 are wired to the third column in. Each resistor bridges the third column in with the second column in. The long leg of each LED is soldered to the second column in, and the short leg is in the outermost column. Finally, the outermost column is wired to ground. The LED could be soldered directly to ground, but it doesn't sit as well that way.

Step #2: Solder up the power wiring.

PrevNext
Spooky Blinking EyesSpooky Blinking EyesSpooky Blinking EyesSpooky Blinking Eyes
  • The power for the microprocessor is run from Vcc at the top, through the push button switch, to pin 8 of the microprocessor. The switch is six-pin, and closing the switch connects the middle pin on each side to the outer pins.
  • Connect GND (ground) at the top to pin 4 of the microprocessor. Solder the 100 nF capacitor into the C3 holes in the upper right. This bridges the power supply wires and keeps the voltage stable.
  • Solder in the wires for the battery and finish the power supply with a little electrical tape.

Step #3: Finish with the programming logic.

PrevNext
Spooky Blinking EyesSpooky Blinking EyesSpooky Blinking EyesSpooky Blinking Eyes
  • There are two pieces to the programming circuitry. The first is the gray wire (toward the bottom) connecting the RESET pin on the microprocessor to the RESET column on the ATMegaXX8 pin-out. The second is the 6-pin programming header in the upper right corner.
  • I use the CrossPack-AVR tool chain on a Mac and a USBTinyISP to compile and load the code.
  • The final board. Eliminating the switch and the programming header also eliminates two wires. The microprocessor can be soldered directly to the board, but either the socket or the programming header need to be used to reprogram the microprocessor.

Brad Blumenthal

Over the years, I've been a British civil servant, assistant professor, museum exhibit developer, independent consultant, and corporate ladder climber -- roughly in that order. I'm passionate about everything involving computers and computing, and I'm lucky to make my living at it.


Comments

  1. Mike says:

    Great project Brad. I’m looking to port this to a 8 MHz ATTiny85. Would you be able to provide the timer timings for a processor going a bit faster than yours :)

    1. Thanks, Mike.

      I’m not sure exactly what you’re asking. The ATTiny85 is exactly the same as the ATTiny45, except that it has twice as much memory, 8k v. 4k program flash in particular. They both have the same clock speeds. The biggest difference in the ATTinyX5 series clock speed is between the low-power (‘V’) parts and the regular parts.

      Therefore, the code should work straight out of the box for the ATTiny85, provided you change the value of DEVICE in the Makefile to ‘attiny85′. Running ‘make install’ will set the clock speed of the ATTiny85 to the same 128kHz frequency I used in the ATTiny45. This is done in the FUSE settings in the Makefile:

      FUSES = -U lfuse:w:0xd4:m -U hfuse:w:0xd7:m -U efuse:w:0xff:m

      See the insanely useful fuse calculator here: http://www.engbedded.com/fusecalc and note that you can put fuse values in the bottom box (Low == D4, High == D7, Extended == FF), “Apply” those values, and the features and settings boxes above will be updated to show you what a fuse setting does. That is, you can calculate from settings to features as well as from features to settings.

      If you use the default fuse settings — Low == 62, High == DF, Extended == FF — then the clock will run at 8MHz, that is, 64 times faster than 128kHz. To make that work, you would need to multiply all the counts by 64.

      The biggest change is that you would have to add a tick_count variable that would be incremented every time the interrupt routine is called, and the tick_flag would only be set when the tick_count hit 64 (instead of the tick_flag being set every time the interrupt routine is called). You would need to reset the tick_count every time the tick_flag is set as well.

      You would also need to increase the size of the blink_count variable to 16-bits (uint16_t) and increase the value from the LFSR by 64x when loading up the blink_count. You could do that by either multiplying the value of the 8-bit LFSR by 64 (left shift 6 bits) or by using a 16-bit LFSR and clearing the 2 high-order bits (which would give you a little more variation in blink intervals).

      More than you wanted to know?

      1. Mike says:

        Actually very useful. I am looking at using an ATTiny85 already programmed with a arduino-like bootloader (Adafruit Trinket – https://www.adafruit.com/products/1500). These run stock at 8 MHz at 3,3 volts.

        With the tick count method counting to 64 then incrementing tick flag, would the timer setups remain the same (that is my weakness, I’m good at programming and hardware but still learning AVR low level timer and other hardware setup). I started to set a divide by 64 in the timer but without more knowledge, I’m poking the bear.

        Shifting LFSR appears straightforward (probably until I try to implement) – generating 16 bit LFSRs I would have to read up more on the methodology. With the ’85 I have more code space.

        Thank you for helping me with my folly, Mike

      2. Mike says:

        // Blinky Eyes coded for 8 MHz ATTiny85

        #define F_CPU 8000000UL

        #include
        #include
        #include
        #include
        #include

        uint8_t eyes_open;
        volatile uint16_t blink_count; // 16 bits for 8 MHz processor count
        volatile uint8_t blink_flag;
        volatile uint8_t tick_count;
        volatile uint8_t tick_flag;
        volatile uint8_t getting_brighter;
        const uint8_t min_bright=16;
        const uint8_t max_bright=128;
        volatile uint8_t brightness;
        uint16_t lfsr; // Linear Feedback Shift Register (16 bits for 8 MHz)
        const uint8_t min_blink = 2048u; // don’t blink more than once every 3 secs or so (was 32)

        ISR (TIMER1_OVF_vect) {
        TIMSK &= ~_BV(TOIE1); // I have this thing about shutting off interrupts
        sei(); // … but only the ones I’m servicing

        tick_count++;
        if(tick_flag >= 64) {
        tick_flag = 1;
        blink_count–;
        if (!blink_count) {
        blink_flag = 1;
        }
        tick_count = 0; // reset count back to zero
        }
        TIMSK |= _BV(TOIE1);
        }

        void init_timer0_PWM(void) {
        DDRB |= (1<<DDB0) | (1<<DDB1); // Data direction even for PWM
        // Setting up the Timer / Counter Prescalar and Clock Source (TTCR0A/B)
        TCCR0A |= (1<<WGM01) | (1<<WGM00); // Fast PWM
        TCCR0A |= (1<<COM0A1) | (1<<COM0B1); // Clear on match; set at BOTTOM
        TCCR0B |= (1<<CS00); // No pre-scaler (tried CS02 also, blinks once a sec

        brightness = max_bright;
        OCR0A = brightness; // OCR0A/B are the registers that control PWM on
        OCR0B = brightness; // pins 5 and 6 (OC0A/B)
        getting_brighter = 0;
        }

        void setup() {

        // Timer1 set to CK/64 or about 256 counts * ~10 (8) hZ at 128KhZ clock rate
        TCCR1 |= _BV(CS12) | _BV(CS11) | _BV(CS10); // was _BV(CS12)
        TIMSK |= _BV(TOIE1); // Enable Timer/Counter1 Overflow Interrupt

        // Turn on the eyes
        eyes_open = 1;
        blink_flag = 0;
        init_timer0_PWM();
        lfsr = (23 <> 1) ^ (-(lfsr & 1u) & 0xB400u);

        tick_count = 0; // for 8 MHz AVR, we will increment tick_flag oce every 64 counts

        sei(); // Release the hounds (enable interrupts)
        }

        void loop() {

        if (tick_flag) {
        tick_flag = 0;
        if (blink_flag) {
        blink_flag = 0;
        if (eyes_open) {
        eyes_open = 0;
        // Turn eyes off by setting compare output mode to normal operation
        TCCR0A &= ~(1<<COM0A1) & ~(1<<COM0B1);
        blink_count = (lfsr & 0×0001) + 1; // off for 1-2 ticks
        }
        else {
        eyes_open = 1;
        // Turn eyes on by setting mode to fast PWM. (Eeek!)
        TCCR0A |= (1<<COM0A1) | (1<> 1) ^ (-(lfsr & 1u) & 0xB400u);
        }
        }
        else { // One “tick,” but we didn’t blink…
        if (getting_brighter) {
        brightness++;
        brightness++;
        OCR0A = brightness;
        OCR0B = brightness;
        if (brightness >= max_bright)
        getting_brighter = 0;
        } else {
        brightness–;
        brightness–;
        OCR0A = brightness;
        OCR0B = brightness;
        if (brightness <= min_bright)
        getting_brighter = 1;
        }
        }
        }
        }

  2. Mike says:

    Sorry for the code mess, see http://21stdigitalhome.blogspot.com/ for the mods in red.

    1. Brad Blumenthal says:

      There’s quite a lot going on here, but the very short answer to your original question is that clock speeds can be accommodated simply by adjusting the Timer1 pre-scaler. This is done by writing bits CS10-CS13 into the TCCR1 (Timer / Counter Control Register 1). See pp. 89-90 of the ATTiny25/45/85 document here: http://www.atmel.com/Images/Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf

      That’s the answer I should have given you before. My apologies. Pretty much everything I wrote in my previous comment was either unnecessary or just plain wrong. Sigh.

      In the original 128kHz clock speed version, the Timer1 pre-scaler is set to 64, which gives a tick of Timer1 2,048 times a second (128K / 64). Timer1 counts from 0 to 255 before generating an interrupt, so the interrupts come about 8 times a second. The pre-scaler is set in this line:

      TCCR1 |= _BV(CS12) | _BV(CS11) | _BV(CS10); // setting these four bits to 0111

      If the clock speed is cranked up to 8MHz (64x 128k) the interrupts can be kept at 8 per second by changing the pre-scaler to 4,096 (64 * 64). That would use this line:

      TCCR1 |= _BV(CS13) | _BV(CS12) | _BV(CS10); // setting the same four bits to 1101

      That’s it. I’ve actually made the changes to one of my boards and not to the other and verified that they are more or less in sync (drift being what it is).

      A couple of other corrections:

      * The fuse settings I listed as the defaults in my previous comment (62, DF, FF) set the clock speed to 8MHz, but also divide the clock speed by 8 internally. That can be handled by setting the Timer1 pre-scaler to 512. I’ve also verified this empirically.

      * Although the code modifications you’d started turned out to be unnecessary, you were headed in the right direction. However, I steered you wrong on the LFSR. Since you had already slowed down the tick rate with the tick_count variable, you didn’t need to multiply the blink_count by 64 (my bad). I’ve also verified that your tick count modifications work at 8MHz if you use the original 8-bit LFSR code.

      I’m curious to see if the Interrupt Service Routine in the original code will work in the Arduino runtime code you started. I’ll have to dig out my Uno and check it out.

      1. Mike says:

        Yes, I originally started with the scalar tweeks but again, that is where I am but an apprentice. So if I have it right, I can keep the tick count in the ISR (effectively working every 64 calls) plus TCCR1 |= _BV(CS13) | _BV(CS12) | _BV(CS10); for the prescaler. I’ll change the LFSR back. I’ll see if that gives the same effect as your original. The current Unos work at 16 MHz I think so you’d have to take that into account if you dust it off. Again my sincere thanks, Mike

        1. Brad Blumenthal says:

          Actually, you don’t need the tick_count variable at all. Go back to the original code from the Github repository.

          Take your clock speed and divide it by 2,048. That gives you the pre-scaler you want. Look up the pre-scaler in the right-hand column of the table on pp. 89-90 of the ATTiny25/45/85 data sheet (URL above). Read the bits to be set (CS10-CS13), and replace them in the line

          TCCR1 |= _BV(CS12) | _BV(CS11) | _BV(CS10);

          (that’s the first line of the main() routine).

          That’s it. You’re done. This approach will handle clock speeds up to 32MHz.

          (By the way, if you’re wondering where the 2,048 comes from, it’s the 256 times that Timer1 ticks before it generates an interrupt times the goal of generating an interrupt 8 times a second).

          Again, apologies for making this so much more confusing than it needed to be. I certainly learned a couple of things, though. : – )