Sending 4 bytes int number with serial.write to serial

i have a value like 15123145(it is just random) int value greater than 255 and i have to send it to serial as bytes and get this number from C#

and i used serial.write(buf,len)

here is part of my arduino code

int value = 15123145;
byte *p ;
*p = Value;
Serial.write(p, 4);

it is sending value but that is not my value how can i write this integer value to serial port without turning to character array.

Hi -

Remember that int32 is signed so if you need positive numbers then you should use a unsigned int. You can simply use Serial.write(uint32) and receive it in .NET as 4 bytes.

CORRECTION: See posts below, you cannot write Serial.write(uint32)!

Then you can use:

BitConverter.ToInt32()
Returns a 32-bit signed integer converted from four bytes at a specified position in a byte array.

thank you for your answer but this not the problem about c# side the problem is arduino side because when i use the serial.write(uint32) it is sending only 1 byte of it but uint32 4 byte and i want to send all four bytes in the same time.

Use write, not print:

Writes binary data to the serial port. This data is sent as a byte or series of bytes; to send the characters representing the digits of a number use the print() function instead.

http://www.arduino.cc/en/Serial/Write

In your OP you state you are using this. How are you reading the values in .NET and how did you determine that only one byte was written? I use DtrEnable = true on my SerialPort instances and use the DataReceived event.

Don't forget to read all 4 bytes from the Serial after the event has fired.

this is not the problem about c# side you still don't understand my question :slight_smile:
i can read 4 bytes from the c# side

my problem is about arduino side and i am using serial.write in arduino not print.

How did you determine that only one byte was written?

Ahh, found it:

Syntax
Serial.write(val)
Serial.write(str)
Serial.write(buf, len)

Parameters
val: a value to send as a single byte
str: a string to send as a series of bytes
buf: an array to send as a series of bytes
len: the length of the buffer

Because you want to send multiple bytes, you'll need to convert your 32-bit number (long) to a byte array.

Consider this code:

unsigned long data = 4294967295;

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

void loop()
{  
  byte buf[4];
  buf[0] = data & 255;
  buf[1] = (data >> 8)  & 255;
  buf[2] = (data >> 16) & 255;
  buf[3] = (data >> 24) & 255;

  Serial.write(buf, sizeof(buf));
  data -= 1;
  
  delay(3000);
}

Remember this, Arduino uses 16 bits for signed an unsigned integers (unless you are using an Arduino Due).

http://www.arduino.cc/en/Reference/Int
http://www.arduino.cc/en/Reference/UnsignedInt

You probably want to use an unsigned long if you have 32 bits.

http://www.arduino.cc/en/Reference/UnsignedLong

In order for you to read the four bytes, that were written in this sketch, you can use the BitConverter.

class Program
    {
        static SerialPort arduinoSerial = new SerialPort(new Container());

        static void Main(string[] args)
        {
            arduinoSerial.PortName = "COM21";
            arduinoSerial.BaudRate = 9600;
            arduinoSerial.DtrEnable = true;
            arduinoSerial.DataReceived += arduinoSerial_DataReceived;

            Console.Write("Connecting {0} @ {1} ... ", arduinoSerial.PortName, arduinoSerial.BaudRate);
            arduinoSerial.Open();
            Console.WriteLine("Done!");

            Console.ReadLine();
        }

        static void arduinoSerial_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            var bufferSize = arduinoSerial.BytesToRead;
            if (bufferSize >= 4)
            {
                byte[] data = new byte[4];
                arduinoSerial.Read(data, 0,  4);

                UInt32 result = BitConverter.ToUInt32(data, 0);
                Console.WriteLine(result);
            }
            
        }
    }

Also note that unsigned int (UInt32) is 32bit on the .NET Framework. This little console app gives me the following output:

Connecting COM21 @ 9600 ... Done!
4294967294
4294967293
4294967292
4294967291
4294967290

1 Like

thank you for your help. It really helped me now. It is working correctly. :slight_smile:

After thinking about this problem I figured that it would be plain odd if the Serial class didn't support byte pointers instead of arrays. Hoping I could create one I found the following:

size_t Print::write(const uint8_t *buffer, size_t size)
{
  size_t n = 0;
  while (size--) {
    n += write(*buffer++);
  }
  return n;
}

This code seems to do exactly what you want! So why didn't you get it to work? I tried myself and modified my sketch above to be able to test it with the same console app. It works! Don't be thrown off by the "Print" class, the Serial class borrows this function.

This is the sketch now, it produces exactly the same results:

unsigned long data = 4294967295;

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

void loop()
{  
  Serial.write((byte*)&data, sizeof(data));
  data -= 1;
  delay(3000);
}

The address-of operator will convert the variable into a pointer (of the same type) which you can then cast to a byte pointer.

Maybe I should have looked into this a little more but I guess that the documentation threw me off.

Something else I noticed is that the HardwareSerial class contains this:

     inline size_t write(unsigned long n) { return write((uint8_t)n); }
    inline size_t write(long n) { return write((uint8_t)n); }
    inline size_t write(unsigned int n) { return write((uint8_t)n); }
    inline size_t write(int n) { return write((uint8_t)n); }

So basically this casts unsigned and signed longs and ints to byte when calling HardwareSerial.write. This made me scratch my head a little, is it just me or will you never want to do that? ::slight_smile:

