Arduino as I2C Slave

Hi All -

I’m trying to use my Arduino as an I2C slave, where a master can poll the Arduino and read the current state of its digital and analog inputs. I’d like the I2C master to be able to poll either single parts of the current status, or just grab the whole ‘current state’.

As a first step, I’ve programmed my Arduino to use two different data sets and flip between the two sets every thirty seconds. I’m having trouble making the Arduino respond correctly to multi-byte reads, though. The ‘request’ callback only gets called once on a multi-byte read, and I have no idea how to tell in the request handler how many bytes were requested.

Here’s my Arduino code:

#include <Wire.h>

byte datasetA[] = {0xaa, 0x55, 0x01, 0xff}; // test pattern
byte datasetB[] = {0x33, 0x31, 0x34, 0x32}; // Pi, more or less

int ptr = 0;

boolean state = HIGH;
int counter = 0;

void setup(){
  Wire.begin(0x50); // write address 0xa0, read address 0xa1
  Wire.onRequest(requestHandler);
  Wire.onReceive(receiveHandler);
  Serial.begin(9600);
  Serial.println("Initialized");
}

void loop(){
  counter++;
  counter = counter % 30;
  if (counter == 0){
    state = !state;
  }
  delay(1000); // yeah, I realize this will drift slightly as loop() takes > 1 second to complete
}

void requestHandler(){
  if (state){
    Wire.send(datasetA[ptr]);
  }else{
    Wire.send(datasetB[ptr]);
  }
  Serial.println("Request handled");
  ptr++;
  ptr = ptr % 4;
}

void receiveHandler(int count){
  Serial.print("Receive ");
  Serial.print(count, HEX);
  Serial.println(" bytes");
}

The symptom is that if I request a single byte from the Arduino, I get a correct byte. If I issue multiple single-byte reads, I get the complete data set and it repeats just like it should, and changes every 30 seconds just like it should. If I issue a n-byte read to the Arduino, I get one byte of the current dataset, followed by (n-1) bytes of 0xFF.

I spent a little while digging around the Wire library source but wasn’t able to figure out what to do. It doesn’t look like I can tell from ‘userspace’ (what I call the arduino Wire class) that the bus master is clocking the Arduino several times and not sending a NACK.

I did try another thing – I hard-coded my requestHandler to respond with all 4 bytes – and this works…but it isn’t really what I want, because I’d like the I2C master to be able to request one byte (or any arbitrary number of bytes, really) at a time sometimes.

If anybody has any clues to get me started, I’d be all eyes.

Thanks for any insights,
Reid

I've programmed my Arduino to use two different data sets and flip between the two sets every thirty seconds.

That is your problem, you need to use two sub addresses or registers, one to return one byte and the other to return the whole state. Don't have the receiving end flip between two responses as there is no way to synchronise this with the master.

That is your problem, you need to use two sub addresses or registers, one to return one byte and the other to return the whole state. Don't have the receiving end flip between two responses as there is no way to synchronise this with the master.

I'm not sure that I understand. With for example I2C EEPROM, I can request one byte or multiple bytes, both using the same slave address...my understanding of how this looks on the wire is as follows:

Master transmits I2C Start Bit, Master transmits I2C Slave Address, master waits for ACK from slave, Master clocks back in a byte from the slave, Master sends ACK (or stop bit if it only wants one byte). Repeat 'clock back in a byte followed by ACK' until satisfied, then put an I2C stop bit on the line to let the slave know that the Master has enough data.

If I'm right in this view of the state machine, I'll tinker with the Wire library to make it do the right thing (twi.cpp currently doesn't try to get more data from userspace if it is in slave transmit mode and received an ACK from the master).

Reid