Search code examples
arduinoi2c

How to pass Arduino struct to Raspberry Pi via i2c?


For my Arduino, I have a struct:

int temp;

struct dataStruct {
   int Data;          
   int Data2;
   int Data3;
   int Data4;
   int Data5;
} my; 

void setup() {
    Wire.begin(SLAVE_ADDRESS);
    Wire.onReceive(receiveData);
    Wire.onRequest(sendData);
}

void loop(){
    delay(1000)
}

void receiveData(){
    while(Wire.available){
        temp = Wire.read();
    }
}

void sendData(){
    Wire.write((byte *)&my, sizeof(my));
}

And I want to pass the struct to my Raspberry Pi through i2c via the Wire.write function. I realize that simply trying Wire.write(my); will not work so I am wondering if there is a way I can go about doing this? Maybe I need to try a whole other method altogether? I am willing to try other ways as long as I can get the struct transmitted to the Raspberry Pi.

Here is my Python code as well:

import socket
import os
import random
import time
import smbus
import struct
bus = smbus.SMBus(1)
address = 0x04

temp = bytes([0x00, 0x00, 0x00, 0x00, 0x00])
the_struct = [0, 0, 0, 0, 0]

def writeNumber(value):
    bus.write_byte(address, value)
    return -1

def readNumber():
    number = bus.read_byte(address)
    return number

while True:
    temp = readNumber()
    the_struct = struct.unpack('5h', temp)
    print(the_struct)
    time.sleep(1)

Solution

  • You can use Wire.write((byte *)&my, sizeof(my)) to write to the RPi. To read the data, you can unpack the struct into a tuple using the struct module like this:

    import struct
    
    #assuming you've recved the struct over I2C into a bytes object named 'data'
    the_struct = struct.unpack('5h', data)
    

    the_struct now holds the 5 integers in your original struct.

    EDIT

    Firstly, 0x04 is one of the reserved addresses. Try 0x15 instead; anything (almost) from 0x08 upwards will do.

    You are reading a byte and then attempting to unpack that byte into 5 integers. You should instead read 10 bytes, save them one by one to a bytearray and then unpack them as shown earlier. Replace your readNumber() with this:

    def read_block(num_bytes):
        vals = bus.read_i2c_block_data(address, num_bytes)
        return vals
    
    while True:
        temp = read_block(10)  # 10 is the number of bytes you want to read
        the_struct = struct.unpack('5h', temp)
        print(the_struct)
        time.sleep(1)