Go Down

Topic: I2C question (Read 150 times) previous topic - next topic

richardtheboffin

Jan 22, 2019, 02:06 pm Last Edit: Jan 22, 2019, 02:12 pm by richardtheboffin
I read that the latest wire library is now able to deal with any data types rather than just bytes.

(correction, there is an additional library WireData, thanks Nick!)

What is the most elegant method to send a block of say 5 unsigned long variables over I2C?

Put them into a structure and just send it?

Originally I was using memcpy to put them into a byte array, but this wastes ram of course.

Cheers!

pylon

Quote
Put them into a structure and just send it?
The most elegant solution is not to do it. Do the split up into bytes explicitly and send byte by byte. This way it's clear in which order they are transferred. The solution you described just works if you don't cross endianess borders.
As sending the longs is a hack anyway I would just send them.

GolamMostafa

#2
Jan 22, 2019, 08:39 pm Last Edit: Jan 22, 2019, 08:49 pm by GolamMostafa
Put them into a structure and just send it?
Yes!

The WireData.h Library works well for transmission/reception of structure data without any problem for endianess.


Master Codes (Tested)n in UNO + NANO
Code: [Select]
#include <Wire.h>
#include <WireData.h>

struct
{
  unsigned long x0 = 0x12345678;
  unsigned long x1 = 0x78123456;
  unsigned long x2 = 0x45345634;
} myData;

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

void loop()
{
  Wire.beginTransmission(0x08);
  wireWriteData(myData);
  Wire.endTransmission();
  delay(1000);
}


Slave Codes (Tested) in UNO + NANO
Code: [Select]
#include <Wire.h>
#include <WireData.h>

bool flag1 = LOW;
struct
{
  unsigned long x0;
  unsigned long x1;
  unsigned long x2;
} myData;

void setup()
{
  Serial.begin(9600);
  Wire.begin(0x08);
  Wire.onReceive(i2cReceive);
}

void loop()
{
  if (flag1 == HIGH)
  {
    Serial.println(myData.x0, HEX);
    Serial.println(myData.x1, HEX);
    Serial.println(myData.x2, HEX);
    Serial.println();
    flag1 = LOW;
  }
}

void i2cReceive(int byteCount)
{
  wireReadData(myData.x0);
  wireReadData(myData.x1);
  wireReadData(myData.x2);

  flag1 = HIGH;
}



pylon

Quote
The WireData.h Library works well for transmission/reception of structure data without any problem for endianess.
This is simply not true, at least if we're talking about the same library. There's no handling of endianess in this library so any transfer over platform boundaries (and no, the UNO and the Nano are not a different platform, they even use exactly the same processor) will fail for multi-byte types.

GolamMostafa

#4
Jan 24, 2019, 07:40 am Last Edit: Jan 24, 2019, 09:13 am by GolamMostafa
When the following experiment between UNO-1Master and UNO-2Slave indicates that the Master has sent the most lower byte (0x78) first and the Slave has received that byte first, there is still an endianness problem? All of our common Arduino Learning Kits (UNO, NANO, MEGA, DUE) are of little endian architecture.

UNO-1Master Codes:
Code: [Select]
#include <Wire.h>
#include <WireData.h>

struct
{
  unsigned long x0 = 0x12345678;
  unsigned long x1 = 0x78123456;
  unsigned long x2 = 0x45345634;
} myData;

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

void loop()
{
  Wire.beginTransmission(0x08);
  wireWriteData(myData);
  Wire.endTransmission();
  delay(1000);
}


UNO-2Slave Codes:
Code: [Select]
#include <Wire.h>
#include <WireData.h>

bool flag1 = LOW;
byte recvArray[30];

struct
{
  unsigned long x0;
  unsigned long x1;
  unsigned long x2;
} myData;

void setup()
{
  Serial.begin(9600);
  Wire.begin(0x08);
  Wire.onReceive(i2cReceive);
}

void loop()
{
  if (flag1 == HIGH)
  {
    Serial.println(recvArray[0], HEX); //shows: 78
    /*
      Serial.println(myData.x0, HEX);
      Serial.println(myData.x1, HEX);
      Serial.println(myData.x2, HEX);
      Serial.println();
    */
    flag1 = LOW;
  }
}

void i2cReceive(int byteCount)
{
  for (int i = 0; i < byteCount; i++)
  {
    recvArray[i] = Wire.read();
  }
  // Serial.print(recvArray[0], HEX);
  /*
    wireReadData(myData.x0);
    wireReadData(myData.x1);
    wireReadData(myData.x2);
  */
  flag1 = HIGH;
}

pylon

Quote
All of our common Arduino Learning Kits (UNO, NANO, MEGA, DUE) are of little endian architecture.
This is correct but the Due is an ARM architecture which is bi-endian, so the endianess depends on the compiler used (or even it's switches).
But for example the AVR32 architecture uses big-endian byte order and is used for some Arduino-IDE compatible boards.

The OP asked for an elegant solution. So either handle that explicitly (so the code documents byte order) or transfer everything in network byte order (which is equal to big-endian byte order).

Go Up