Fire Truck toy: adding LED and siren using attiny13a

Hi,

I have upgraded an old fire truck toy with 2 LEDs, a piezo (speaker) and an ATTiny13a (1.2MHz on 2 AAA with each 1.34V max). The fire truck is nothing more than a plastic shell with two axle and wheels on it. No electronic at all.
Searched the forum for related posts but found non similar. Read the interrupts page from Nick Gammon, again and again ;).

What is it I want–and I partially have achieved already:
When the truck starts moving, start playing the “siren” and have the two LEDs flash alternately, all for a fixed time. No need to make it more complex.

What was needed to do:

  1. “recognize” the moving and activate an interrupt
  2. activate the LED alternately
  3. play a simple tone sequence as long as the LEDs are flashing

What is working: Basically all.

Problems/questions:

  1. Siren is pretty quiet. How to make the piezo siren loader without changing power supply or adding step up converter?
  2. Sometimes the LED/siren get “stuck” and slowed down, respectively. That is, a single tone is heard for more time than usual and one LED is on for more than a second. This happens when moving the truck as well as when it is stopped. More frequent when it’s moving. I guess the interrupt is firing so fast that the ATTiny13a can’t keep up? Well that is what I want to clarify with your help. What is wrong with my interrupt handling?
  3. Recognizing the movement was a bit tricky. Maybe someone has another/better idea how to recognize the movement? My current solution: The front axle is made of metal. I attached small conducting magnets to it. When the fire truck starts moving the magnets are rotated, hit the bottom and short two separate wires below. PB1 (INT0) is pulled to ground. IMO the magnets and the axle will suffer attrition after some use.

I’m using MicroCore, GitHub - MCUdude/MicroCore: An light-weight Arduino hardware package for ATtiny13, and Arduino IDE 1.6.9.
Sketch below and schematics attached (hobbyist here but did my best).

Thanks for your help & best!

#define PIN_IRQ PB1  // external interrupt INT0
#define PIN_LED PB3
#define PIN_LED2 PB2
#define PIN_BUZZER PB0

#include <avr/io.h>
#include <avr/interrupt.h>
volatile uint8_t irq = false;

#define SLEEP_INTERVAL 128
#include <avr/sleep.h>
ISR(WDT_vect) {
}


#define PLO(pin)    (PORTB &= ~(1<<pin))
#define PHI(pin)    (PORTB |= (1<<pin))



void pinChange() {
  //detachInterrupt(0);
  irq=true;
}

int main(int argc,  char** argv) {  // 20 bytes smaller than setup/loop!
  
  pinMode(PIN_IRQ,INPUT);
  pinMode(PIN_LED,OUTPUT);
  pinMode(PIN_LED2,OUTPUT);
  pinMode(PIN_BUZZER,OUTPUT);

  // Buzzer
  TCCR0A |= (1<<WGM01); // set timer mode to Fast PWM
  TCCR0A |= (1<<COM0A0); // connect PWM pin to Channel A of Timer0
  
  attachInterrupt (0, pinChange, LOW);
  sei();

  sirene(); // signal battery attached


  while (1) {
    
    if (irq) {
      sirene();
      sirene();
      sirene();
      sirene();
  
      irq=false;
      //attachInterrupt (0, pinChange, LOW);
    }
  
    sleepNow(SLEEP_INTERVAL);
  }
}

void sirene() {
  tone(1);PHI(PIN_LED);_delay_ms(250);
  
  tone(2);PHI(PIN_LED2);PLO(PIN_LED);_delay_ms(250);
          PHI(PIN_LED);PLO(PIN_LED2);_delay_ms(250);
          
  tone(1);PHI(PIN_LED2);PLO(PIN_LED);_delay_ms(250);
  
  tone(2);PHI(PIN_LED);PLO(PIN_LED2);_delay_ms(250);  
          PHI(PIN_LED2);PLO(PIN_LED);_delay_ms(250);
          
  stop();PLO(PIN_LED);PLO(PIN_LED2);_delay_ms(50);
  
  tone(1);PHI(PIN_LED);_delay_ms(50);
          PHI(PIN_LED2);PLO(PIN_LED);_delay_ms(50);
          PHI(PIN_LED);PLO(PIN_LED2);_delay_ms(50);
          PHI(PIN_LED2);PLO(PIN_LED);_delay_ms(50);
          PHI(PIN_LED);PLO(PIN_LED2);_delay_ms(50);
          
  tone(2);PHI(PIN_LED2);PLO(PIN_LED);_delay_ms(50);
          PHI(PIN_LED);PLO(PIN_LED2);_delay_ms(50);
          PHI(PIN_LED2);PLO(PIN_LED);_delay_ms(50);
          PHI(PIN_LED);PLO(PIN_LED2);_delay_ms(50);  
          PHI(PIN_LED2);PLO(PIN_LED);_delay_ms(50);
          PHI(PIN_LED);PLO(PIN_LED2);_delay_ms(50);
          PHI(PIN_LED2);PLO(PIN_LED);_delay_ms(50);
          PHI(PIN_LED);PLO(PIN_LED2);_delay_ms(50); 
          PHI(PIN_LED2);PLO(PIN_LED);_delay_ms(50);
          PHI(PIN_LED);PLO(PIN_LED2);_delay_ms(50);

  PLO(PIN_LED);PLO(PIN_LED2);
  stop();
}

void sleepNow(byte b) {
  {
    ACSR |= (1<<ACD);   //Analog comparator off
    ACSR = ADMUX = ADCSRA = 0;
  }
  
  if (b != 128) {
    WDTCR |= b;      //Watchdog 
    // Enable watchdog timer interrupts
    WDTCR |= (1<<WDTIE);
  }

  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  
  cli();  // No interrupts; timed sequence
  BODCR = (1<<BODS) | (1<<BODSE);
  BODCR = (1<<BODS);
  sei();  // Enable global interrupts or we never wake
  sleep_mode();
  sleep_disable();
}


/******************
 * adapted from Łukasz Marcin Podkalicki <lpodkalicki@gmail.com>
 * ATtiny13/007
 * Simple tone generator.
 */

typedef struct s_note {
  uint8_t OCRxn; // 0..255
  uint8_t N;
} note_t;


#define N_1 (_BV(CS00))
#define N_8 (_BV(CS01))
#define N_64  (_BV(CS01)|_BV(CS00))
#define N_256 (_BV(CS02))
#define N_1024  (_BV(CS02)|_BV(CS00))
// bei 1.2 MHz
// bei 4.8?

static void tone(uint8_t t)
{
  note_t ta = {20, N_64};
  note_t tue = {127, N_8};
  note_t val = t==1?ta:tue;
  // PB0!!
  TCCR0B = (TCCR0B & ~((1<<CS02)|(1<<CS01)|(1<<CS00))) | val.N;
  OCR0A = val.OCRxn - 1; // set the OCRnx
}

static void stop(void)
{
  TCCR0B &= ~((1<<CS02)|(1<<CS01)|(1<<CS00)); // stop the timer
}

/*****************************/

firetruck.png

Giving an update myself.
I remembered that the LOW level interrupt is firing as long as the pin is low. Trying to change attachInterrupt to RISING or FALLING did not work at all.
Instead of attachInterrupt I’m now using

 GIMSK   |=  (1<<PCIE);         
  PCMSK   |=  (1<<PCINT1);

Seems to help.

Would be nice if someone has some input for the other issues.

Thanks & Best