ATtiny13 12-LED blinker with motion detection and scripted blinks

ATtiny13 12-LED blinker with motion detection and scripted blinks


{adinserter Internal_left}
I can hear you screaming:”Not another LED blinker!” and yet here it is, packaged into yet another transparent Ferrero Rocher chocolate box, just like the first one. Why another ATtiny13 LED project? I needed to change the software to add a new feature and I had another box stashed away after Christmas – that alone should have been a reason enough 🙂 Additionally, I have to admit right here that there will likely be one more post that includes an ATtiny13, LEDs and chocolates before I let it go (soon, I promise) and move onto more serious things, such as the DVD-CNC project that’s been languishing on and off my workbench for more than a year now.

That said, if you are still interested in programming ATtiny13 with the Arduino IDE to blink 12 Charlieplexed LEDs, respond to motion (shaking) and have the LED on/off sequence scripted in an orderly fashion rather than random or simply 1 through 12, then read on!

Circuit schematics for the ATTiny charlieplexed LED chocolate box project

Circuit schematics for the ATTiny charlieplexed LED chocolate box project

Circuit schematics for the ATTiny charlieplexed LED chocolate box project[/caption]The circuit itself is exactly the same as in the first ATtiny13 LED blinker here and so I simply repost the same schematics diagram. Please note, however, that the new software will control all 12 LEDs so never mind the “not used in this project” label next to LED11 and LED12 – all 12 LEDs are used here.
The construction of the project is slightly different this time, too. I had a number of SOIC SMD-type ATtiny13 chips that I wanted to use and the much smaller SMD package looked much more appropriate for the project. Additionally, since I built the first chocolate LED blinker, I’ve stocked up on 3V battery holders – the part that was holding me back at the time – and wanted to use 3V power here.

All parts for the ATtiny13 12-LED blinker project

All parts for the ATtiny13 12-LED blinker project

There is just a handful of parts needed for the project and perhaps about two hours worth of your time.

Mount Attiny13 onto the carrier board

Mount Attiny13 onto the carrier board

If you are using the surface mounted version of ATtiny13, a carrier board such as the SOIC-DIP8 adapter from Schmartboard, which is what I used. This particular one is very nice because it has four additional plated holes not connected to anything but allowing you to solder a through-hole component to it. I used if to mount the ball switch – very convenient! Also, by chance, the distance between the holes connected to GND and VCC (4 and 8) pins of ATtiny are almost equal to the distance between the two pins of the 2032 3V battery holder. I had to bend one of its legs just a bit to fit into the holes on the SMD adapter. If you have an 8-pin SOIC AVR programming adapter, you could have programmed the ATtiny13 in the adapter and solder it in place already programmed. I did not have such adapter and I also anticipated to change program many times before the project was over, so I soldered an ICSP programming connector, right ontop of ATTiny13’s legs – an optional step you can omit if you’re doing just one of these blinkers.
Circuit is built around the ATtiny13 mounted on the SMD adapter

Circuit is built around the ATtiny13 mounted on the SMD adapter

Here is a picture of the complete circuit built around the SOIC-DIP8 adapter. The battery holder (large black circle) is facing in the opposite direction while its pins are soldered from the face side. The black electrolytic-capacitor-looking part is the ball switch. Its legs are not bent into shape on this photo yet, but when you make the final positioning of the circuit inside the box, be mindful of how the ball switch is positioned. It is most sensitive when the length of the cylinder is parallel to the surface of the table the box rests on. The ball inside of the cylinder is very unstable and will move around from the smallest vibration. You do not want it to fire off too often, however, perhaps only when someone takes the box into their hands and therefore tilts it some 5-10 degrees towards them. You also want to make sure that the ball switch is tilted 5-10 degrees is such a way that the end of the cylinder opposite to the pins is facing towards the ground. When the ball is away from the pins, the switch is open and there is no current flowing through the 100+KOhm resistor connected in series with the switch. Granted, it’s a small current but it’s unnecessary when the box is at rest, so no reason to waste the power, no matter how small.
An obvious part missing on this photo are the 12 LEDs prepped with 10″ lengths of 30AWG wire. I used white/yellow color coding to indicate the LED’s anodes and cathodes. White/Yellow blended it nicely with this particular box’es color scheme. If you use a different box, you may need to use different color wires. There’s a lot of wiring in this box and it tends to spill over. Since the box is transparent, you don’t want too much of the wiring to show.
Top of the box - best place to house the circuit

