Search code examples
arduinoraspberry-pii2c

Use Arduino Mega as I2C Slave with RPi3


I am trying to use Arduino Mega 2560 for extending I/Os of RPi3 with PWM and Analog Inputs. Infact I am not using RPi3 GPIO pins at all as maintaining two voltages for inputs 3.3 and 5 V is difficult.

Basically, I am trying to:

    1. send an Array from RPi3 to set the outputs in Arduino and
    1. send an Array from Arduino to RPi3 giving the status of Inputs.

Some values in the array could go as high as 10000.

I have been able to achieve the Number 1 above without the values higher than 255.

Python Code

bus = smbus.SMBus(1)
address = 0x06

def writeNumber(value):
    bus.write_i2c_block_data(address, 1, [5,0,1,255, 6]) #dummy array as of now. This can go upto 50 values
    return -1


def readNumber():
    # number = bus.read_byte(address)
    data_received_from_Arduino = bus.read_byte(address)
    for i in data_received_from_Arduino:
       print(i)

    return number

while i1:
    writeNumber(1)
    readNumber()

Arduino Code

#include <Wire.h>

#define SLAVE_ADDRESS 0x06
int number[50] = {0};
int inputs[100] = {0};

int state = 0;
int p=0; 

void setup() {
pinMode(13, OUTPUT);

Serial.begin(9600); // start serial for output
// initialize i2c as slave
Wire.begin(SLAVE_ADDRESS);

// define callbacks for i2c communication
Wire.onReceive(receiveData);
Wire.onRequest(sendData);

Serial.println('Ready!');
}

void loop() {

//delay(1);
}

// callback for received data
void receiveData(int byteCount){
  Serial.println(byteCount);
  int p=0;
  while(Wire.available()) {
      number[p] = Wire.read();
      p++;
  }
    for(int k=0; k < 5; k++)  { 
    Serial.print( k);
    Serial.print( ":");
    Serial.println(number[k]); 
  }

}


// callback for sending data
void sendData(){  
  for(int k=0; k < 56;k++) {
  inputs[k] = digitalRead(k);
  Serial.print( k ); Serial.print(" : "); Serial.print(inputs[k]); 
  Serial.println(digitalRead(k));
  }
  Wire.write( inputs,56);
}

Can somebody guide? Does anyone know a sample Git for achieve the above. I can build it up for my application even if the sample is for a small array.


Solution

  • I have been playing around experimenting with sending and receiving four 16-bit numbers from a Raspberry Pi to an Arduino over I2C and got the following working.

    Be aware that I am no expert in SMBus or I2C and I don't know if there are easier ways to do this. I am happy to retract my answer if anyone knows better!

    Here's the code for the Raspberry Pi, it just sends four 16-bit numbers 100, 200, 1000, 10000 and then reads them back.

    #!/usr/bin/env python3
    from smbus import SMBus
    from time import sleep
    
    bus = SMBus(1)
    address = 0x08
    
    def split(v):
        """Split 16-bit value into low and high bytes"""
        lobyte = v & 0xff
        hibyte = (v >> 8) & 0xff
        return lobyte, hibyte
    
    def join(lo,hi):
        return lo | (hi << 8)
    
    def Transmit():
        """Send 100, 200, 1000, 10000 on I2C"""
        a,b = split(100)
        c,d = split(200)
        e,f = split(1000)
        g,h = split(10000)
        bus.write_i2c_block_data(address, a,[b, c, d, e, f, g, h])
    
    def Receive():
        block = bus.read_i2c_block_data(address, 0)
        i = join(block[0],block[1])
        j = join(block[2],block[3])
        k = join(block[4],block[5])
        l = join(block[6],block[7])
        print("{} {} {} {}".format(i,j,k,l))
    
    Transmit()
    sleep(1)
    Receive()
    

    On the Arduino side, I just read four 16-bit numbers from I2C, store them in an array and increment each one. When a read request comes in, I send back the four incremented numbers:

    #include <Wire.h>
    
    const int address= 8;
    
    #define N 4
    
    // Last four 16-bit values we received
    int16_t values[N];
    
    void setup() {
      Serial.begin(9600);
      Serial.print("Starting on i2c address:");
      Serial.println(address,DEC);
      Wire.begin(address);
      Wire.onReceive(receiveEvent);
      Wire.onRequest(requestEvent);
    }
    
    void loop() {
      delay(100);
    }
    
    // callback for when data are received
    void receiveEvent(int nBytes) {
      Serial.print("Received: ");
      Serial.println(nBytes);
    
      if(nBytes != 2 *N){
         Serial.print("I was expecting 8 bytes");
    
         return;
      }
      unsigned char *p = (unsigned char *)&values;
      for(int i=0;i<2*N;i++){
        *p++ = Wire.read();
      }
      // Increment all the values we received
      for(int i=0;i<N;i++){
        values[i]++;
      }
    
    }
    
    // Callback for when data are read
    void requestEvent() {
    
      Serial.println("Data requested");
      // Send back
     Wire.write((const uint8_t*)&values, N*2);
    }
    

    When I run the Python code on the Raspberry Pi, I get:

    ./i2c.py 
    101 201 1001 10001