I2C - invalid data after sending 11 bytes

Hello,

I am working on a small sensor project, where voltage monitor data is being requested by the I2C master.
The slave sends back a predefined message of 28 bytes.
The message looks like this:
Message Header (total of 4 bytes)
Message Length uint16_t
sensor type id uint8_t
software version uint8_t
Message Body (total of 24 bytes)
v0 float
v1 float
v2 float
v3 float
v4 float
v5 float

The strange thing now is, that the master actually receives 28 bytes, and I receive correct values for:
bytes 0-1 → message length
byte 2 → sensor type
byte 3 → software version
bytes 4,5,6,7 → v1 float
bytes 8,9,10,11 → v2 float
but the the values for v3, v4 and v5 stay 0.000000.

I tried so many things, and I am stuck on this for days now… drives me crazy!

Can someone please please please take a look at my code?
Maybe I am missing something really simple here…

Any help is greatly appreciated!

Here’s the code:

I2C MASTER

#include <WProgram.h>
#include <Wire/Wire.h>

// function  prototypes
void PrintValuesToSerial();

#define slaveAddress 0x8 	// the slave I2C address
#define ledPin 13

byte data[28]; // buffer used when receiving data

union float_union { 	byte v_byte[4];	float v_float; } fl; 	// union for float data type
union int16_union { 	byte v_byte[2];	uint16_t v_int16; } i16; // union for int16 data type

volatile byte* header_msglength_ptr;
uint16_t header_msglength = 0;

volatile byte* header_sensortype_ptr;
uint8_t header_sensortype = 0;

volatile byte* header_swversion_ptr;
uint8_t header_swversion = 0;

float v0 = 0.00;
float v1 = 0.00;
float v2 = 0.00;
float v3 = 0.00;
float v4 = 0.00;
float v5 = 0.00;

// start main program
int main()
{
	init();

	setup();

	while (true)
	{
		loop();
		PrintValuesToSerial();
		delay(500);	// request data from slave
	}
}

void setup()
{
	Serial.begin(57600);
	Wire.begin();
}

void loop()
{
	Wire.requestFrom(slaveAddress, 28); // request 28 bytes from slave address

	if (Wire.available())
	{
		digitalWrite(ledPin, HIGH);
		int i = 0;
		while (Wire.available()) // slave may send less than requested
		{
			data[i] = Wire.receive(); // receive a byte as character
			i++;
		}

		//Serial.print("nr of bytes received from slave: ");
		//Serial.println(i);

		if (i == 28) // check if we actually received the amount of bytes that was requested
		{
			// parse header
			i16.v_byte[0] = data[0];
			i16.v_byte[1] = data[1];
			header_msglength = i16.v_int16;

			header_sensortype = data[2];

			header_swversion = data[3];

			//union float_union0 	{byte v_byte[4];float v_float; } fl0; // union for float for voltage cell 1
			fl.v_byte[0] = data[4];
			fl.v_byte[1] = data[5];
			fl.v_byte[2] = data[6];
			fl.v_byte[3] = data[7];
			v0 = fl.v_float;

			//union float_union1 { 	byte v_byte[4];	float v_float; } fl1; // union for float for voltage cell 1
			fl.v_byte[0] = data[8];
			fl.v_byte[1] = data[9];
			fl.v_byte[2] = data[10];
			fl.v_byte[3] = data[11];
			v1 = fl.v_float;

			//union float_union2 { 	byte v_byte[4];	float v_float; } fl2; // union for float for voltage cell 1
			fl.v_byte[0] = data[12];
			fl.v_byte[1] = data[13];
			fl.v_byte[2] = data[14];
			fl.v_byte[3] = data[15];
			v2 = fl.v_float;

			//union float_union3 { 	byte v_byte[4];	float v_float; } fl3; // union for float for voltage cell 1
			fl.v_byte[0] = data[16];
			fl.v_byte[1] = data[17];
			fl.v_byte[2] = data[18];
			fl.v_byte[3] = data[19];
			v3 = fl.v_float;

			//union float_union4 { 	byte v_byte[4];	float v_float; } fl4; // union for float for voltage cell 1
			fl.v_byte[0] = data[20];
			fl.v_byte[1] = data[21];
			fl.v_byte[2] = data[22];
			fl.v_byte[3] = data[23];
			v4 = fl.v_float;

			//union float_union5 { 	byte v_byte[4];	float v_float; } fl5; // union for float for voltage cell 1
			fl.v_byte[0] = data[24];
			fl.v_byte[1] = data[25];
			fl.v_byte[2] = data[26];
			fl.v_byte[3] = data[27];
			v5 = fl.v_float;

		}
		else
		{
			Serial.print("invalid amount of bytes received from slave, expected 28 but received: ");
			Serial.println(i);
		}

		digitalWrite(ledPin, LOW);
	}
}

void PrintValuesToSerial()
{
	Serial.print("Message Length: ");
	Serial.println(header_msglength);

	Serial.print("Sensor type: ");
	Serial.println(header_sensortype, DEC);

	Serial.print("Software version: ");
	Serial.println(header_swversion, DEC);

	Serial.print("v0: ");
	Serial.println(v0, DEC);

	Serial.print("v1: ");
	Serial.println(v1, DEC);

	Serial.print("v2: ");
	Serial.println(v2, DEC);

	Serial.print("v3: ");
	Serial.println(v3,DEC);

	Serial.print("v4: ");
	Serial.println(v4,DEC);

	Serial.print("v5: ");
	Serial.println(v5,DEC);
}

SLAVE

#include <WProgram.h>
#include <Wire/Wire.h>

void requestEvent();

#define slaveAddress 0x8 	// this slave I2C device
#define ledPin 13