Top of the box – best place to house the circuit

Top of the box turned out to be the best place for the circuit – do not hot-glue the circuit itself to the plastic insert of the box! I did that the last time and found that changing the batteries becomes an unnecessary hassle if the circuit is glued in place. It will be help securely enough on the bundle of LED wires, since the LEDs are glued and pretty secure themselves. Note, once again, the position of the ball switch. When the box it turned right side up, it will be facing about 5-10 degrees towards the ground.

Glue LEDs in place using low temp hot glue

Glue LEDs in place using low temp hot glue

Using a low-temp hot glue gun, glue the LEDs in place according to their numbering. The LED1 is at the bottom of the box, LED12 is at the top. In this particular box there is really no space to locate all the LEDs on one straight line – you might as well spread them around the box a bit but still pay attention to the order of LEDs. If they are positioned randomly like in the previous project, it will be very difficult to script the sequence – LED number is a part of the command that turns it on and off.

all LEDS are in place, ready for programming

all LEDS are in place, ready for programming

Picture on the left shows all LEDs glued in place. Give them some 10 minutes to cool down – the glue is still no strong right after it starts to solidify – because of the springing wires, the LEDs may rotate from their ideal vertical position – you don’t want that because you want them facing the person opening the box. The project on this picture is ready to be programmed (and if you’ve programmed the Attiny13 before you soldered it, then you don’t need the ICSP cable hanging to the left of the box. Otherwise, load the Arduino sketch (below) in the Arduino IDE (here is how to setup Arduino IDE to program Attiny13) and upload it using a programmer of your choice, such as the Arduino ISP.

Arduino Sketch for the ATtiny13 12 LED blinker project
Here is the sketch in a file or just copy and paste from the program listing below. Please read the commentary inside the program for the way to write the blink scripts. There’s still about 120 bytes of memory remaining in the ATtiny13 which will be good for describing 60 more LED blinking steps. The highlighted part of the code is what defines the state of each LED (1=ON, 0-OFF) and the duration of the particular step.

0B0100100000000001 – here the LED1 (“1” on the right) and LED12 (“1” on the left of the 12 least significant bits) are ON

The 4 most significant bits are multiplied by the duration of one “clock” – 10 ms default. You may want to try defaults and then adjust to your own taste as needed:
0B0100100000000001 – 4 * clock (10 ms) = 40 ms

/*
  Program code for the Pimp Your Chocolates project Version 2. 
  Charlieplexing 12 LEDs mounted into a chocolate box using ATtiny13
  
  The tilt switch motion sensor is connected to Pin 3 (PB4)
  See http://elabz.com/pimp-your-chocolates-with-arduino-ide-and-attiny13/ 
  for the complete circuit schematics
  
  Code by Elabz.com
  http://elabz.com/
  This example code is in the public domain. If you end up using it in a project, please drop me a message, I'd be happy to
  know it was of some use. I'll also be happy to feature your project on my site, so send some pictures, too.
  
  LED hookup can be gleaned from the bitmap[] array values. For example, first LED's value is B00000001 which means that
  to light the LED the D0 has to ho HIGH and D1 - LOW, so the LED's anode is facing D0. LED #2 is between the same legs but in reverse. 
  LED #3 is between D0 and D2, LED #4 is the same legs but in reverse and so on. 
  
//       ATMEL ATTINY13 / ARDUINO
//
//                 +-\/-+
// ADC0 (D 5) PB5 1|    |8 Vcc
// ADC3 (D 3) PB3 2|    |7 PB2 (D 2) ADC1
// ADC2 (D 4) PB4 3|    |6 PB1 (D 1) PWM1
//            GND 4|    |5 PB0 (D 0) PWM0
//                 +----+

  
  
 */
#include <avr/pgmspace.h>

#include <avr/sleep.h> // sleep code by insidegadgets.com
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif


byte bitmap[] PROGMEM ={0B00000001,0B00000010,0B00000001,0B00000100,0B00000001,0B00001000,0B00000010,0B00000100,0B00000010,0B00001000,0B00000100,0B00001000};
byte outModes[] PROGMEM ={0B00000011,0B00000011,0B00000101,0B00000101,0B00001001,0B00001001,0B00000110,0B00000110,0B00001010,0B00001010,0B00001100,0B00001100};

PROGMEM prog_uint16_t column[]   = {
0B0111000000000001, // LED #1 ON, all others OFF , duration of this step is 7 ticks (7 x oneTick) (duration = 4 most significant bits)
0B0111000000000011,
0B0111000000000111,
0B0111000000001111,
0B0111000000011111,
0B0111000000111111,
0B0111000001111111,
0B0111000011111111,
0B0111000111111111,
0B0111001111111111,
0B0111011111111111,
0B0111111111111111, // All 12LEDs lit
0B0111101000000001,
0B0100100100000001,
0B0100100010000001,
0B0100100001000001,
0B0100100000100001,
0B0100100000010001,
0B0100100000001001,
0B0100100000000101,
0B0100100000000011,
0B0100100000000001  // LED #1 and #12 are lit, duration 4 ticks
};

boolean d=true; // direction forward=true
byte oneTick = 10;
int columnDelay; //5ms width of the column and space between them
int showTime=15000;
byte cycles;
byte ticks;
void setup() { 
  sbi(GIMSK,PCIE); // Turn on Pin Change interrupt
  sbi(PCMSK,PCINT4); // Which pins are affected by the interrupt
  cycles = sizeof(column) / sizeof(int);
}

void loop() {
unsigned long showNow;
unsigned  long periodNow;
int currentColumn;
DDRB = 0B00000000; // turn everything off right after the MCU is awakened
start_show(); // turn on all LEDs one by one BEFORE the scripted animation starts. 
//You can remove the start_show() function and use the space for several dozen more scripted steps instead

showNow = millis();
while(millis()-showNow < showTime)
{
  for(byte x=0; x<cycles; x++){ // counter for the columns
      currentColumn = pgm_read_word_near(&(column[x]));
      ticks = currentColumn >> 12;
      columnDelay = ticks * oneTick;
      periodNow = millis();
      while(millis()-periodNow < columnDelay)
       {
       show_column(currentColumn);  
       }
       DDRB = 0B00000000;
       //delay(columnDelay);
       
  }

}

    DDRB = 0B00000000; // turn everything off at the end of the show

    system_sleep();



}
void show_column(int currentColumn){
  int mask=1;    
  for(byte j=0;j<12;j++){ // bit shift the column to light up each individual LED 
      if(currentColumn & mask){
      DDRB = 0B00000000; // turn everything off
      PORTB = pgm_read_byte(&(bitmap[j])); // turn LED on
      DDRB = pgm_read_byte(&(outModes[j]));     
  }else{
    
     DDRB = 0B00000000; // turn everything off
     
  } 
    delayMicroseconds(250); // give each LED a chance to shine
    mask <<= 1;
  }
}
void start_show(){
  for(byte j=0;j<12;j++){ 
      DDRB = 0B00000000; // turn everything off
      PORTB = pgm_read_byte(&(bitmap[j])); // turn LED on
      DDRB = pgm_read_byte(&(outModes[j]));
      delay(25); // give each LED a chance to shine
  }
   for(byte k=12;k>0;k--){ 
      DDRB = 0B00000000; // turn everything off
      PORTB = pgm_read_byte(&(bitmap[k])); // turn LED on
      DDRB = pgm_read_byte(&(outModes[k]));
      delay(25); // give each LED a chance to shine
  } 
  
}
// From http://interface.khm.de/index.php/lab/experiments/sleep_watchdog_battery/
void system_sleep() {
  cbi(ADCSRA,ADEN); // Switch Analog to Digital converter OFF
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Set sleep mode
  sleep_mode(); // System sleeps here
  sbi(ADCSRA,ADEN);  // Switch Analog to Digital converter ON
}

ISR(PCINT0_vect) {

}

Once ATtiny13 has been programmed, it should go through several cycles of blinks for the duration of the show set by this variable: showTime=15000; (15 seconds default) and then go to sleep until you shake the box again. Here is the video one more time that shows how the sketch above works:

I hope you enjoyed this latest installment in the LED blinker series of posts here, I’ll go on finish the third (and hopefully the last one on this blog) LED blinker project.

As always, if you have a question regarding the project, post it into the Forums, Help section. Blog comments at the bottom of this page are not as useful for the question/answer flow that may be needed to help you with your project as the forums.

5 Responses to “ATtiny13 12-LED blinker with motion detection and scripted blinks”

Leave a Reply

Or use the Forums ! If your comment is a question, please consider posting it to a matching section of our Electronics Forums. The forums allow for a more natural conversation flow, especially if multiple replies are required. Additionally, you'll be able to style your writing (bold font, italics etc.) and post images which can help with a good answer.