Most I2C tutorials made each Arduino a slave and master, but this approach is better, because each Arduino is either a master or a slave (not both) and no switching is needed. That makes things easier.
I2C is better than serial, because you can add more Arduinos in the same bus.
I have implemented I2C between two Arduinos and it's not more complicated that reading/writing to the serial port (which you have already done). And I bet you can generalize your serial port code to work with both serial and I2C comms.
This is my example (just a proof of concept). The slave Arduino controls a some pins, a temp. sensor and a watchdog under orders from the master Arduino. If slave doesn't receive a beat in time, it reset the master Arduino.
Master code
#include <Wire.h>
#define CMD_SENSOR 1
#define CMD_PIN_ON 2
#define CMD_PIN_OFF 3
#define CMD_LUMEN 4
#define CMD_BEAT 5
const byte SLAVE_ADDRESS = 42;
const byte LED = 13;
char buffer[32];
void setup ()
{
Serial.begin(9600);
Serial.println("Master");
Wire.begin ();
pinMode (LED, OUTPUT);
digitalWrite(LED, HIGH);
delay(1000);
digitalWrite(LED, LOW);
Wire.beginTransmission (SLAVE_ADDRESS);
Serial.println("Send LED on");
Wire.write (CMD_PIN_ON);
Wire.write (2);
Wire.write (10);
Wire.endTransmission();
int x = Wire.requestFrom(SLAVE_ADDRESS, 1);
Serial.print("status=");
Serial.println(x);
} // end of setup
void loop ()
{
Serial.println(".");
Wire.beginTransmission (SLAVE_ADDRESS);
Wire.write (CMD_SENSOR);
Wire.endTransmission();
int x = Wire.requestFrom(SLAVE_ADDRESS, 1);
Serial.print("Disponibles = ");
Serial.println(x);
int temp = (int) Wire.read();
Serial.println(temp);
Wire.beginTransmission (SLAVE_ADDRESS);
Wire.write (CMD_LUMEN);
Wire.endTransmission();
Wire.requestFrom(SLAVE_ADDRESS, 2);
int light = Wire.read() << 8 | Wire.read();
Serial.print("Light=");
Serial.println(light);
Wire.beginTransmission (SLAVE_ADDRESS);
Wire.write (CMD_BEAT);
Wire.endTransmission();
Wire.requestFrom(SLAVE_ADDRESS, 1);
delay (5000);
} // end of loop
Slave code
/*
Esclavo I2C
Recibe los siguientes comandos
<- 1° byte -> <- 2° byte -> <- 3° byte ->
CMD_SENSOR
CMD_PIN_ON n° de pin duracion en segundos
CMD_PIN_OFF n° de pin
CMD_LUMEN
CMD_BEAT
Cada comando recibe una respuesta, ya sea el valor pedido
o un status.
*/
#include <max6675.h>
#include <Wire.h>
typedef struct {
int pin;
unsigned long off;
} PIN_PGMA;
/*
Lista de pines que se pueden activar via CMD_PIN_ON.
*/
#define PIN_LUMEN A0
#define PIN_LED 2
#define PIN_RESET 3
PIN_PGMA pgma[] = {
{PIN_LED, 0},
{PIN_RESET, 0}
};
const int pgmaSize = sizeof(pgma) / sizeof(PIN_PGMA);
#define CMD_SENSOR 1
#define CMD_PIN_ON 2
#define CMD_PIN_OFF 3
#define CMD_LUMEN 4
#define CMD_BEAT 5
#define ST_OK 0
#define ST_BAD_PIN 1
#define ST_TIME_0 2
#define ST_BAD_LEN 3
#define MY_ADDRESS 42
// Maximo tiempo de espera entre comandos CMD_BEAT. Pasado
// ese tiempo, se activa el PIN_RESET.
// En milisegundos.
#define BEAT_INTERVAL 10000
unsigned long lastBeat;
// Largo del reset en milisegundos.
#define RESET_LENGTH 250
byte cmd = 0;
byte status = 0;
int thermoDO = 11;
int thermoCS = 12;
int thermoCLK = 13;
MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);
void setup ()
{
Serial.begin(9600);
pinMode(PIN_LUMEN, INPUT);
analogRead(PIN_LUMEN);
for (int i = 0; i < pgmaSize; i++) {
pinMode(pgma[i].pin, OUTPUT);
digitalWrite(pgma[i].pin, LOW);
}
lastBeat = millis();
Wire.begin (MY_ADDRESS);
Wire.onReceive (receiveCommand);
Wire.onRequest (sendAnswer);
}
void loop()
{
unsigned long now = millis();
// Baja la linea de RESET si no ha recibido un beat ultimamente.
unsigned long diff = now - lastBeat;
if (diff > BEAT_INTERVAL) {
resetPin();
}
// Recorre la lista de pines y apaga aquellos cuyo tiempo termino.
for (int i = 0; i < pgmaSize; i++) {
if (pgma[i].off > 0 && pgma[i].off <= now) {
Serial.print("off pin="); Serial.println(pgma[i].pin);
pgma[i].off = 0;
digitalWrite(pgma[i].pin, LOW);
}
}
}
// called by interrupt service routine when outgoing data is requested
void sendAnswer() {
byte temp;
int lightReading;
switch (cmd) {
case CMD_SENSOR:
temp = thermocouple.readCelsius();
Wire.write(temp);
break;
case CMD_LUMEN:
lightReading = analogRead(PIN_LUMEN);
Wire.write(lightReading >> 8);
Wire.write(lightReading % 0xFF);
break;
case CMD_PIN_ON:
case CMD_PIN_OFF:
case CMD_BEAT:
Wire.write(status);
status = ST_OK;
break;
}
cmd = 0;
}
// called by interrupt service routine when incoming data arrives
void receiveCommand (int howMany) {
cmd = Wire.read ();
status = ST_OK;
switch (cmd) {
case CMD_PIN_ON:
cmdPinOn();;
break;
case CMD_PIN_OFF:
cmdPinOff();
break;
case CMD_BEAT:
lastBeat = millis();
break;
}
} // end of receiveEvent
void cmdPinOff() {
if (Wire.available() != 1) {
status = ST_BAD_LEN;
} else {
int pin = Wire.read();
int i = searchPin(pin);
if (i < 0) {
status = ST_BAD_PIN;
}
else {
pgma[i].off = 0;
digitalWrite(pin, LOW);
}
}
}
int searchPin(int pin) {
int i = pgmaSize - 1;
while (i >= 0 && pgma[i].pin != pin) {
i--;
}
return i;
}
/*
* Programa el encendido y duracion del RESET.
*/
void resetPin() {
if (digitalRead(PIN_RESET) == LOW) {
unsigned long now = millis();
int i = searchPin(PIN_RESET);
pgma[i].off = now + RESET_LENGTH;
lastBeat = now;
digitalWrite(PIN_RESET, HIGH);
}
}
void cmdPinOn() {
if (Wire.available() != 2) {
status = ST_BAD_LEN;
} else {
int pin = Wire.read();
int len = Wire.read();
int i = searchPin(pin);
Serial.print("pin="); Serial.print(pin); Serial.print(",index="); Serial.println(i);
if (i < 0) {
status = ST_BAD_PIN;
Serial.println("bad pin");
} else {
if (len == 0) {
status = ST_TIME_0;
Serial.println("ban len");
}
else {
pgma[i].off = millis() + len * 1000;
digitalWrite(pin, HIGH);
Serial.println("ok");
}
}
}
}