So I went to have a look at the code
In Slave mode the ISR() is triggered when the master has requested data and after a bit of setup, the ISR is calling the handler after emptying the tx buffer
twi_txBufferIndex = 0;
// set tx buffer length to be zero, to verify if user changes it
twi_txBufferLength = 0;
// request for txBuffer to be filled and length to be set
// note: user must call twi_transmit(bytes, length) to do this
twi_onSlaveTransmit(); // <<----- THIS IS WHERE THE HANDLER IS CALLED
If you call write() in the handler multiple times, it calls twi_transmit(data, quantity); which, after verifying the buffer will not overflow, fills the slave tx buffer with data correctly by increasing the actual length of the buffer and copying the data in the right location
for(i = 0; i < length; ++i){
twi_txBuffer[twi_txBufferLength+i] = data[i];
}
twi_txBufferLength += length;
Once the handler returns, the tx buffer has all the necessary information and the ISR transmits first byte from buffer
case TW_ST_DATA_ACK: // byte sent, ack returned (THIS IS IGNORED THE FIRST TIME AS THERE IS NO BREAK ABOVE)
// copy data to output register
TWDR = twi_txBuffer[twi_txBufferIndex++];
// if there is more to send, ack, otherwise nack
if(twi_txBufferIndex < twi_txBufferLength){
twi_reply(1);
}else{
twi_reply(0);
}
break;
the first byte is now in the TWDR register ready to go. The hardware does its magic BASED ON THE CLOCK SEQUENCING FROM THE MASTER and then the ISR is triggered again with TW_STATUS being TW_ST_DATA_ACK so you send all the other bytes one by one through hardware interrupts. Once the last byte has been sent twi_reply(0); is called an the next interrupt will finalize the clean up.
This in theory seems all fine but does not seem to address the challenge of the clock being dictated by the master --> If the handler is slow, you are already within the ISR so the clock signal does not trigger anything and I assume former data that was sitting in the TWDR register is being transmitted and thus you get crap on the other side...
--> that's what I2C Clock Stretching is used for - the I2C slave holds down the clock and the master is required to read back the clock signal after releasing it to the high state and wait until the line has actually gone high.
It does not look like this is implemented and that could explain what Mike described with
This was solved once my partner had the hardware by sending the data from the position array rather than reading it in the data request function, hence it did not make it into my code. So it looks like if you wait too long before doing the wire write, it went off and sent an incomplete message. That looks to be like an unintended consequence of a fix to me.
--> my take on this = maintain the status array (your 4 int - either -1 while moving or the analogRead()) in the loop — in a interrupt protected critical section (as will be polled from the ISR) — and in the handler indeed just push that array...