Not receiving data from i2c slave correctly

My programs dont work right. If i delete a few variables from the struct i get the data as expected. if i add any more than 5 floats to the struct the received data is nan.

my master is mega2560 and the slave is a arduino UNO

Master,

struct testStruct {
  float T1 = 0;
  float T2 = 0;
  float T3 = 0;
  float T4 = 0;
  float T5 = 0;
  float T6 = 0;
  float T7 = 0;
  float T8 = 0;
  float T9 = 0;
};
testStruct ts1;

char messageBuffer[sizeof(ts1)];
unsigned long now = 0;
int state = 0;
#include <Wire.h>

void setup() {
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(115200);  // start serial for output
}

void loop() {
  Wire.requestFrom(2, sizeof(ts1));    // request data from slave device #2

  while (Wire.available())
  {
    Wire.readBytes(messageBuffer , sizeof(ts1));
    // /   strncpy (verifyStart, (char*)messageBuffer, 6);//6 bytes
    memcpy(&ts1, & messageBuffer , sizeof(ts1));
    return;
  }
  delay(100);
}

Slave,


struct testStruct {
  float T1 = 0;
  float T2 = 0;
  float T3 = 0;
  float T4 = 0;
  float T5 = 0;
  float T6 = 0;
  float T7 = 0;
  float T8 = 0;
  float T9 = 0;
};
testStruct ts1;

uint8_t message[sizeof(ts1)];
#include <Wire.h>

void setup()
{
  Wire.begin(2);                // join i2c bus with address #2
  Wire.onRequest(requestEvent); // register event
  Serial.begin(9600);           // start serial for output
}

void loop()
{
  //  delay(100);
}


void requestEvent()
{
  Wire.write((uint8_t*)&ts1, sizeof(ts1));
}

Why if i make more than 5 variables in the struct is the data not received right?

If i just use 1 float variable in the struct the data seems to show up to the master correctly. but not if i use more than 5.

Is this because of the way i use sizeof()

Split your struct and transmit each part in a different transmission or increase the I2C buffer size for each controller.

Im curious how would one split the struct into multiple messages, also i just read the Wire library has a 32byte restriction. my sizeof() returns 36 bytes. IS that the problem? can i increase the wire buffer without modifying the library?

Split the struct into multiple small structs.

No.

What is the most reasonable solution. there are other i2c devices on this bus. How do i split into multiple transmissions, how would i receive that?

It also looks like my buffer may already be larger than default. i don't remember changing this,


#ifndef TwoWire_h
#define TwoWire_h

#include <inttypes.h>
#include "Stream.h"

#define BUFFER_LENGTH 128

the arduino website says invalid location for wire, looks like my pc using this one,

Using library Wire at version 1.0 in folder: C:\Users\username\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.4\libraries\Wire

which is set to 32kb. ill try it and see what happens. should i be worried about changing the buffer to 256?

So i set the buffer to 128 but this made the slave not receive the data. Am i missing somthing. do i need to change something else??

Do it as everybody else does it: send a register number from which the next value is requested. The slave gets the value from its array of values and transmits it. Eventually add auto-increment or multiple sequential transfers. For inspiration read the communication protocol section of some I2C sensor datasheet.

Thanks but that don't make any sense to me. I just want to send the struct. why does increasing the buffer from 32 break communications. am i suppose to change something else?

Then reinvent the wheel again. Good luck :wink:

what does this even mean.

Float values are 4 bytes so instead ill just convert my floats to ints that are 2 bytes and i can convert the int back into float.

This will cut my struct in half for sending. I think itl be okay to convert temperature into int.

this should be okay for up to 327.67 f

If you changed a number from 32 to 128 or more, then please delete the Arduino files (not your project files), find the hidden "arduino15" folder and delete that and reinstall Arduino.
You have return to a reliable Arduino environment.
We can not help you with other problems if you do not return to a normal Arduino build environment, because it might cause other (memory) problems. I wonder what else you have changed.

If you want a larger buffer, then buy two Arduino boards with the SAMD21G ARM M0+ processor (Arduino MKR Zero and others). They have a buffer size of 256.

A few notes:

  1. The buffer size is defined in two places and a number of buffers are created (three or four). Together with your struct and messageBuffer, the Arduino Uno sram memory will be too small.
  2. There is no need to use a 'while' or a 'return' in the loop(). Instead of the 'while', you can do a test if the right amount of data was received with: if(Wire.available() == sizeof(ts1))
  3. There is no need for a 'messageBuffer' or 'message'. The struct itself can be used.

For an Arduino Uno, stick with the maximum of 32 bytes.
Put a index number or identifier in front of 5 floats and transmit the data in two parts.
In the Slave code, put the right data at the right place. It will be easier with an array of floats instead of a struct.

It is possible to emulate a sensor with registers and use a register_address and length. Then any kind of combination of data is possible, but it needs some extra code.

Question: Why do you want to use the I2C bus and why do you have a Mega and a Uno in your project ? Can you do your project with a single Arduino board ?

I guess i could use 1 arduino but i just don't want to. the seconds i2c arduino give me more inputs, PWM, analog, adc etc.

