Sending bytes via I2C and converting to array of floats

I am working with an Arduino Uno and a Particle Photon. I "simply" want to send numbers from the Arduino to the Photon via the I2C interface.

The problem is that I have 8 numbers to send based on sensor readings and I can't figure out how to send and array of chars and convert it into an array of floats.

From their I can upload the data to the cloud, I know how to do that part, its the char to float conversion and array transmission that I can't figure out.

Any advice?

drmesa:
The problem is that I have 8 numbers to send based on sensor readings and I can't figure out how to send and array of chars and convert it into an array of floats.

Are you saying you have numbers that you want to convert to char and send that to the Particle and it will convert char back to floats and store them in an array?

The library has built in support to send arrays.

Use a union to do the conversion for you. For example:

union f_arr
{
    byte    fbytes[sizeof(float)];
    float   fvalue;    
    
} flt;

void setup( void )
{
    Serial.begin( 9600 );

    flt.fvalue = 25.5467;
    
    Serial.println( flt.fvalue, 4 );
    for( int i=0; i<sizeof( float); i++ )
    {
        Serial.print( flt.fbytes[i], HEX );
        Serial.println( " " );
        
    }//for

}//setup

void loop( void )
{
    
}//loop

dougp, Yes that's what I need to do. From there I can take the data from the array and send to the cloud as needed.

larryd, Which library are you referring to? The Wire library? I've not seen that functionality used before.

Blackfin, Is fvalue where I insert the value I need to convert?

A pseudo-code example:

#include <Wire.h>

union f_arr
{
    byte    fbytes[sizeof(float)];
    float   fvalue;    
    
} flt;

float fArray[] = 
{
    10.1,
    9.6,
    12.7,
    128.567
    15.0,
    0.0045;
};


void setup() 
{
    Wire.begin(); // join i2c bus (address optional for master)
    
}//setup

void loop() 
{
    Wire.beginTransmission( SOME_DEV_ADDRESS );
    Wire.write( SomeInstruction  );
    
    for( int i=0; i<6; i++ )
    {
        //assign fvalue the flaot we want to decompose and send
        flt.fvalue = fArray[i];
        ////walk through the fbyte[] array of the union and send the bytes
        for( int j=0; j<4; j++ )
            Wire.write( flt.fbytes[j]  );
            
    }//for
        
    Wire.endTransmission();     // stop transmitting

    //.
    //.
    //.
    
}//loop

The receiver the puts the bytes into a union of the same type (and in the correct order) and extracts the float.

Example:

byte eightByteArray[8] = {0, 100, 200, 3, 4, 5, 6, 7};
.
.
.
//send 8 bytes from the 'eightByteArray' array
Wire.write(eightByteArray, 8 );

//end transmitting (data is actually sent at this time)
Wire.endTransmission();
.
.
.

dougp:
Are you saying you have numbers that you want to convert to char and send that to the Particle and it will convert char back to floats and store them in an array?

drmesa:
dougp, Yes that's what I need to do. From there I can take the data from the array and send to the cloud as needed.

1. You have UNO Board and Photon Particle Board which are connected together via I2C Bus.

2. You have 8 numbers. I am assuming that they are float numbers which you have not explicitly mentioned i your post; but, it is understood. They are stored in an array.

3. You want to send these numbers from UNO to PP using I2C Bus.

4. These are the steps that could be taken:
(1) Declare your float numbers:

float myfloat[] = {1.23, 4.56, 7.89, 12.45, 624.75, 11.23, 74.12, 0.07};

(2) Send them to PP using I2C_Anything.h Library which converts each float number to its corresponding 32-bit (4-byte) number and sends to PP. (Edit:) reads the bytes of the float number from an unseen array and stores them in FIFO for onward transmission to destination.

void loop()
{
  Wire.beginTransmission(0x08);
  for (int i = 0; i < 8; i++)
  {
    I2C_writeAnything(myfloat[i]);
  }
  Wire.endTransmission();
  delay(1000);
}

(3) At the Receiver/Slave side, collect the received bytes from FIFO and save into an array of float numbers:

void receiveEvent(int howMany) //howMany = nuber of bytes received  
{
  for (int i = 0; i < 8; i++)
  {
    I2C_readAnything(myfloat[i]);
  }
  flag1 = true;
}

(4) Screenshot of the Slave where we can see that the received numbers agree with the numbers sent by Master.
sm69.png

