Since my RF receivers require a full hardware-based UART to function reliably, I decided to opt for adding a simple attiny2313 as a controller connected to an Arduino. Ready to build a simple protocol, I decided to do a little more research into I2C — the inter-integrated-circuit protocol which puts multiple digital micro-controllers on a low-speed bus. As it turns out, the attiny controllers actually implement the full protocol, even if Atmel calls it TWI (two-wire-interface). The protocol defines a master device as well as up to 127 slave devices (7-bit addressing).

The hardware is very easy to hook up and it simply requires two wires (TWI is not just a clever name) – one called SDA, to transmit the data, and one called SCL, to transmit a clock signal. On an Arduino Duemilanove SDA is analog pin 4 and SCL is analog pin 5. On the attiny2313 controller, SDA is pin B5 (pin 17, also MOSI), and SCL is on pin B7 (pin 19) — also refer to Alex’s cheat sheet for the pins. Just connect SDA to SDA and SCL to SCL.

Arduinos have the Wire library built straight into them, and after some hunting I found a small library for atttiny adapted from the original Atmel code by a Donald Blake (can be found on avrfreaks.net or my subversion repository). The Arduino is quickly configured as a master and set up to send and receive characters typed into the serial console to and from the slave controller:

// Arduino Code
void
  setup()   {
  Serial.begin(57600);
  // initialize as i2c master
  Wire.begin();
  Serial.println("OK>");
}
// loop forever..
void loop()
{
  // data available from computer?
  if (Serial.available()) {
    // read the incoming byte from the serial port
    sByte = Serial.read();
    // begin transmission to device 1    
    Wire.beginTransmission(1);
    // send
    Wire.send(sByte);
    // done
    Wire.endTransmission();

    // request data from device 1
    Wire.requestFrom(1,1);
    // echo out whatever we get back
    while(Wire.available()) {
      Serial.print(Wire.receive());
      Serial.print("-");
    }
  }
  delay(100);
}

On the slave, simply link the Donald Blake’s library into the code and this then does all the magic:

// attiny2313 code
int main(void)
{
  // initialize as slave with id 1
  usiTwiSlaveInit(1);

  // enable interrupts (must be there, i2c needs them!)
  sei();

  // handle commands
  while (1)
  {
    // check if data is in the i2c receive buffer
    if(usiTwiDataInReceiveBuffer())
    {
      // get it
      uint8_t b = usiTwiReceiveByte();
      // echo it back
      usiTwiTransmitByte(b);
    }
    // Do something else while waiting for the TWI transceiver to complete.

    asm volatile ("NOP" ::);
  }
  return 0;
}

Works like a charm!