i don't quite understand that. You mean an array of floats? i think since my temp range is limited, I could convert my floats to int to save space?

This is what i intend to do. do you see any error?

#include <OneWire.h>
#include <DallasTemperature.h>

#define ONE_WIRE_BUS 13

#include <Wire.h>

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature tempSensors(&oneWire);

uint8_t t9Temp[8] = { 0x28, 0x0E, 0xD3, 0x45, 0x92, 0x15, 0x02, 0xB4 }; // underT
uint8_t t1Temp[8] = { 0x28, 0xC6, 0x7C, 0x45, 0x92, 0x18, 0x02, 0x5F };
uint8_t t2Temp[8] = { 0x28, 0x67, 0xDa, 0x45, 0x92, 0x11, 0x02, 0x29 };
uint8_t t3Temp[8] = { 0x28, 0x2D, 0x1B, 0x45, 0x92, 0x05, 0x02, 0xAB };
uint8_t t4Temp[8] = { 0x28, 0xFD, 0xF5, 0x45, 0x92, 0x11, 0x02, 0xC0 };
uint8_t t5Temp[8] = { 0x28, 0x0C, 0xA4, 0x45, 0x92, 0x14, 0x02, 0x22 };
uint8_t t6Temp [8] = { 0x28, 0xFF, 0xAE, 0x9A, 0xA3, 0x16, 0x05, 0x48 };
uint8_t t7Temp[8] = { 0x28, 0x82, 0x91, 0x45, 0x92, 0x10, 0x02, 0xB6 }; //main ac evaporator
uint8_t t8Temp[8] = { 0x28, 0xCF, 0x7D, 0x45, 0x92, 0x18, 0x02, 0x04 }; //main ac evaporator

int tempState = 0;


struct testStruct {
  int T1 = 20;
  int T2 = 20.02;
  int T3 = 20.20;
  int T4 = 20.20;
  int T5 = 20.20;
  int T6 = 20.22;
  int T7 = 22.22;
  int T8 = 22.55;
  int T9 = 22.58;
};
testStruct ts1;

int  resolution = 12;
unsigned long lastTempRequest = 0;
int  delayInMillis = 780;
float temperature = 0.0;
unsigned long now = 0;

void setup(void)
{
  Serial.begin(115200);
  Wire.begin(0x50);                // join i2c bus with address #2
  Wire.onRequest(requestEvent); // register event
  tempSensors.begin();
  tempSensors.setResolution(resolution);
  tempSensors.setWaitForConversion(false);

}

void loop(void)
{
  if (millis() - now >= 1000) {
    if (tempState == 0) {
      tempSensors.requestTemperatures();
      lastTempRequest = millis();
      tempState = 1;
    }
    if (millis() - now >= 2000) {
      if (tempState == 1) {
        float tempF =  tempSensors.getTempF(t1Temp);
        if (tempF > 0) {
          ts1.T1 = tempF * 100;
        }
        tempF =  tempSensors.getTempF(t2Temp);
        if (tempF > 0) {
          ts1.T2 = tempF * 100;
        }
        tempF =  tempSensors.getTempF(t3Temp);
        if (tempF > 0) {
          ts1.T3 =  tempF * 100;
        }
        tempF =  tempSensors.getTempF(t4Temp);
        if (tempF > 0) {
          ts1.T4 =  tempF * 100;
        }
        tempF =  tempSensors.getTempF(t5Temp);
        if (tempF > 0) {
          ts1.T5 =  tempF * 100;
        }
        tempF =  tempSensors.getTempF(t6Temp);
        if (tempF > 0) {
          ts1.T6 =  tempF * 100;
        }
        tempF =  tempSensors.getTempF(t7Temp);
        if (tempF > 0) {
          ts1.T7 =  tempF * 100;
        }
        tempF =  tempSensors.getTempF(t8Temp);
        if (tempF > 0) {
          ts1.T8 = tempF * 100;
        }
        tempF =  tempSensors.getTempF(t9Temp);
        if (tempF > 0) {
          ts1.T9 = tempF * 100;
        }
        lastTempRequest = millis();
        now = millis();
        tempState = 0;
      }
    }
  }
  delay(1);
}


void requestEvent()
{
  Serial.println("sent");
  Wire.write((uint8_t*)&ts1, sizeof(ts1));
}

ts1 needs to be declared volatile, and interrupts need to be temporarily disabled while accessing ts1 outside of requestEvent().

Arrays would be helpful, if nothing else the code itself would be much shorter.

is this valid to make all members volatile?

struct testStruct {
  int T1 = 0;
  int T2 = 0;
  int T3 = 0;
  int T4 = 0;
  int T5 = 0;
  int T6 = 0;
  int T7 = 0;
  int T8 = 0;
  int T9 = 0;
};
volatile testStruct ts1;

Yes.

A little off topic but, do i need to do this with variables i use with IRRemote ?

I think you mentioned before any variable that has anything to do with ISR.

I have no idea, have not worked with that library much, and do not know exactly how you are using the variables.

But if i have to disable interrupts when i access ts1, this means i should disable interrupt when i print the ts1 variables to lcd?

What happens if i don't disable interrupts?