// HEADER DATA
volatile byte* header_msglength_ptr;
const uint16_t header_msglength = 28;

volatile byte* header_sensortype_ptr;
uint8_t header_sensortype = 1;

volatile byte* header_swversion_ptr;
const uint8_t header_swversion = 2;

// VOLTAGE SENSOR DATA
volatile byte* v0_ptr;
float v0 = 0.23;

volatile byte* v1_ptr;
float v1 = 1.23;

volatile byte* v2_ptr;
float v2 = 2.23;

volatile byte* v3_ptr;
float v3 = 3.23;

volatile byte* v4_ptr;
float v4 = 4.23;

volatile byte* v5_ptr;
float v5 = 5.23;


// start main program
int main()
{
	init();

	setup();

	while (true)
	{
		loop();
	}
}

void setup()
{
	pinMode(ledPin, OUTPUT);
	Serial.begin(57600);
	Wire.begin(slaveAddress); // join i2c bus, slave address
	Wire.onRequest(requestEvent);
}

void loop()
{

}

void requestEvent()
{
	digitalWrite(ledPin,HIGH);

	//Serial.println("data request received");

		// create message
	// ###############################################
	// create header
	uint8_t Data[28];

	// message length
	header_msglength_ptr = (byte*) &header_msglength; // uint16_t
	Data[0] = header_msglength_ptr[0];
	Data[1] = header_msglength_ptr[1];

	// sensor type
	Data[2] = header_sensortype; // uint8_t

	// software version
	Data[3] = header_swversion; // uint8_t

	// ###############################################
	// voltage sensor data
	v0_ptr = (byte*) &v0;
	Data[4] = v0_ptr[0];
	Data[5] = v0_ptr[1];
	Data[6] = v0_ptr[2];
	Data[7] = v0_ptr[3];

	v1_ptr = (byte*) &v1;
	Data[8] = v1_ptr[0];
	Data[9] = v1_ptr[1];
	Data[10] = v1_ptr[2];
	Data[11] = v1_ptr[3];

	v2_ptr = (byte*) &v2;
	Data[12] = v2_ptr[0];
	Data[13] = v2_ptr[1];
	Data[14] = v2_ptr[2];
	Data[15] = v2_ptr[3];

	v3_ptr = (byte*) &v3;
	Data[16] = v3_ptr[0];
	Data[17] = v3_ptr[1];
	Data[18] = v3_ptr[2];
	Data[19] = v3_ptr[3];

	v4_ptr = (byte*) &v4;
	Data[20] = v4_ptr[0];
	Data[21] = v4_ptr[1];
	Data[22] = v4_ptr[2];
	Data[23] = v4_ptr[3];

	v5_ptr = (byte*) &v5;
	Data[24] = v5_ptr[0];
	Data[25] = v5_ptr[1];
	Data[26] = v5_ptr[2];
	Data[27] = v5_ptr[3];

	Wire.send(Data,28);

	digitalWrite(ledPin, LOW);
}
	byte* Data;

	// message length
	header_msglength_ptr = (byte*) &header_msglength; // uint16_t
	Data[0] = header_msglength_ptr[0];
	Data[1] = header_msglength_ptr[1];

The byte *Data line defines that Data is a pointer to memory that is to be processed as byte. It does NOT actually reserve any space. The Data[n] lines expect that the space will already have been reserved.

Why not just define Data as an array?

byte Data[28];

I tried that, but then I get the error:

invalid conversion from 'volatile unsigned char' to 'byte*'

on the lines where I write to Data (e.g. Data[0] = header_msglength_ptr[0];).

	header_msglength_ptr = (byte*) &header_msglength; // uint16_t
	Data[0] = header_msglength_ptr[0];
	Data[1] = header_msglength_ptr[1];

Edit:
A caset to byte* fixes the compilation error:

Data[0] = (byte*)header_msglength_ptr[0];

I will try to see if it works now :slight_smile:

Yuk

./main.cpp:125: error: no matching function for call to 'TwoWire::send(byte* [28], int)'
D:\Data\Eclipse Workspaces\I2CTest\I2CSlave\ArduinoLib-0022/Wire/Wire.h:54: note: candidates are: void TwoWire::send(uint8_t)
D:\Data\Eclipse Workspaces\I2CTest\I2CSlave\ArduinoLib-0022/Wire/Wire.h:55: note:                 void TwoWire::send(uint8_t*, uint8_t)
D:\Data\Eclipse Workspaces\I2CTest\I2CSlave\ArduinoLib-0022/Wire/Wire.h:56: note:                 void TwoWire::send(int)
D:\Data\Eclipse Workspaces\I2CTest\I2CSlave\ArduinoLib-0022/Wire/Wire.h:57: note:                 void TwoWire::send(char*)

I am used to C#, not C/C++ so I dont have that much experience with pointers... can I do another cast here?

Edit:
I updated the code in the start post; byte* Data; is now uint8_t Data[28].
Now the compiler is happy, and I the master received correct data, until the 11th byte... :frowning:
So the memory allocation was not the cause of the problem.

Something really strange happened...
I reset the default value to 0 (zero) on the MASTER for the variables:

uint16_t header_msglength = 0;
uint8_t header_sensortype = 0;
uint8_t header_swversion = 0;

And now it works like a charm...

I AM FLABBERGASTED!!
AND HAPPY :slight_smile:

I updated the start post, so if anyone ever copies this code... then it works :slight_smile:

So the memory allocation was not the cause of the problem.

Not the only problem, perhaps, but a problem nonetheless.

PaulS:

So the memory allocation was not the cause of the problem.

Not the only problem, perhaps, but a problem nonetheless.

Yes, you are right :slight_smile: that not allocating the buffer was a problem too.