2.) On the slave, the function registered as an event with Wire.onRequest must be the function that sends the response back (using Wire.send()). Of course you cannot have delay() in it, which is understandable, but neither can you get tricky and set a flag that will later call a function from loop(). From all I've seen, the Wire.send() must be in the event.
This is not entirely true, it doesn't have to be the function, but it does have to be called by it, or within a reasonable amount of time. The protocol should define the maximum amount of time between the request and the response to be valid, but depending on how you do it, it should work fine.
Here is some code I successfully used with Arduino 011, to do almost the exact same thing you're talking about.
One, specific handler (for sending an int, and requesting a variable amount of data back):
void send_command_to_motor(byte command, byte sub_com, unsigned int val, byte rec_len) {
byte comm_init;
byte cnt = 0;
memset(recv, 0, SIZE_OF_I2C_BUF);
Wire.beginTransmission(SLAVE_I2C_ADDR);
// we're going to send our command value (3 bits)
// and our sub-com/data length (4 bits)
comm_init = command << 5;
// we're sending three bytes...
comm_init |= B00000110;
Wire.send(comm_init);
Wire.send(sub_com);
// send the int as two bytes, MSB first
Wire.send((byte) (val >> 8));
Wire.send((byte) (val & 0xFF));
// flush out the buffer
Wire.endTransmission();
// we have to delay so our request works...
delay(100);
Wire.requestFrom(SLAVE_I2C_ADDR, rec_len);
// get up to total number of bytes expected in return
while( Wire.available() && cnt < rec_len) {
recv[cnt] = Wire.receive();
cnt++;
}
return;
}
And, on the slave end:
void twi_receive_from_master(int count) {
// called whenever we receive data from the master via TWI
//
// a command byte (1st byte) is received, and then a specified
// number of sub-command bytes
bool got_com = false;
byte cur_inp = 0;
byte twi_com_len = 0;
byte input;
while( Wire.available() ) {
input = Wire.receive();
if( got_com == false ) {
// we don't have a command yet
// so this is our initial command
// Serial.print("Rec Byte Val: ");
// Serial.println(input, DEC);
// we use got_com to make sure we got a valid command
// from the master. Zero means no command, no length,
// so that's not valid
if( input == 0 ) {
// Serial.println("Received Empty Request, Not Taking Action");
}
else {
// set our flag to say we've received a command
got_com = true;
// we have 3 bits for a command, 8 possible values [0-7]
twi_command = (byte) ( input >> 5);
// we have 4 bits for a sub-command byte count,
// 16 possible values
// mask out any bits we're not interested in..
twi_com_len = (byte) ( (input & B00011110) >> 1);
// there are two bits left, we might do something
// with them later
// Serial.print("Got Command: ");
// Serial.println(twi_command, DEC);
// Serial.print("Got SubCom Cnt: ");
// Serial.println(twi_com_len, DEC);
// clear out our previous command buffer
for(int i = 0; i < TWI_MAX_BYTES; i++)
twi_command_bytes[i] = 0;
}
}
else {
twi_command_bytes[cur_inp] = input;
// Serial.print("Sub Com: ");
// Serial.println(input, DEC);
cur_inp++;
/* if( cur_inp == twi_com_len ) {
// we've received all the bytes we're going to
// receive
Serial.println("Received all sub-command bytes");
}
*/
}
}
}
void twi_request_from_master() {
// Serial.println("In Rcv Request");
//called whenever a request for data is received from the
// master
//
// any number of bytes may be in the twi_command_bytes
// array, based on our command type and sub-command values
// the command byte defines the action we want to take, and the sub
// commands define any further specifics
switch(twi_command) {
case 0:
// status request
twi_send_status();
break;
case 1:
// set variable command
twi_set_variable();
break;
case 2:
// start
// Serial.println("Got Start Request");
is_running = true;
Wire.send(1);
break;
case 3:
// stop
// Serial.println("Got Stop Request");
is_running = false;
do_manual = false;
Wire.send(1);
break;
case 4:
// manual control
Wire.send(1);
twi_run_manual();
break;
default:
// unrecognized command
Wire.send(0);
break;
}
}
void twi_send_status() {
// send status information back to the master
byte stat_str;
/* Serial.print("Sending Status Typ: ");
Serial.println(twi_command_bytes[0], DEC);
Serial.print("Got SubStat: ");
Serial.println(twi_command_bytes[1], DEC);
*/
// first byte is the type of status response requested
switch(twi_command_bytes[0]) {
case 0:
// is running or not
Wire.send((byte) is_running);
break;
case 1:
// camera cycles shot in current program
stat_str = (byte) (camera_done_cycles >> 8);
Wire.send(stat_str);
stat_str = (byte) camera_done_cycles & 0xFF;
Wire.send(stat_str);
break;
default:
Wire.send(0);
break;
}
}
void twi_set_variable() {
// set a variable from data sent by the master
// Serial.print("Setting Variable: ");
// Serial.println(twi_command_bytes[0], DEC);
// first byte is type of variable to set
switch(twi_command_bytes[0]) {
case 0:
// directions
truck_dir = (bool) (twi_command_bytes[1] >> 2);
pan_dir = (bool) ((twi_command_bytes[1] & B00000010) >> 1);
tilt_dir = (bool) (twi_command_bytes[1] & B00000001);
break;
case 1:
// truck steps
truck_move_step = (int) twi_command_bytes[1] * 10;
break;
case 2:
// truck skip cycles
truck_skip_cycles = twi_command_bytes[1];
break;
// and a whole bunch more stuff
default:
break;
}
Wire.send(1);
}
(Kinda ugly, but it works =)
!c