sm69.png

1 Like

GolamMostafa:
(2) Send them to PP using I2C_Anything.h Library which converts each float number to its corresponding 32-bit (4-byte) number and sends to PP.

I doubt that it converts the data in any way.
I think I2C_Anything only gives some syntactic sugar as a write(&object, sizeof(object)) replacement.

GolamMostafa, Thank you for going into detail, I am a noob and needed to see it :slight_smile: However I am unable to replicate your results. The code I used, inspired by yours, is attached below.

anythingtestMASTER.ino (350 Bytes)

anythingtestSLAVE.ino (692 Bytes)

SlaveSerialOut.JPG

Whandall:
I doubt that it converts the data in any way.
I think I2C_Anything only gives some syntactic sugar as a write(&object, sizeof(object)) replacement.

I2C is a byte oriented bus; therefore, the float number 12.34 (for example) has to be transformed into byte form either being in the ASCII domain (0x31, 0x32, 0x2E, 0x33, 0x34) or being in the binary domain 0x414570A4 (it needs to be separated into 4 distinct bytes using pointer or union). So, we need some kind of conversion. The answer (hopefully) could be found in the anatomy of I2C_writeAnything() function.

drmesa:
GolamMostafa, Thank you for going into detail, I am a noob and needed to see it :slight_smile: However I am unable to replicate your results. The code I used, inspired by yours, is attached below.

Your Master's sketch looks alright and is same as mine one. The Slave's program needs some corrections which I have done. The slave sketch is posted below. Please, give a test run and report the result. You need to understand a little bit theory on the I2C Bus for which you may consult this post.
Slave Sketch

#include <Wire.h>
#include <I2C_Anything.h>

float data[8];
bool flag1 = false;//int flag1;

void setup()
{
  Wire.begin(0x08);//();
  Serial.begin(9600);
  //---------------------
  Wire.onReceive(receiveEvent);
}

void loop()
{
  if (flag1 == true)
  {
    Serial.print(data[0], 2);  //2-digit after decimal point
    Serial.print(" ");
    Serial.print(data[1], 2);  //data[1] is in float format
    Serial.print(" ");
    Serial.print(data[2], 2);
    Serial.print(" ");
    Serial.print(data[3], 2);
    Serial.print(" ");
    Serial.print(data[4], 2);
    Serial.print(" ");
    Serial.print(data[5], 2);
    Serial.print(" ");
    Serial.print(data[6], 2);
    Serial.print(" ");
    Serial.println(data[7], 2);
    flag1 = false;
  }
}

void receiveEvent(int howMany) //howMany = nuber of bytes received
{
  for (int i = 0; i < 8; i++)
  {
    I2C_readAnything(data[i]);
  }
  flag1 = true;
}

GolamMostafa:
I2C is a byte oriented bus; therefore, the float number 12.34 (for example) has to be transformed into byte form either being in the ASCII domain (0x31, 0x32, 0x2E, 0x33, 0x34) or being in the binary domain 0x414570A4 (it needs to be separated into 4 distinct bytes using pointer or union). So, we need some kind of conversion. The answer (hopefully) could be found in the anatomy of I2C_writeAnything() function.

Just the usual convoluted misinformation, stuffed with unrelated information.

All bytes of the float are transfered as they are, they are not converted or transformed in any way.

That float is only a interpretation of some bytes, to transfer the bytes that make up such an object,
there is no conversion or transformation necessary.

Whether that byte pattern can be interpreted on the receiving side without conversion depends,
between systems with the same architecture the pattern can be interpreted as a float.

For reference a part of that magic library

template <typename T> unsigned int I2C_writeAnything (const T& value)
  {
  Wire.write((byte *) &value, sizeof (value));
  return sizeof (value);
  }  // end of I2C_writeAnything

Whandall:
Just the usual convoluted misinformation, stuffed with unrelated information.

Sorry, I have not conveyed any 'information/misinformation' whatsoever; I have just put my perception based on the fundamental understanding that the I2C Bus sends (receives) data 1-byte at a time. Therefore, the float number 12.34 has to be split into bytes -- how? The answer is in the I2C_Anything() function and IEEE-754 Standard.

Crap. The bytes are sent in the length of the object.

template <typename T> unsigned int I2C_writeAnything (const T& value)
  {
  Wire.write((byte *) &value, sizeof (value));
  return sizeof (value);
  }  // end of I2C_writeAnything

