3

So after 2 days attempting this I can't get my head round it.

So I am using CAN bus to transfer accelerometer data and time data (the micros() function if under 71 minutes will output data that can be an unsigned long), however I have to send the data as bytes. I have managed to bit shift a 16-bit int into 2 bytes but I am having trouble extrapolating that method (will add later) to using longs. I am having trouble understanding the union method as I am new to creating my own functions, so, how would you guys accomplish this? As simple as possible please :)

  • you subtract the maximum byte 3 can be from the total, and from that subtract the maximum byte 2 can be to get byte 3. byte 4 is just max byte 4 minus number minus max byte 3 +1. – dandavis Dec 13 '18 at 17:27
1

You can achieve what you want by using bitshifting and bit-wise and operators.

Here is an example that lets you extract the individual bytes of your long variable. I think it should be straigtforward to extend it to variables of different length:

void loop() {
  long a=0x12345678, c=0;
  byte b[4];
  Serial.println("Original:");
  Serial.println(a,HEX);
  delay(1000);
  Serial.println("Individual bytes:");
  for (int i=0; i<4; i++)
  {
    b[i]=((a>>(i*8)) & 0xff); //extract the right-most byte of the shifted variable
    Serial.println(b[i],HEX);
    delay(1000);
  }

  for (int i=0; i<4; i++)
  {
    c+=b[i]<<(i*8);
  }
  Serial.println("Reconstructed:");
  Serial.println(c,HEX);
  delay(1000);
}

Depending on what byte ordering (big or little endian) is expected, you might need to reorder the bytes before sending or after receiving.

  • Very straightforward and easy to understand the principle so thankyou, however under "reconstructed" it only prints 5678? – Ross Hanna Dec 14 '18 at 8:00
  • Did you copy the code snippet from my answer? Because if I run the code on my nodemcu I get the right answer. – oh.dae.su Dec 14 '18 at 8:20
  • Yes, i copied and pasted the code, added void setup(); and Serial.begin(9600); to get the code to run on my arduino nano – Ross Hanna Dec 14 '18 at 13:57
  • 1
    by default on an 8 bit AVR, to the right hand side of the equals defaults to int arithmetic which is signed sixteen bit. So i cast the array to a uint32_t in the loop. Unsure of if that is the most efficient way, but it works! Thankyou for your help – Ross Hanna Dec 14 '18 at 19:40
  • yes right. I didn't think about the arduino controller, when I tried on the nodemcu. – oh.dae.su Dec 14 '18 at 19:49
3

The union type is similar to a struct except that each of the members of the element occupy the same memory. If you define a struct so that it has 2 members -- one 4-byte type and one 4-element array of a single byte type, then you can easily refer to the same data as a whole 4-byte element, or byte-wise as you desire.

union packed_long {
  long l;
  byte b[4];
};

This is the type, so you have to declare the variable to be of that type:

packed_long mydata;

Now, you have two sub-elements of mydata: l and b[].

To save your long value, write to the l part:

mydata.l = myaccel.getY();

To access each of the 4 bytes:

byte1 = mydata.b[0];
byte2 = mydata.b[1];
byte3 = mydata.b[2];
byte4 = mydata.b[3];

On the receiving end, you take each of the 4 bytes:

mydata.b[0] = canbus.read();
mydata.b[1] = canbus.read();
mydata.b[2] = canbus.read();
mydata.b[4] = canbus.read();

As long as you receive the bytes in the same order in which you sent them, you can now access your long value in mydata.l.

The actual size of different types is dependent on the compiler and architecture, so you may also wish to define your value as a definite-sized variable to ensure you are always working with 32 bits: int32_t (signed 32-bit integer) or uint32_t (unsigned 32-bit integer).

1

A way to send a long is to mask off and send the LSB byte (CAN standard), then shift the next one into LSB position, for 4 bytes:

uint32_t data;

// Send to CAN bus LSB-first
// This method is destructive; data should be a temp if the
// Arduino is to retain the data after sending it.
for( uint8_t b = 4; b > 0; --b ){
   sendByte(data & 0xFF);  // send curr byte
   data >>= 8;             // shift that byte away
}

// Read from CAN bus
for( uint8_t b = 4; b > 0; --b ){
   data >>= 8;                 // make room for byte
   data |= (readByte() << 8);  // read next higher byte
}
// 
  • OK, so CAN standard is LSB first. As you have knowledge of CAN, from what i can find they send an 8 byte packet, is only one or two of those the data? So for a total of 16 bytes of information, would 8 packets be needed? – Ross Hanna Dec 14 '18 at 8:04
  • You caught me! That's about everything I know about CAN bus, and that was from Wikipedia. There is a lot of CAN bus documentation online; that article's bibliography is good start. – JRobert Dec 14 '18 at 13:56
  • 8 byte is the size of the data payload of a standard can-frame. so you need two standard frames to send 16 byte of information. the newer can-fd supports 64 byte data payload per frame. – oh.dae.su Dec 14 '18 at 18:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.