Let's Make Robots!

Scary Skulls

Scares trick or treaters

My kids and I like to build all sorts of stuff and we like Halloween. So we decided to build some Halloween decorations. A trip to the dollar store got us a couple of skulls and some ping-pong ball eyes. A trip to the parts cabinet got some ATTiny 13s and some LEDs. Let's build. This is pretty simple. The ATTiny13 controls 3 LEDs. A white "headlight" inside the skull, and a red one inside each eye. The ATTiny13 only has 1K of flash and 64 bytes of RAM. But it is amazing what you can fit into that. The current code uses about 750 bytes of flash and some of it is not even used. The red LEDs are connected to PWM outputs for brightness control. The white one is connected to a gpio pin for on-off control. Another pin is(will be) connected to a switch mat to sense when a kid is next to it. On closing the switch the code goes into a play sequence. Currently the sequence does some changes of the brightness of the eyes and flashes the headlight. The timer is set to interrupt every 256 clock cycles and I track ticks and milliseconds with that. I use delays for the actual sequencing and the overhead of the timer interrupt throws that off some, but it doesn't matter. There are a couple interesting things in the code. The timer interrupt code is a decent example of what an interrupt routine should look like: short and to the point. There is also a simple pseudo random number generator that uses a linear feedback shift register. It is simple and works well for this type of application. There is also something special about the headlight flashing. My daughter came up with the idea and it was great. I will let you figure it out. We had fun building these. The kids (12 and 13) really picked up on the soldering very well. I was surprised how well they did. They did almost all the work other than the programming. That's next.

 

The code:  (yeah, I know)

 

//////////////////////////////////////////////////////////////////////////////
///  Copyright (c) 2013 William R Cooke
//////////////////////////////////////////////////////////////////////////////
///   Halloween skull project
///   Lights LEDs and makes sound, moves head
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
///
///  CPU
///  Runs on internal RC oscillator at 9.6 MHZ
///  Brownout detector set to 2.7V
///  Longest startup time
///
///  Port
///  B0    Left Eye LED PWM
///  B1    Right Eye LED PWM
///  B2    Head LED
///  B3    Sound out
///  B4    Trigger input (low to trigger, pullup enabled)
///
///  Timer 0 
///  No prescale
///  2 channels pwm at 39500 Hz
///  Overflow interrupt counts ticks and ~milliseconds
//////////////////////////////////////////////////////////////////////////////


#include 
#include 
#include 

//#define CBC
 #define JWC

#define F_CPU 9600000     // Clock freq for delays
#include 

#define HEADLIGHT_ON  PORTB |=_BV(2)
#define HEADLIGHT_OFF PORTB &= ~(_BV(2))
#define HEADLIGHT_TOGGLE PORTB ^= _BV(2)
#define BASE_TIME    100
#define LONG_TIME    (BASE_TIME * 3)

void init();
void set_up_timer();
void set_pwm(uint8_t left, uint8_t right);
volatile static uint8_t ticks = 0;
volatile static uint16_t milliseconds = 0;

//////////////////////////////////////////////////////////////////////////////
///  \b random
///
///  @brief Implements Fibonacci LFSR random number generator
///         taps at 10,12,13,15
///         seed must never be zero
///  @return  returns 16 bit "unsigned" pseudo random number
///
//////////////////////////////////////////////////////////////////////////////
uint16_t seed = 1;
uint16_t random()
{

   seed = (seed <<1) | (( (seed >> 15) ^ (seed >>13) ^ (seed >> 12) 
			 ^ (seed >> 10)) & 1);

   return seed;
}

uint16_t get_milliseconds();
void dit()
{
   HEADLIGHT_ON;
   _delay_ms(BASE_TIME);
   HEADLIGHT_OFF;
   _delay_ms(BASE_TIME);
}

void dah()
{
   HEADLIGHT_ON;
   _delay_ms(LONG_TIME);
   HEADLIGHT_OFF;
   _delay_ms(BASE_TIME);
}

void space()
{
   HEADLIGHT_OFF;
   _delay_ms(LONG_TIME);
}

