I2C (Raspberry Pi commanding multiple Arduinos)

I know this is a well used topic but I have a few questions on the subject.

I am looking to setup a smart home and after doing a lot of research on the subject I've decided to go with a single Raspberry Pi connected to multiple Arduinos over I2C (Each Arduino would be placed into each room). The Arduino will be responsible for controlling everything in the room (Sockets, Lights, Sensors: [Temperature, Motion, etc.]). The Raspberry Pi would receive its commands from a Web Interface or other services and control the Arduinos or get information from them. This data would then be stored in a database and used for the smart home controls.

I have started writing code for the Arduinos and was just looking for some advise on whether or not I am on the right track. I am open to suggestions but something to keep in mind is that this is a new build (still being built), I'm not very interested in using plug and play smart home items unless absolutely necessary.

I have taken bits from other posts on the web and come up with the below code, this is completely untested and I have no idea if what I am doing will even work.

// Wire - Version: Latest 
#include <Wire.h>

#define SLAVE_ADDRESS 0x04
#define PIN_SLAVE 13
int size = 0;
int commands[];
int commandsReceived[];
int commandsRequest[];

void setup() {
    pinMode(PIN_SLAVE, OUTPUT);
    
    // Start serial for output
    Serial.begin(9600);
    
    // Initialize i2c as slave
    Wire.begin(SLAVE_ADDRESS);

    // Define callbacks for i2c communication
    Wire.onReceive(receiveData);
    Wire.onRequest(requestData);
}

void loop() {
    delay(100);
}

// callback for received data
void receiveData(int byteCount) {
    clearCommands();
    
    byte receive;
    int index = 0;

    while (Wire.available()) {
        receive = Wire.read();
        
        if (!commands[index]) {
            commands[index] = receive << 8 | Wire.read();
        }
        
        if (receive == '|') {
            index++;
            commands[index] = Wire.read() << 8 | Wire.read();
        } else {
            commandsReceived[index] = receive << 8 | Wire.read();
        }
    }
    
    size = index;
}

// callback for sending data
void requestData() {
    int bufferSize = 0;
    byte buffer[];
    
    if (size > 0) {
        for (int index = 0; index < size; index++) {
            byte returnValue = doCommand(index);
            buffer[(index * 2)] = returnValue >> 8;
            buffer[((index * 2) + 1)] = returnValue & 255;
            bufferSize += 2;
        }
    }

    Wire.write(buffer, bufferSize);
}

int doCommand(int index) {
    int command = commands[index];
    int commandReceived = commandsReceived[index];
    
    switch(command) {
        case 1:
            return light(commandReceived);
        break;
        case 2:
        break;
    }
}

void clearCommands() {
    if (size > 0) {
        for (int i = 0; i < size; i++) {
            delete commands[i];
            delete commandsReceived[i];
            delete commandsRequest[i];
        }
        
        size = 0;
    }
}

// Commands
void light(bool onOff) {
    digitalWrite(PIN_RELAY, onOff);
    Serial.println("Light " + ((onOff) ? 'On' : 'Off'));
    
    return onOff;
}

Raspberry Pi connected to multiple Arduinos over I2C (Each Arduino would be placed into each room

. . and the maximum recommended cable length for I2C is . . . what?

sigh that is a very good point. I was under the impression that it was good enough to wire your home.

The clue is in the abbreviation/acronyn - Inter-integrated Circuit. It was designed to allow ICs on a circuit board to communicate.

to my observation, i2c did not work very well with AVR Arduinos to Raspi - perhaps it was a clock stretching issue (using WiringPi and Jessie releases from 2016); OTOH, the Due always worked very well, up to 1.5m cable lenght, including the Due, a RTC, and an IMU. But perhaps this AVR issue is meanwhile resolved with the new Linux kernel used by Stretch.
Nonetheless for your task I would work with UART instead, and for long distances I would choose 2x HC-05 interfaces in between. The HC-05 on the Pi also works at the USB ports, and having e.g. 3 HC-05 pairs at 3 different USB ports plus another one at the serial0 UART will provide you all over 4 serial connections to 4 different Arduinos.
And when you'll plug an USB hub to your Pi, then even some more... what would you think about that?

ps,
in case you had many many more than just 4 clients to host, then perhaps consider using a web interface. The ESP8266 nodeMCU works very well like stand-alone Arduinos (it can be programmed directly by the Arduino IDE just like Nanos) and can communicate to web servers via different WiFi or ESP8266web client/server libs to the Pi.
The Pi may work as the webserver then, but tbh, that's not always trivial to program.
So you might like to connect an additional nodeMCU to the Pi via USB/serial or i2c and use this extra one working as a ESP8266 web server to all those ESP8266 web clients, either one using the handsome Arduino libs. And not to forget, 1 ESP8266 costs just less than 5 EUR each.

Thank you for the information, I think it will definitely be more than 4, please see attached a first draft - very rough idea, of what I would like to achieve...

For this type of application, with potential EMI everywhere and "long" distance cables, an architecture with a CAN bus seems to me the best option. Note that CAN bus is widely used for other applications than automotive.

With a CAN Bus, you can connect up to 120 nodes, from 1Mbps for a 40 meters bus to 100 Kbps for a 500 meters bus.

thanks, I'll look into that!

I am looking at using CAT6 cable throughout (purely for the extra shielding capabilities) and I'd like to limit any wifi/rf modules as I feel that wired is a lot more reliable and longer lasting. I don't think any single cable would be longer than 40 meters but I'd have to do some calculations, the building itself is 21m in length and I'd like to take all cabling as direct as possible to a server room where the primary controller will be housed.

I tend to favor wired when feasible for the same reasons.

RS485 is another good choice.

RS-485 networks can achieve reliable data transmissions in electrically noisy environments. By considering the tradeoff between data rate and cable length, you can design a system that achieves data rates in excess of 50Mbps over cable lengths of hundreds of meters, and without repeaters.
Full Guide to Serial Communication Protocol and Our RS-485 |

Reliable, cheap and fairly simple.
Our own @Nick Gammon has a good write up with libraries here: https://www.gammon.com.au/forum/?id=11428

perhaps also think about WiFi as there isn't any much safer data transmission control protocol than TCP/IP, and you can drop all your cables to wire to either client.

look for the P82B715PN

It allows to extend the I2C distance to 20m-30m, using CAT5 cable