Anyway, the Serial_ class (which is defined in USBAPI.h) doesn't have these overloads so I decided to create them:

inline size_t write(unsigned long n) { return write((byte*)&n, sizeof(n)); }
inline size_t write(unsigned int n) { return write((byte*)&n, sizeof(n)); }

Inserting that code into the public section of USBAPI.h will allow you to do this:

unsigned long int32 = 4294967295;
unsigned int int16 = 65535;

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

void loop()
{  
  Serial.write(int32--); // Writes 4 bytes
  Serial.write(int16--); // Writes 2 bytes
  delay(3000);
}

So tell me Arduino community, what do you think? Do you think these simple adaptations will benefit everybody? My take is that this would definitely suit the expectations of consumers of the Serial_ class, especially for beginners.

Who exactly architects and maintains the Arduino core source code? Is that team open for pull requests on Github? And what about those potentially unwanted casts in the HardwareSerial classes?

    int value = 15123145;

You have fallen at the first hurdle. An int holds 32767 at most, so there is no way that 15123145 is in it. The rest therefore is irrelevant.

You don't mean this I suppose?

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  unsigned long foo = 4294967295;
  Serial.println (foo);
  }  // end of setup
void loop () { }

That prints 4294967295.

So basically this casts unsigned and signed longs and ints to byte when calling HardwareSerial.write. This made me scratch my head a little, is it just me or will you never want to do that?

Not in my test, see above.

If you really want to write it "as bytes" (whatever that means) how about:

  unsigned long foo = 4294967295;
  Serial.write ((char *) &foo, 4);

I think you are better off sending a number "in the normal way" (ie. 4294967295) and just decoding it in C# by looking for a delimiter like a space or newline.

 int value = 15123145;
    byte *p ;
    *p = Value;

That has made a pointer to address 15123145, except it isn't address 15123145 because:

  • That is too large to fit into memory (of which you possibly have a maximum of 2048 bytes)
  • 15123145 won't fit into an int anyway

I was wrong. You have made a pointer which is not initialized. Then you put the number 15123145 into some random memory address of one byte (8 bits). That certainly won't fit into 8 bits, and since it is uninitialized it will probably crash the program.

Quite. Convert it to an unsigned long and you still have the problem of pointer conversion.

   unsigned long value = 15123145;
    byte *p;
    p = (byte*)&value;
    Serial.write(p, 4);

So basically this casts unsigned and signed longs and ints to byte when calling HardwareSerial.write.

Perhaps, but Serial.print will work OK.

Fexduino:
Quite. Convert it to an unsigned long and you still have the problem of pointer conversion.

Agreed. But I think my one-line solution is a bit simpler. :wink:

The write function is supposed to write one byte*. It is called in various other places (eg. by print) for doing byte-by-byte transmission. So converting any input argument to a byte is reasonable.

  • Or a string of bytes. However it is not supposed to convert a long/short/float to a string of bytes.

Nick,

I'm not really sure about this but I think that using println will actually convert the long into a ansii char array. That means that your longs of four bytes could end up taking 10 bytes plus your delimeter. In some cases where bandwidth, performance and cpu load matter this might be a problem. E.g. driving a large number of RGB LED's over a serial.

Only simpler if you are a advanced programmer that doesn't fatigue easily (you'd have to type your one-liner on every occasion).

(not trying to be a smartass)

I am of course referring to this and not to the long to byte array conversion:

inline size_t write(unsigned long n) { return write((byte*)&n, sizeof(n)); }
inline size_t write(unsigned int n) { return write((byte*)&n, sizeof(n)); }

;D

This is something to think about, that wasn't really clear to me. I genuinely expected that the print class was for dealing with strings and I made the assumption that people don't always want to send strings. Thanks for all the infos

Fexduino:
I'm not really sure about this but I think that using println will actually convert the long into a ansii char array. That means that your longs of four bytes could end up taking 10 bytes plus your delimeter. In some cases where bandwidth, performance and cpu load matter this might be a problem. E.g. driving a large number of RGB LED's over a serial.

True, but that is probably an edge case. For example with a 4-digit number it would be the same. Plus with "binary" data you have the issue of sychronization. You lose a single byte and suddenly everything after makes no sense.

I genuinely expected that the print class was for dealing with strings and I made the assumption that people don't always want to send strings.

Most of the "outputting" classes expect you to use the virtual function "write" because all you have to do is re-implement that and you get all the benefits of the Print class. For example, I outputted to an LCD by just overriding that one function.

Here's an example of the fancy things you can do:

class slowPrint : public Print
  {
  public:
  virtual size_t write (byte x)
    {
    Serial.write (x);
    delay (200);
    return 1;
    }
  };
    
slowPrint bar;

void setup ()
  {
  Serial.begin (115200);
  bar.println ();
  unsigned long foo = 4294967295;
  bar.println (foo);
  bar.println (42.34);
  }  // end of setup

void loop () { }

I have derived a class from Print and only replaced the virtual function write(). This does a write of a byte with a 200 mS delay afterwards. Since it is derived from Print I can still print longs and floats, with the added delay.

I used that in some code where I wanted to "print" to the Leonardo's keyboard, but more slowly because characters were being dropped.