Strange bytes are sent over the I2C bus

Hi all,

I'm trying to make a connection between two Arduinos for a game. I'm using the MasterWriter Tutorial to do this (http://arduino.cc/en/Tutorial/MasterWriter). I have two Arduino Uno's connected to each other using A4 and A5 and the slave gets power from the master. Also, the grounds are connected. I am using Arduino 1.0 on OS X.

With the code below I was expecting to see messages go from Master to Slave and back again. By using the Serial connection I can see that messages from the master to the slave are correctly sent and received ("M10"), but messages from the slave to the master are garbled on reception by the master. The slave does send the correct values ("S20").

I can't figure out what's wrong! The message received by the Master is 3 bytes as expected, but instead of "S20", I get the bytes "0-255-255".

Does anybody have a clue?

Thanks in advance!

(I've removed all references to Serial.print in the code below to make it more legible)

/** Master */

#include <Wire.h>

byte GAME_IS_OURS = 0;
byte GAME_IS_THEIRS = 1;

byte move_count = 0; // we never have a board which allows more than 255 moves
byte mode = GAME_IS_OURS; // we begin, because we are the master
byte next_move = 0; // which column we want to put a block in
byte opponent_last_move = 0;

void setup()
{
  Wire.begin(); // join i2c bus (address optional for master)
}

void loop()
{
  mode = GAME_IS_OURS;

  next_move = calculate_next_move();
  move_count++;

  // Send our move to opponent
  sendBoardStatusWire();
  mode = GAME_IS_THEIRS;
  
  // Get the opponent's move:
  while(!receiveWireMessage())
  {  
    Wire.requestFrom(4, 3);
    delay(500);
  }
  
 if(move_count > 30)
    while(true)
    {;}
}

byte calculate_next_move()
{
  return move_count % 5; // sends a new column everytime (makes horizontal four in a row)
}

void sendBoardStatusWire()
{
  Wire.beginTransmission(4); // transmit to device #4
  Wire.write('S');        // sends one bytes
  Wire.write(move_count);
  Wire.write(next_move);
  Wire.endTransmission();    // stop transmitting
}

boolean receiveWireMessage()
{
  // the only message we will accept is {<char>, <byte>, <byte>}, with <char> == "M"
  char control = 'X';
  byte opponent_move_count = 1;
  byte opponent_move = 1;

  if(Wire.available())
    control = Wire.read();
  else
    return false;
  if(Wire.available())
    opponent_move_count = Wire.read();
  else
    return false;
  if(Wire.available())
    opponent_move = Wire.read();
  else
    return false;

  if(control != 'M')
    return false;
  if(opponent_move != (move_count + 1))
    return false;
  
  opponent_last_move = opponent_move;
  move_count++;

  return true;
}
/** Slave */

#include <Wire.h>

byte GAME_IS_OURS = 0;
byte GAME_IS_THEIRS = 1;

byte move_count = 0; // we never have a board which allows more than 255 moves
byte mode = GAME_IS_THEIRS; // the opponent starts, because we are such nice people
byte next_move = 0; // which column we want to put a block in
byte opponent_last_move = 0;

void setup()
{
  Wire.begin(4);                // join i2c bus with address #4
  Wire.onReceive(receiveEvent); // register event
  Wire.onRequest(requestEvent); // register event
}

void loop()
{
  delay(1000);
  
  // The game is ours, we have to do something!
  if(mode == GAME_IS_OURS)
  {
    // TODO the next line should be made smarter!
    next_move = opponent_last_move; // <- we block a vertical four in a row
    
    mode = GAME_IS_THEIRS; // we've 'calculated' our move, now it's up to them!
    move_count ++;
  }
}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
  char control = 'A';
  byte opponent_move_count = -1;
  byte opponent_move = -1;
  
  // message should equal 3 characters
  if(howMany != 3)
    return;
  
  if(Wire.available())
    control = Wire.read(); // receive byte as a character
  if(Wire.available())
    opponent_move_count = Wire.read();    // receive byte
  if(Wire.available())
    opponent_move = Wire.read();    // receive byte
  
  // Let's check if the message was correctly received. We expect an "S" first:
  if(control != 'S')
    return;
  
  // then we expect that the move_count is one more than we had:
  if(opponent_move_count != move_count + 1)
    return;
  
  // We received a correct message, let's store it so we can calculate our next move
  opponent_last_move = opponent_move;
  move_count ++;
  
  // The ball is in our park now :)
  mode = GAME_IS_OURS;
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent()
{
  // The opponent asks us to send our new move
  // We'll send it only when we've calculated it
  if(mode == GAME_IS_THEIRS)
  {
    Wire.write('M');
    Wire.write(move_count);
    Wire.write(next_move);
  }
  else
  {
    byte zero = 0;
    // we're not ready yet!
    Wire.write('A');
    Wire.write(zero);
    Wire.write(zero);
  }
}

Looks like you are using Arduino 1.0. Things may have changed, but it looks like not.

This won't work:

void requestEvent()
{
  // The opponent asks us to send our new move
  // We'll send it only when we've calculated it
  if(mode == GAME_IS_THEIRS)
  {
    Wire.write('M');
    Wire.write(move_count);
    Wire.write(next_move);
  }
  else
  {
    byte zero = 0;
    // we're not ready yet!
    Wire.write('A');
    Wire.write(zero);
    Wire.write(zero);
  }
}

In a requestEvent handler you can only successfully send one "thing". You are sending three, so each one cancels the earlier one. You need something like this:

if(mode == GAME_IS_THEIRS)
  {
    byte buf [3] = { 'M', move_count, next_move };

    Wire.write (buf, sizeof buf);
  }

Untested and uncompiled, but you get the idea.

Yep! That was it! Thanks a lot!

Could not find this anywhere in the documentation or the tutorial, though. This can be quite tricky for people to realize.

I mention it here:

That's not the "official" documentation, but hopefully you will find it helpful.