I2C on atmega168/328 using interrupts
I've been able to arrive at an answer to my own question. 

Master code.

#include &lt;stdint.h&gt;
#include &lt;util/delay.h&gt;
#include &lt;avr/interrupt.h&gt;
#include &lt;stdbool.h&gt;

volatile uint8_t ledValue = 0;
volatile uint8_t refValue = 0;
volatile uint8_t status;
volatile uint8_t pstatus = 0;
volatile uint8_t rw;
volatile bool receiveQ = true;
volatile bool transmitQ = false;

void send_start(){ TWCR &amp;= ~(1 &lt;&lt; TWSTO); TWCR |= (1 &lt;&lt; TWINT) | (1 &lt;&lt; TWSTA); }

void send_stop(){ TWCR &amp;= ~(1 &lt;&lt; TWSTA);  TWCR |= (1 &lt;&lt; TWINT) | (1 &lt;&lt; TWSTO); }

void send_data(){ TWCR &amp;= ~((1 &lt;&lt; TWSTA) | (1 &lt;&lt; TWSTO)); TWCR |= (1 &lt;&lt; TWINT); }

void toggle(){PORTB ^= (1 &lt;&lt; PORTB0);}

ISR(TWI_vect)
{
    status = TWSR &amp; 0xF8;
    if(status == 0x08 || status == 0x10)
    {
        pstatus = 1;
        TWDR = rw;
        send_data();
        return;
    }
    if(status == 0x38)
    {
        pstatus = 2;
        send_start();
        return;
    }
    if(status == 0x40)
    {
        pstatus = 3;
        TWCR &amp;= ~(1 &lt;&lt; TWEA);
        send_data();
        return;
    }
    if(status == 0x48)
    {
        pstatus = 4;
        send_start();
        return;
    }
    if(status == 0x50)
    {
        pstatus = 5;
        ledValue = TWDR;
        TWCR &amp;= ~(1 &lt;&lt; TWEA);
        send_data();
        return;
    }
    if(status == 0x58)
    {
        pstatus = 6;
        ledValue = TWDR;
        receiveQ = false;
        transmitQ = true;
        send_stop();
        return;
    }
    if(status == 0x18 || status == 0x20)
    {
        pstatus = 7;
        TWDR = refValue; //data to be transmitted
        send_data();
        return;
    }
    if(status == 0x28 || status == 0x30)
    {
        pstatus = 8;
        receiveQ = true;
        transmitQ = false;
        send_stop();
        return;
    }
    if((pstatus == 8 || pstatus == 6)){toggle();}
    pstatus = 0;
    send_stop();
}

void initialize_pwm()
{
    DDRD |= (1 &lt;&lt; DDD3);
    TCCR2A |= (1 &lt;&lt; COM2B1) | (1 &lt;&lt; WGM20);
    TCCR2B |= (1 &lt;&lt; CS20);
}

int main(void)
{
    DDRB |= (1 &lt;&lt; DDB0);
    //uint8_t slaveAddress = 5;
    initialize_pwm();
    TWBR = 10;
    TWCR = (1 &lt;&lt; TWEN) | (1 &lt;&lt; TWIE);
    sei();

    while(1)
    {
        if(receiveQ)
        {
            rw = 11; //BitShiftLeft[5, 1] + 1, read
            refValue += 5;
            if(refValue &gt; 255){refValue = 0;}
            send_start();
        }
        _delay_ms(100);
        OCR2B = ledValue;
        if(transmitQ)
        {
            rw = 10; //BitShiftLeft[5, 1], write
            send_start();
        }
    }
    return 0;

}


Slave code.

#include &lt;stdint.h&gt;
#include &lt;util/delay.h&gt;
#include &lt;avr/interrupt.h&gt;

volatile uint8_t ledValue = 0;
volatile uint8_t refValue = 0;
volatile uint8_t status;

void send_ack(){ TWCR |= (1 &lt;&lt; TWINT) | (1 &lt;&lt; TWEA);}

void send_n_ack(){ TWCR &amp;= ~(1 &lt;&lt; TWEA); TWCR |= (1 &lt;&lt; TWINT);}

void toggle(){PORTB ^= (1 &lt;&lt; PORTB0);}

ISR(TWI_vect)
{
    status = TWSR &amp; 0xF8;
    if(status == 0xA8 || status == 0xB0)
    {
        TWDR = refValue;
        send_n_ack();
        return;
    }
    if(status == 0xB8)
    {
        TWDR = refValue;
        send_n_ack();
        return;
    }
    if(status == 0xC0 || status == 0xC8)
    {
        send_ack();
        return;
    }
    if(status == 0x60 || status == 0x68 || status == 0x70|| status == 0x78)
    {
        send_ack();
        return;
    }
    if(status == 0x80 || status == 0x90)
    {
        ledValue = TWDR;
        send_n_ack();
        return;
    }
    if(status == 0x88 || status == 0x98 || status == 0xA0)
    {
        send_ack(); 
        return;
    }
    toggle();
    send_ack();
}

void initialize_pwm()
{
    DDRD |= (1 &lt;&lt; DDD3);
    TCCR2A |= (1 &lt;&lt; COM2B1) | (1 &lt;&lt; WGM20);
    TCCR2B |= (1 &lt;&lt; CS20);
}

int main(void)
{
    DDRB |= (1 &lt;&lt; DDB0);
    initialize_pwm();
    TWAR = 10; //BitShiftLeft[5, 1];
    TWCR = (1 &lt;&lt; TWEN) | ( 1 &lt;&lt; TWIE) | (1 &lt;&lt; TWEA);
    sei();

    while(1)
    {
        refValue += 5;
        if(refValue &gt; 255){refValue = 0;}
        _delay_ms(100);
        OCR2B = ledValue;
    }

}