Sending "unsigned long" over I2C

Hi,

How can I send "unsigned long" over I2C from slave to master?

I know that "unsigned long" is four bytes and that I can define in Wire.write how many bytes I want to send and in Wire.requestFrom how many bytes I want to read.

But how do I chop 4 bytes long "unsigned long" in to bytes in slave and how do put the bytes back together in master to have "unsigned long"?

Thanks,
Tipo

The write() has overloaded version for unsigned long and have to be between the beginTransmission() and endTransmission().

Thanks Budvar10, but I'm so newbie that this "The write() has overloaded version for unsigned long" doesn't open up to me.
Can you elaborate little bit?

Thanks,
Tipo

Ahh sorry, I was wrong. It will send just on byte even ulong is passed. Forgot my answer about write(). :confused:

But this one should work: Wire.write(data, length). Data is a pointer to your variable and length is 4.

I use a union structure.

// create a union to hold the data
union Buffer
{
    long longNumber;
    byte longBytes[4];
};

Buffer buffer;  // create an instance of the union 
buffer.longNumber = 0xaabbccdd;  // assign a value to the long number

Now the longBytes array contains the pieces of the long number.
Send the longBytes array using the write(data, length) function.
Have an identical union at the receiving end. Read the bytes into the buffer.longBytes array. Now the buffer.longNumber variable holds the long number.

The same method works for float data type. Just replace the long longNumber with
float floatNumber.

Example code for I2C slave that sends upon request from the I2C master.

Slave sender

// Wire Slave Sender

#include <Wire.h>

union Buffer
{
   unsigned long longNumber;
   byte longBytes[4];
};

Buffer buffer; // create an instance of Buffer

void setup()
{
   pinMode(13, OUTPUT);
   Wire.begin(8);                // join i2c bus with address #8
   Wire.onRequest(requestEvent); // register event
}

void loop()
{
   buffer.longNumber = 0xaabbccdd;
   delay(100);
}

void requestEvent()
{
   Wire.write(buffer.longBytes, 4); // send requested bytes
   digitalWrite(13, !digitalRead(13));  // just an indicator
}

Master requester

// Wire Master Reader

#include <Wire.h>

union Buffer
{
   unsigned long longNumber;
   byte longBytes[4];
};

Buffer buffer;

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

void loop()
{
   Wire.requestFrom(8, 4);    // request 4 bytes from slave device #8
   if (Wire.available() > 0)   // slave may send less than requested
   {
      buffer.longBytes[0] = Wire.read();
      buffer.longBytes[1] = Wire.read();
      buffer.longBytes[2] = Wire.read();
      buffer.longBytes[3] = Wire.read();
      Serial.println(buffer.longNumber, HEX);
   }
   
   delay(1000);
}

@groundFungus: It works, thanks! But I don't understand how. I read about "union", and still don't get it.

In slave the data is stored to buffer.longNumber, but that is not written to I2C. Instead buffer.longBytes is written.

And in master buffer.longBytes are read, but somehow data is in buffer.longNumber even though it doesn't appear to have been transmitted...?

Using this same union concept, can I send two unsigned long's? That's why I'm trying to understand how it works.

Thanks,
Tipo

In the union the two data types occupy the same addresses in memory. In the union you declare an unsigned long. A long is 4 bytes so, let's say, that the long is in memory locations 100, 101, 102 and 103. Those bytes all empty at first. Then in the union, you declare a byte array of 4 bytes. The memory address of byte[0] is 100, byte[1] is 101 and so on and they are also empty.

Now you write an unsigned long to buffer,longNumber, say 0xaabbccdd. Now buffer.byte[0] = aa, buffer.byte[1] = bb, and so on.

It works the other way, too. If you write the 4 bytes of an unsigned long to buffer.bytes[0] through buffer.bytes[3] you can then read the value of buffer.longNmuber.

1 Like

Got it, thanks again!

The same project continues: Now I'm trying to read two integers over I2C. I ended up trying to do it with <I2C_Anything.h>, but it's not working. At the best I've been able to read the first integer.

What can be wrong with the code below?

Thanks,
Tipo

<I2C_Anything.h>

// Written by Nick Gammon
// May 2012

#include <Arduino.h>
#include <Wire.h>

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

template <typename T> unsigned int I2C_readAnything(T& value)
  {
    byte * p = (byte*) &value;
    unsigned int i;
    for (i = 0; i < sizeof value; i++)
          *p++ = Wire.read();
    return i;
  }  // end of I2C_readAnything

Master:

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

int P1; 
int P2;

#define DATALEN (sizeof P1) + (sizeof P2)

void setup() 
{ 
  Wire.begin();      
  Serial.begin(9600); 
  Serial.println("P1: , P2:  ");
  Serial.println("---------------");
}

void loop()
{
  Wire.requestFrom(2, 4);    // request data from slave device #2
  if (Wire.available() == DATALEN) {
    I2C_readAnything (P1);
    I2C_readAnything (P2);
  }
  
  Serial.print(P1);
  Serial.print(" , ");
  Serial.println(P2);
  delay(1000); 
}

Slave:

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

int P1 = 15;
int P2 = 1001;

void requestEvent() 
{
  I2C_writeAnything (P1);
  I2C_writeAnything (P2);
}

void setup() {
  Wire.begin(2);                
  Wire.onRequest(requestEvent); 
  Serial.begin(9600);     
}

void loop() {
}

I got it done easily with this:
http://forum.arduino.cc/index.php?topic=180352.0