[solved] Arduino I2C (Slave) sending Struct to Raspberry Pi (C)

Hello,

I am building a weather station with multiple sensors all communicating to a arduino (atmega 328p on a breadboard) over a RF 433Mhz link. I have successfully sent a struct over the air containing temperature, battery status and sensor name.

This central arduino is connected to a raspberry pi over I2C and I am trying to send the struct to the raspberry pi (using C, not Python). I have got a little success but getting stuck and hoping someone will be able to point me in the right direction.

Here is the code from the arduino (not all of it as its a little big but can add as attachment if necessary).

struct sensor_Data 
{
  int temperature; 
  char sensorID[10];
  int perBatt;
};

union structToSendOverRF
{
  sensor_Data tempSensorValues;
  byte packet[sizeof(sensor_Data)];
};
structToSendOverRF sts;

// callback for sending data
void sendData()
{
 //Wire.write((byte *) &tempSensorValues, sizeof tempSensorValues);
 Wire.write(sts.packet, sizeof(sensor_Data));
 Serial.println(sizeof(sensor_Data));
}
void receiveData(int byteCount)
{
 int number;
 while(Wire.available()) 
 {
  number = Wire.read();
  if (number == 1)
  {
    digitalWrite(dhterror_LED, HIGH);
  } 
 }
}

I saw someone in another forum post used a union to send it as bytes and then rebuild it later.

On the raspberry Pi side I managed to get the temperature reading but can't get anything else.

#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
 
// The PiWeather board i2c address
#define ADDRESS 0x04
 
// The I2C bus: This is for V2 pi's. For V1 Model B you need i2c-0
static const char *devName = "/dev/i2c-0";

struct sensor_Data 
{
  int temperature; 
  char sensorID[10];
  int perBatt;
};

union structRecOverRF
{
  struct sensor_Data outside;
  char packet[20];
};
union structRecOverRF sts;
 
int main() 
{
  //printf("Size of struct %i", sizeof(struct sensor_Data));
  printf("I2C: Connecting\n");
  int file;
 
  if ((file = open(devName, O_RDWR)) < 0) {
    fprintf(stderr, "I2C: Failed to access %d\n", devName);
    exit(1);
  }
 
  printf("I2C: acquiring buss to 0x%x\n", ADDRESS);
 
  if (ioctl(file, I2C_SLAVE, ADDRESS) < 0) 
  {
    fprintf(stderr, "I2C: Failed to acquire bus access/talk to slave 0x%x\n", ADDRESS);
    exit(1);
  }
 read(file, sts.packet, 2);

 printf("Received %i\n", sts.outside.temperature);
 printf("Received %i\n", sts.outside.perBatt);
 printf("Received %s\n", sts.outside.sensorID);

 
  close(file);
  return (EXIT_SUCCESS);
}

While read(file, sts.packet, 2); is set to '2' I can get the temperature as shown.
I2C: Connecting
I2C: acquiring buss to 0x4
Received 1875
Received 0
Received

If I change it to 3 it turns to a large number.
I2C: Connecting
I2C: acquiring buss to 0x4
Received 5179219
Received 0
Received

If I change it to 14 it turns to a large number and half get the name.
I2C: Connecting
I2C: acquiring buss to 0x4
Received 1968113491
Received 0
Received tside

The arduino is sending 14 bytes according to the sizeof.

Not sure where to go from here, would be very grateful if anyone could assist.

Thank you!

Dump the structure for now, and put the data in an array. That should be easy enough.
byte myArray[14];
The first two bytes is the temperature.
The next 10 are the sensor ID.
The last two is perBatt.

SurferTim:
Dump the structure for now, and put the data in an array. That should be easy enough.
byte myArray[14];
The first two bytes is the temperature.
The next 10 are the sensor ID.
The last two is perBatt.

I tired to implement what you suggested but I couldn't figure out an easy way of converting the two int's and char[] into one single char[] to send over the i2c. In C an array must consist of the same types so the ints would have to be cast?

Anyway after a while I decided to have another go with the struct. On the raspberry pi the size of the struct was 20 bytes and the arduino was 14 bytes. I changed from int to uint16_t as these are always the same size, with that change the raspberry's struct was 14 bytes. The code then reconstructed the struct correctly.

For reference is the code:

#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
 
// The PiWeather board i2c address
#define ADDRESS 0x04
 
// The I2C bus: This is for V2 pi's. For V1 Model B you need i2c-0
static const char *devName = "/dev/i2c-0";

struct sensor_Data 
{
  uint16_t temperature; 
  char sensorID[10];
  uint16_t perBatt;
}outside;
 
int main() 
{
  //printf("Size of struct %i", sizeof(struct sensor_Data));
  printf("I2C: Connecting\n");
  int file;
 
  if ((file = open(devName, O_RDWR)) < 0) {
    fprintf(stderr, "I2C: Failed to access %d\n", devName);
    exit(1);
  }
 
  printf("I2C: acquiring buss to 0x%x\n", ADDRESS);
 
  if (ioctl(file, I2C_SLAVE, ADDRESS) < 0) 
  {
    fprintf(stderr, "I2C: Failed to acquire bus access/talk to slave 0x%x\n", ADDRESS);
    exit(1);
  }
 printf("%i\n",sizeof(outside));
 char buf[14];
 read(file, buf, 14);

 memcpy(&outside, buf, 20);
 
 printf("Received %i\n", outside.temperature);
 printf("Received %i\n", outside.perBatt);
 printf("Received %s\n", outside.sensorID);

 
  close(file);
  return (EXIT_SUCCESS);
}

The arduino code:

struct sensor_Data 
{
  uint16_t temperature; 
  char sensorID[10];
  uint16_t perBatt;
}tempSensorValues;

//union structToSendOverRF
//{
//  sensor_Data tempSensorValues;
//  byte packet[sizeof(sensor_Data)];
//};
//structToSendOverRF sts;


// callback for sending data
void sendData()
{
 //Wire.write((byte *) &tempSensorValues, sizeof tempSensorValues);
 //Wire.write(sts.packet, sizeof(sensor_Data));
 
 int len = sizeof(struct sensor_Data);
 Serial.println(len);
 char test[len];
 memcpy(test, &tempSensorValues, len);

 //memcpy(&doesitwork, test, len);

 Wire.write(test, len);
}

highByte and lowByte will split an int into two bytes.

To reassemble an int isn't too much more difficult.

int temperature = myArray[0] << 8;
temperature = temperature | myArray[1];

or word (h,l);

Thanks for the tips, did not know you could do that.

I'll mark this thread as solved now.

Thanks!