ATTiny10-based Animated Necklace Pendant

Last summer I bought a Adafuit iNecklace for my daughter.  She loved it, but it got me starting thinking about using the ATTiny10 to make some electronic jewelry of my own design.  I decided to create something a bit more geeky, so I started experimenting with driving Charlieplexed LEDs using the ATTiny10.  The ATTiniy10 has 4 available I/O pins so, in theory, I could drive up to 12 LEDs.  However, it turns out that the RESET line has a weaker drive transistors on its outputs, which makes for uneven illumination of the LEDs.  So, I decided to only use the three I/O pins that have strong outputs, which let me independently drive 6 LEDs.  I decided to go with programming the LEDs in a rotating, "chaser" animation, which creates a very cool effect, as you can see in the following video:

The effect is actually much more striking when viewed directly, as the red LEDs seem to wash out in the video capture.  In real life they're much more intense, especially in subdued lighting.

Schematic

The circuit for the necklace pendant is very simple, as shown by the followig schematic diagram.  It consists of six SMD LEDs, three 1K SMD resistors, the ATTiny10 (or any of it's three, cheaper variants) and a CR927 type lithium button cell battery.

PCB Layout

The LEDs and resistors I chose are all 0603 size, as these are the smallest SMD components my tired old eye can deal with.  However, using them I was able to squeeze the whole design down to a .6 inch diameter PCB which I fabbed through the ever fabuluos OSH Park for a total cost of $1.80 for three boards.  I was able to build a battery holder onto the reverse side of the board using a Keystone Model 3031 coin cell cup, which solder into the two large holes on the PCB.  The reverse side (righthand image below) has the large pad for the negative side of the CR927 battery.  You turn it on by sliding in the battery and turn it off by sliding (or poking) it back out.  The PCB also has two smaller holes near the D1 LED, which permit you to attach a chain, or piece of string to create a necklace.  Or, you might use two of them to create some very geeky earrings.

   

Programmer Mod

The tricky part of the project was figuring out how to program the ATTiny10 once the whole circuit was soldered together.  To make this possible, I had designed the board to have five contact pads (the 5 small, circulr pads that surround U1 in the above picture) to which I could connect my new ATTiny10 Programmer design I created as part of my ATTiny10 C IDE and Improved Device Programmer project.  To do this, I also designed a small PCB that uses pogo pins to press against these 5 contact points during programming.  This board was designed to connect to the 6 pin DIP socket in the PROGRAM position on my new

ATTiny10 Programmer shield.  However, try as I might, I could not get my new programmer to recognize the ATTiny10 chip and program it.

After much headscratching, I finally reliazed that TPI protocol used to program the ATTiny10 assume that the TPIDATA line on pin 1 needs to be pulled up by an internal resistor to make the pin work as a bidirectional I/O pin.  However, the Charlieplexed LEDs and 1K resistors presented too much of a "sink" load for this resistor to overcome.  The solution was to increase the pullup strength by adding a 1K resistor from pin 1 to +5 (pin 5) on the programming adapter (not on the necklace pendant itself, as this would then interfere with the Charlieplexed LEDs), as is shown in the following schematic:

The Programming Adapter

The pogo pin-based programming adapter I designed to connect to the necklace pendant is shown in the photo below.  The 6 pin DIP socket in the right connects to the programming circuit via a 6 pin ribbon cable.  I originally designed this board to have large holes that match the holes in the pendant PCB thinking that I could use these holes to build an alignment guide to help hold the pins in position when I need to program the board.  However, I found that with a steady hand, it was fairly easy to just hold the pogo pin board in contact with the pads on the pendant PCB.  While you can;t see it, the other side of the board contains the 1K resistor used to boost the pull up current.

Pogo pin Programming Adapter

The finished pendant, front and rear views

Programming the Animation Effect

The code to create the animation effect is fairly simple but, to reduce power, I coded it to nru the ATTiny10 at a clock speed of 1 MHz, but put the processor to sleep and used the timer overflow to wake it up for each cycle of the animation.  The net result is that it uses about 800 microAmperes when running at 3.3 volts.  This is only slightly more than the 540 microAmperes it takes to drive one of LED in the same circuit.  The source file is downloadable below, but here's the code for browsing:

#pragma clock 8000000

#pragma chip attiny10

//           +====+

//  PWMA/PB0 |*   | PB3 (RESET)

//       GND |    | Vcc

//  PWMB/PB1 |    | PB2 (CLKO)

//           +====+

#include <avr/interrupt.h>

#include <avr/io.h>

#include <avr/sleep.h>

#define H2 0x44

#define H1 0x22

#define H0 0x11

#define L2 0x04

#define L1 0x02

#define L0 0x01

#define XX 0x00

unsigned char seq = 0;

void setLEDs (unsigned char out) {

  DDRB  = out & 0x0F;

  PORTB = out >> 4;

}

int main (void) {

  // Set clock to 8 MHz

  CCP = 0xD8; // Unprotect CLKPSR reg

  CLKPSR = 0x00; // Divide by 1

  // Calibrate Oscillator

  OSCCAL = 0x58;

  // set PORTD for output

  DDRB = 0x07; // PB0-2 are outputs

  PORTB = 0; // Disable pullups

  // Setup Timer0 overflow interrupt

  TCCR0A = 0x00; // normal counter operation

  TCCR0B = 0x02; // timer clock = 1 MHz / 8

  TIMSK0 = 0x01; // enable overflow interrupt

  sei(); // Enable Global Interrupts

  while (1) {

    // Wait for interrupt

    sleep_mode();

  }

}

ISR (TIM0_OVF_vect) {

  switch (seq) {

  case 0:

    setLEDs(H2 | L1 | XX); // 0 

    break;

  case 1:

    setLEDs(L2 | H1 | XX); // 1

    break;

  case 2:

    setLEDs(XX | L1 | H0); // 2

    break;

  case 3:

    setLEDs(XX | H1 | L0); // 3

    break;

  case 4:

    setLEDs(H2 | XX | L0); // 4

    break;

  case 5:

    setLEDs(L2 | XX | H0); // 5

    break;

  }

  seq = (seq + 1) % 6;

}