Wednesday 4 January 2017

Archer and "The Last Leg"-inspired 'Phrasing' button


The inspiration

 

In some ways my workplace is very much like this - anything that could be misconstrued as an innuendo, even remotely, will inevitably have people calling out "hey, phrasing!", "That's what she said..." etc, etc.

Combining that with this "Bullshit button" from The Last Leg:


.. and I had an idea.


To cap it all off, at work we were having a clear out of some cupboards, and I found this:


It plays a dog-barking noise when pressed. I'm told it was used for some kind of team building thing long before I joined the company. Weird.
By this point it must be pretty obvious where this project is headed...

The teardown
First things first, see how much space in the button there is to work with, and what parts can be reused.

The single PCB contains a controller under a blob of epoxy, as was expected, so I'll need to replace that. However, the same PCB contains the actual button, so it'll need to stay in place. To allow that I just cut the traces connecting the button to the PCB, and scraped off some of the solder mask in order to provide connection points for my replacement controller.


The electronics

As this was intended to be a quick and easy project, I had to make do with what I could find in my electronics junk box. I couldn't use the same MP3 trick that I used in the Swear Jar project, as what I had available was too large for the buttons' case.

Whichever controller I used needed to have enough memory to store the audio data, which ruled out a lot of micros, so I ended up recycling an old arduino micro (ATMega328)

To make room for the arduino, the battery compartment had to be removed - I would instead use coin cell batteries. This would still work, but come at the cost of a reduced battery life.

The audio

To capture the original audio, I used the same technique I covered here to create a WAV file. After using Audacity to crop and edit down the relevant clip (Archer saying "Uh, Phrasing"), I used the EncodeAudio application from HiLoTech to turn it into a header file (sounddata.h in the code below)


The software
The code used on the arduino was based around the PCMAudio example, but I needed to modify it to incorporate some power saving - to account for the reduced capacity of the coin cell batteries. Code listed below:

#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/sleep.h>
#define SAMPLE_RATE 8000

#include "sounddata.h"

int ledPin = 13;
int speakerPin = 11;
volatile uint16_t sample;
byte lastSample;


void stopPlayback() {
    TIMSK1 &= ~_BV(OCIE1A);
    TCCR1B &= ~_BV(CS10);
    TCCR2B &= ~_BV(CS10);
    digitalWrite(speakerPin, LOW);
}

ISR(TIMER1_COMPA_vect) {
    if (sample >= sounddata_length) {
        if (sample == sounddata_length + lastSample) {
            stopPlayback();
        }
        else {
            if(speakerPin==11){
                OCR2A = sounddata_length + lastSample - sample;
            } else {
                OCR2B = sounddata_length + lastSample - sample;
            }
        }
    }
    else {
        if(speakerPin==11){
            OCR2A = pgm_read_byte(&sounddata_data[sample]);
        } else {
            OCR2B = pgm_read_byte(&sounddata_data[sample]);
        }
    }
    ++sample;
}

void startPlayback() {
    pinMode(speakerPin, OUTPUT);

    ASSR &= ~(_BV(EXCLK) | _BV(AS2));

    TCCR2A |= _BV(WGM21) | _BV(WGM20);
    TCCR2B &= ~_BV(WGM22);

    if(speakerPin==11){
        TCCR2A = (TCCR2A | _BV(COM2A1)) & ~_BV(COM2A0);
        TCCR2A &= ~(_BV(COM2B1) | _BV(COM2B0));
        TCCR2B = (TCCR2B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);

        OCR2A = pgm_read_byte(&sounddata_data[0]);
    } else {
        TCCR2A = (TCCR2A | _BV(COM2B1)) & ~_BV(COM2B0);
        TCCR2A &= ~(_BV(COM2A1) | _BV(COM2A0));
        TCCR2B = (TCCR2B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);

        OCR2B = pgm_read_byte(&sounddata_data[0]);
    }

    cli();

    TCCR1B = (TCCR1B & ~_BV(WGM13)) | _BV(WGM12);
    TCCR1A = TCCR1A & ~(_BV(WGM11) | _BV(WGM10));

    TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);

    OCR1A = F_CPU / SAMPLE_RATE;

    TIMSK1 |= _BV(OCIE1A);

    lastSample = pgm_read_byte(&sounddata_data[sounddata_length-1]);
    sample = 0;
    sei();
}

int wakePin = 2;

void setup(){
    pinMode(wakePin, INPUT);
    pinMode(ledPin, OUTPUT);
    
    
    attachInterrupt(0, wakeUpNow, LOW);
    
    startPlayback();
}

void loop(){}
void wakeUpNow(){
    startPlayback();
    sleepNow();
}

void sleepNow() { 
    set_sleep_mode(SLEEP_MODE_PWR_DOWN); 
 
    sleep_enable();    
    attachInterrupt(0,wakeUpNow, LOW); 
    sleep_mode();            
    sleep_disable();
    detachInterrupt(0);
} 

The finish
Some finishing touches, and it's done!

No comments:

Post a Comment