This will work for objects up to 32 bytes, just like write itself.

I see no real benefit from using the template, and it seems to make believe people in magic.

Whandall:
Crap. The bytes are sent in the length of the object.

The user has given 12.34 and not the bytes; but, the I2C Interface is transferring these bytes:0xA4, 0x70, 0x45, 0x41 as a representative of 12.34. If this is not a transformation/conversion, this is 'Light from the Heaven'.

GolamMostafa:
The user has given 12.34 and not the bytes;

To the compiler that transformed that human readable form into the correct byte values.

GolamMostafa:
If this is not a transformation/conversion, this is 'Light from the Heaven'.

It is named compiler and done by it long before the sketch is run.
There is no 12.34 anywhere in the sketch,
there are some bytes that make up that value when interpreted by your standard.

Some more to your last post

GolamMostafa:
Sorry, I have not conveyed any 'information/misinformation' whatsoever;

You have and you still are by claiming the data had to be transformed/converted in any way to be sent via I2C.

GolamMostafa:
I have just put my perception based on the fundamental understanding that the I2C Bus sends (receives) data 1-byte at a time.

The fundamental unit of transferred information is one byte, I2C transactions can be much longer.

GolamMostafa:
Therefore, the float number 12.34 has to be split into bytes

No, that is your misunderstanding that you are spreading.
The float number consists of bytes, they make up the float, not the other way around.
The bytes can be interpreted as a float value.

GolamMostafa:
how? The answer is in the I2C_Anything() function and IEEE-754 Standard.

I2C_Anything just moves the bytes without any splits/conversions/transformations
The IEEE-754 Standard describes how to interpret bytes as a float (unrelated).

@GolamMostafa there are only bytes, everything is bytes.

Sometimes we combine bytes to bigger objects like int, long, float, ...,
sometimes we combine such objects or bytes to struct, class, array.

But in essence, everything is and stays a sequence of bytes.

Easy to transfer via I2C. :wink:

Whandall:
To the compiler that transformed that human readable form into the correct byte values.

Yes!

It is named compiler and done by it long before the sketch is run.

Yes!

There is no 12.34 anywhere in the sketch,
there are some bytes that make up that value when interpreted by your standard.

There is 12.34 in my sketch:

void setup()
{
   float x = 12.34;    //here is 12.34
}

You have and you still are by claiming the data had to be transformed/converted in any way to be sent via I2C.

Diesel is liquid fuel which we transform into electricity by dynamo for transferring energy from one place to another. Likewise, 12.34 is being transformed into some bytes by the Compiler with the help of the IEEE-754's template and the converted bytes are kept in a 'Magic Array' unseen to user. I2C_Anything() function brings out those bytes from the unseen array to user variables for onward transmission to destination via I2C Bus.

The fundamental unit of transferred information is one byte, I2C transactions can be much longer.

Yes!

No, that is your misunderstanding that you are spreading.

No, that is your misunderstanding that you are conceiving. (paraphrase)

The float number consists of bytes, they make up the float, not the other way around.

The valid bytes could also turn into valid float through union data structure.

The bytes can be interpreted a a float value.

Yes!

I2C_Anything just moves the bytes without any splits/conversions/transformations

Yes! (I have never said that I2C_Anything() function splits the bytes.)

The IEEE-754 Standard describes how to interpret bytes as a float (unrelated).

No! It is related whenever we talk about float number.

At the end of day -- Happy Day!! We all are happy!!! Some one knows 'this one better'; someone else knows 'that one better'; this is how we live in home with peace and prosperity. :slight_smile:

GolamMostafa, Thank you again for your help. The program is now working! I hate to ask more of you at this point, but I will need to send multiple arrays over the I2C from the Arduino to Photon.

This main Arduino is collecting data from other Arduinos in array format, then sending it to the Photon for publishing.

If its possible, and I'm sure it is, maybe it would be easier to send one large array from the main Arduino, 7 Arduinos x 8 readings each = 56 Float Array. The main Arduino I'm using is a Mega2560 so it should be able to handle that large of an array right? This large array will need to be broken down in some way on the Photon in order to sort the data into corresponding publish transmissions.

Basically I need to know how to send multiple arrays in the way we have established or one large array constructed from 7 smaller ones. If one large array is the way to go, then I need help breaking it down on the other end for sorting the data.

Thanks

Working.JPG