convert signed int to bytes and back

Hey

So I am ables to convert an unsigned int to two bytes and rebuild it no problem.

int myInt = 900;
byte myBytes[2];
myBytes[0] = 900/256;
myBytes[1] = 900%256;
int newInt = myBytes[0]*256+myBytes[1];

also managed to do it this way

int myInt = 900;
byte myBytes[2];
myBytes[0] = (myInt >> 8);
myBytes[1] = myInt;
int newInt = (myBytes[0] << 8) | (myBytes[1]);

but as soon as i change 900 to -900 i get the result as 64636. I'm sure there must be a simple solution for this but my search of the forum and google only led me to the second method i showed above and it doesn't work either.

try to change

byte myBytes[2];

to

signed char myBytes[2];

As an alternative:

union unionForm {
  byte myBytes[2];
  int myInt;
} myUnion;

void setup() {
  // put your setup code here, to run once:
  int testInt = 900;
  int newInt;
  
  Serial.begin(9600);
  myUnion.myInt = 900;
  Serial.print("900 in HEX = ");
  for (int i = 0; i < sizeof(int); i++) {
    Serial.print(myUnion.myBytes[i], HEX);
    Serial.print(" ");
  }
  Serial.println();
  newInt = myUnion.myInt;
  Serial.print("newInt = ");
  Serial.println(newInt);
}

void loop() {

}

Since the program prints out 900 as 84 3, it gives you a clue as to the order in which the Arduino stores int data.

awesome thankyou robtillart!

Any chance you could explain why that works and bytes don't?

Hey me again...

I recently asked about converting a signed int to bytes and the answer i got here worked a charm! I am now considering doing the same with signed longs but expanding the method doesn't appear to be working for me!

  int myInt = -900;
  signed char myBytes[2];
  myBytes[0] = (myInt >> 8);
  myBytes[1] = myInt;
  int newInt = (myBytes[0] << 8) | (myBytes[1]);
  Serial.println(newInt);
  delay(1000);
  
  long myLong = 123456;
  signed char myBytes2[4];
  myBytes2[0] = (myLong >> 24);
  myBytes2[1] = (myLong >> 16);
  myBytes2[2] = (myLong >> 8);
  myBytes2[3] = myLong;
  long newLong = (myBytes2[0] << 24) | (myBytes2[1] << 16) | (myBytes2[2] << 8) | (myBytes2[3]);
  Serial.println(newLong);
  delay(1000);

I get -7616 instead of 123456 :frowning: can't even work this one out in positive numbers hah!

(if anyone is feeling like a prophet then you might have anticipated that floats are coming next - trying to exapnd the SpiRam library to make it allot less hassel to store ints, longs floats etc. GitHub - dmason1992/SpiRam_Extended: An extended version of the SpiRam library (includes functions for reading and writing variety of variable types) if interested)

google only led me to the second method i showed above and it doesn't work either.

Are you sure? The "second method" you posted prints out -900 for me.

void setup() {
Serial.begin (9600);
int myInt = -900;
byte myBytes[2];
myBytes[0] = (myInt >> 8);
myBytes[1] = myInt;
int newInt = (myBytes[0] << 8) | (myBytes[1]);
Serial.print(newInt);
}

void loop() {}

I think this is basically the same topic so I am going to merge these threads.

  Serial.println (myBytes2[0] << 24);

That gives zero. You have shifted one byte left 24 bits. Ditto for:

  Serial.println (myBytes2[1] << 16);

Now the interesting bit:

  Serial.println (myBytes2[2] << 8);

That prints -7680.

The char was promoted to an int and shifted left 8 bits. Hence -7680.

Add 64 and you get -7616. Which is what you got for your result.

You have to consider intermediate data types in expressions.

Have a look at these two functions that I use for writing or reading any value from EEPROM. It works with floats, longs, bytes, chars, and even custom types like structs and class instances.

Delta_G this looks like it could save me allot of time! I will have a look into it now