void script_1()
{
   uint8_t x;
   uint16_t count = 0;

   set_pwm(0,0);

   for(x=0; x<255; ++x)
   {
      set_pwm(x,x);
      _delay_ms(3);
   }
   HEADLIGHT_ON;
   _delay_ms(500);

   HEADLIGHT_OFF;
   _delay_ms(500);

   seed = get_milliseconds();
   for (count = 0; count < 5; ++count)
   {

// morse code: BOO
   dah();   // B
   dit();
   dit();
   dit();
   space(); //_delay_ms(LONG_TIME);
   dah();   // O
   dah();
   dah();
   space(); // _delay_ms(LONG_TIME);
   dah();
   dah();
   dah();
   space(); //_delay_ms(LONG_TIME);
   space();
   space();
   space();
   //set_pwm((uint8_t) (random() & 0xff), (uint8_t) (random() & 0xff));
   set_pwm( (uint8_t) (count * 50 & 0xff), (uint8_t) (count *50 & 0xff) );

#ifdef CBC
   dah();   // C
   dit();
   dah();
   dit();
   space();
   dah();   // B
   dit();
   dit();
   dit();
   space();
   dah();   // C
   dit();
   dah();
   dit();
   space();
#endif
#ifdef JWC
   dit();   // J
   dah();
   dah();
   dah();
   space();
   dit();   // W
   dah();
   dah(); 
   space();
   dah();   // C
   dit();
   dah();
   dit();
   space();
#endif
   space();
   space();
   space();
   space();
   space();
   space();
   }
// now the random flickering
   seed = get_milliseconds();
   for(count = 0; count < 500; ++count)
   {
      if (random() & 1)
      {
	 HEADLIGHT_ON;
      }
      else
      {
	 HEADLIGHT_OFF;
      }
      _delay_ms(20);
   }


   _delay_ms(200);
   HEADLIGHT_ON;
   set_pwm(255,255);
   for(x=0; x<255; ++x)
   {
      _delay_ms(2);
      HEADLIGHT_TOGGLE;
      set_pwm(x, 255-x);
   }
   for(x=0; x<255; ++x)
   {
      _delay_ms(2);
      HEADLIGHT_TOGGLE;
      set_pwm(255-x, x);
   }
   HEADLIGHT_OFF;
   for (x=255; x>0; --x)
   {
      _delay_ms(2);
      set_pwm(x,x);
   }
   set_pwm(0,0);
}



int main(void)
{
   init();

   while(1)
   {
      // wait for B4 to go low (trigger switch)
      while (PINB & _BV(4));

      script_1();
      _delay_ms(2000);
   }
}

void init()
{
   PORTB = 0x30;      // Turn off outputs and pullups on
   DDRB = 0x0f;    // B0-B3 output, B4, B5 input (B5 is reset)

   set_up_timer();
   set_pwm(128,128);
   sei();
}

void set_up_timer()
{
   TCCR0A = 0xa3;  // Set fast pwm with max count clear on match, set at top
   TCCR0B = 0x01;  // No force outputs, wgm2=0, clk sel = internal no prescale
   TIMSK0 = 2;     // Enable Overflow interrupt
}

void set_pwm(uint8_t left, uint8_t right)
{
   OCR0A = left;
   OCR0B = right;
}

uint16_t get_milliseconds()
{
   uint16_t mils;
   cli();
   mils = milliseconds;
   sei();
   return mils;
}

ISR(TIM0_OVF_vect)
{
   ++ticks;
   if(ticks == 40)
   {
      ticks = 0;
      ++milliseconds;
   }
}

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
aidPUY2.gif

Somehow I just now saw this.  Love it.

Nice project, bdk. Seems the kids are a chip off the old block. I like the idea of using morse code in there too. ;)

Thanks.  The morse code was my daughter's idea.  there are a lot of hams (amateur radio operators) in this town so some might catch it on Halloween.

Good job bdk, fun family project as well!

Every year I think and plan on some kind of fun scary project using technology for Halloween...

And every year it comes and goes without any implementation.

I'll just live vicariously through you this year I guess and claim sucess!

Stephen

Thanks.  I always have lots of big plans that don't happen.

Hurrah physical computing (almost robots) by bdk6 \o/

One small step for bdk6 ('s children) a big step for LMRkind :D

And yeah I know too 0;-)

 

One day...

At least the code looks better now :-)