Serial.readBytesUntil() terminator char, signed vs unsigned

Hi.

The Serial.readBytesUntil() function would be perfect for my project. Unfortunately the devices I am dealing with use an uncommon terminator value, namely 0xfd. Unfortunately the readBytesUntil() function expects a char as terminator, but then proceeds to compare the latest received byte with type int .

0xfd received over the serial line and treated as int is 253.
0xfd received as char parameter in the function call is cast to a signed char value of 0xfffd or 0xfffffffd, depending on architecture.

Thus the compare always fails. This applies to all terminator chars with the high bit set, i.e. all character values from 0x80 to 0xff.

Is there any way to overcome this limitation, except patching the library?

No, I cannot change the terminator char, it's given by the manufacturer f the devices.

Thanks,
Ekki

But you don't have to use the Serial.readBytesUntil() function. You can write your own

if a byte is available then read it
if it is not the terminator then put the byte in the array and increment the array index variable
keep doing this until you receive the terminating character

Welcome to the forum.

The communication is with bytes, you should be able to detect 0xfd.
Can you show a small sketch ? Which Arduino board do you use ?

However, we don't like the Serial.readBytesUntil(). It waits for new data, it is unclear if the resulting text is zero-terminated.

That's why we write our own.

I have done this (reading single bytes, scanning for the terminator) since many years, but on other platforms. Now, starting with Arduino I found this nice readBytesUntil() function and wanted to use it.

Oh well, back to the old way.

Cheers,
Ekki

Maybe I did not make the problem clear enough.

Yes, the readBytesUntil() function reads bytes. Agreed.
But the terminator is handled as a signed char, not a byte.
Thus the comparison fails reliably 100% of the time if the terminator is in the rnage 0x80 to 0xff.

I justwanted to understand if this is a known shortcoming and if there is a workaround, other than writing your own :smiley:

Ok, I will write my own serial receive function, again :smile:

Cheers,
Ekki

Is the data that you are receiving of fixed or variable length ?

The unsigned version is only for the buffer, not for the terminator character. Ouch, I think that is a mistake: https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/Stream.h#L102
By using the 0xfd as a signed char, it should still work, I think.

I often want to check for a '\r', a '\n' or both or in reverse order or with a timeout. So I have to write my own code anyway.

Note: I deleted my previous post, because I only took a glance and made a few mistakes

1 Like

I second your view of ReadBytesUntil(), also due to the fact that it incorporates a timeout which per default is 1000 msec ... You have much more control if you collect the data by your own ...

1 Like

Serial.readBytesUntil() inherits from the Stream utility class.

Serial.readBytesUntil(character, buffer, length)

You could subclass the object and change the trigger character.
Subclasses, Inheritance, and Polymorphism (emory.edu)

Variable length, but fixed frame format. Length varies from 3 to 255 bytes payload.
Frame always starts with the same bytes, always ends with the terminator, protocol definition ensures that start/end bytes cannot occur in the data stream.

But nevermind, I have now written a small receive routine, as you and others suggested. I just thought that I could use a nice and shiny function which does exactly what I need... alas...

Thanks fpr your support.

Cheers,
Ekki

... usually more trouble than it is worth, IMO.

Ray

1 Like

Yeah, I think it's a mistake, or at least an omission in the documentation. Would have saved me some time, but I learned a lot :smiley:

Using the terminator 0xfd as signed char leads the compiler to expand this value to 0xfffd (or 0xfffffffd). It starts as an 8 bit negative and the sign get's extended when the cast happens. That's a quite common pitfall in C as I understand it. The real culprit is handling the two sides of the comparison with different types - int vs. signed char.

I tried most useful data types for this char, charn (which is signed by default), unsigned char, byte, int, all to no avail. The value gets casted to char (signed) and sign expanded...

Anyway, as I said to UKHeliBob - I already wrote my own receive handler, as you suggested.

Thanks for the help.

Cheers,
Ekki

One more reason to not use these functions :wink:

Good catch

Which is exactly what you ended up doing :grinning:

Good luck with your project going forward

Did you try

Serial.readBytesUntil('\xfd', buffer, length);

It might not make any difference....

It should not make any difference the type of the argument and casting is

It doesn't.

The deciding factor is the cast at entry to the function. No matter how I represent the value in the function call, it get's converted to a signed char, the sign gets expanded to int size.

Cheers,
Ekki

Good news! The bug was reported: a decade ago:

It's one of 215 open bug reports in ArduinoCore-avr

1 Like

It shows "How quick nothing happens"... :wink:

LOL... Ok then I don#t need to open a bug report.

Well, I don't blame anybody, that's how it is. I have benefited from open source hard and software all my life and know the procedures. Sometimes there are just more important things.

Cheers,
Ekki

1 Like