Nick I had a look at the article and from what i could tell i needed to try this (it didn't work)

long myLong = 123456;
  signed char myBytes2[4];
  myBytes2[0] = char (myLong >> 24);
  myBytes2[1] = char (myLong >> 16);
  myBytes2[2] = char (myLong >> 8);
  myBytes2[3] = char (myLong);
  long newLong = (myBytes2[0] << 24) | (myBytes2[1] << 16) | (myBytes2[2] << 8) | (myBytes2[3]);
  Serial.println(newLong);
  delay(1000);

Ok so trying the Unions ( something i am going to have to read up on! - only learnt programming through using arudino so i'm getting their slowly) I get this error

error: 'T' has not been declared

Also this still doesn't seem to work - sorry for being so thick sculled here! prints out -7616

long myLong = 123456;
  signed char myBytes2[4];
  myBytes2[0] = (myLong >> 24);
  myBytes2[1] = (myLong >> 16);
  myBytes2[2] = (myLong >> 8);
  myBytes2[3] = myLong;
  long newLong = ((long)myBytes2[0] << 24) | ((long)myBytes2[1] << 16) | ((int)myBytes2[2] << 8) | (myBytes2[3]);
  Serial.println(newLong);
  delay(1000);

error: 'T' has not been declared

Ok so fixed this (forgot to include the template in the header file as well. Now i get this problem XD

D:\Users\Dropbox\Arduino\libraries\SpiRam_Extended\SpiRam_Extended.cpp: In function 'int writeTest(long int, T&)':
D:\Users\Dropbox\Arduino\libraries\SpiRam_Extended\SpiRam_Extended.cpp:195:37: error: there are no arguments to '_prepare' that depend on a template parameter, so a declaration of '_prepare' must be available [-fpermissive]
D:\Users\Dropbox\Arduino\libraries\SpiRam_Extended\SpiRam_Extended.cpp:195:37: note: (if you use '-fpermissive', G++ will accept your code, but allowing the use of an undeclared name is deprecated)
D:\Users\Dropbox\Arduino\libraries\SpiRam_Extended\SpiRam_Extended.cpp: In function 'int readTest(long int, T&)':
D:\Users\Dropbox\Arduino\libraries\SpiRam_Extended\SpiRam_Extended.cpp:213:36: error: there are no arguments to '_prepare' that depend on a template parameter, so a declaration of '_prepare' must be available [-fpermissive]

I don't see a union in your posted code.

Ah sorry the stuff i quoted above was trying to fix the shifting method

cpp file

template<class T>
int writeTest(long address, T& value)
{
  union {
    T type;
    byte b[sizeof(T)];
  }
  temp;

  temp.type = value;
  _prepare(STREAM_MODE,WRITE,address);
  for (unsigned int i = 0; i < sizeof(T); i++)
  {
    SPI.transfer(address + i, temp.b[i]);
  }
  return sizeof(T);
}


template<class T>
int readTest(long address, T& value)
{
  union {
    T type;
    byte b[sizeof(T)];
  }
  temp;

  _prepare(STREAM_MODE,READ,address);
  for (unsigned int i = 0; i < sizeof(T); i++)
  {
    temp.b[i] = SPI.transfer(0xFF);
  }
  value = temp.type;
  return sizeof(T);
}

header file

template<class T>
    int writeTest(long address, T& value);
    template<class T>
    int readTest(long address, T& value);

Try this:

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();

  typedef union {
    long num;
    byte b [sizeof(long)];
  } myUnion;
  
  myUnion foo;
  foo.num = 123456;
  for (int i = 0; i < sizeof (long); i++)
    Serial.println ((int) foo.b [i]);
 
  // make another one
  myUnion bar;
  // copy each byte in
  for (int i = 0; i < sizeof (long); i++)
    bar.b [i] = foo.b [i];
  // print the resulting long
  Serial.println (bar.num);
  }  // end of setup
void loop () { }

Output:

64
226
1
0
123456

I tried this and it works on my Teensy 3.1 :slight_smile: - I'm gonna head to bed now but I will look into it more tomorrow and try to apply what you have shown me in this demo script to the library!

I'm sorry if this post is beside the point, as I quickly glanced over the posts rather than reading them.

The problem is sending multiple-byte-values over a serial interface that works on a per-byte basis. Obviously, longer types are stored as bytes, so all you need to do is take its address and convert this to a byte*. The union approach does the trick but seems a little verbose. Why not use reinterpret_cast?

long val = 0x89abcdef; // 4 bytes!
Serial.write(reinterpret_cast<byte*>(&val), sizeof(val));

Just keep in mind you might have to compensate for endianness when putting it back together at the other end of the line. Probably that's not even necessary.

PS. Code not tested so could contain errors. I hope the idea is clear though.

Edit: By the way, this works for any type (floats, doubles, user-defined types, whatever). If you want, you can make it a (very simple, not scary at all) template:

template <typename T>
void serialWrite(T *buf)
{
    Serial.write(reinterpret_cast<byte*>(buf), sizeof(T));
}

And simply call it like so:

serialWrite(&val); // same 'val' as above

The OP did not specifically mention sending the data anywhere (although you might guess that is his/her intention).

There are already templated functions that simplify doing that, plus what you (jorenheit) suggest. I would be worried a bit about sending raw data down the serial port. For one thing, the 0x00 bytes might be treated as the end of the data stream.

For one thing, the 0x00 bytes might be treated as the end of the data stream.

Whether or not 0x00 bytes are treated as delimiters depends on the receiver implementation. I have done exactly this. The data I was sending could be anything, so it wouldn't be safe to define a delimiter at all. What else would you think is wrong with communicating data like this?

Well, if you lose a single byte due to a buffer overflow, then everything is out by one. So what used to be:

1 2 3 4 5 6 7 8 becomes 1 2 3 4 6 7 8 9 (for example).

I would have a proper "start of packet" byte, the data, preferably a sumcheck, and then an "end of packet".

Or, turning the data into straight ASCII text would partly do that, because then a space (say) delimits the number, and it needs to be "0" .. "9".

Speaking for myself, I considered plain ascii, but that would simply bloat the data too much (1 byte per digit). On top of that, it requires pre- and post-processing if used for anything else besides displaying.

Implementing a protocol like you suggest would definately help verify the integrity of the data. You'd have to have multiple-byte header- and footer-codes to minimize the chance of collisions though. How many would be sufficient depends on the problem. However... I think at this point we're back at square 1. What if one of the control-bytes gets lost for whatever reason?

Another option would be to let the first 4 bytes hold the number of elements, such that the receiver can just wait for that number to show up (and implement some time-out).

In the end, you're still in need of a way of converting the data to raw bytes so the cast I suggested is still valid I think.