SoftwareSerial.read() returning value with all the bits set

I'm trying to get a sketch going using SoftwareSerial on an R4 Arduino Uno wifi.

SoftwareSerial.read() is supposed to read a byte, or -1 if no data is available. The return type of read() is an int, which on this board is an 4-byte quantity, so if no data is available it should return 0xFFFFFFFF.

What I'm finding is that occasionally read() returns 0xFFFFFFdd, where dd is the value of the byte I'm receiving. If I either AND that value with 0xFF, or cast the returned int into a uint8_t, a one-byte quantity, my program works fine.

Looking at the code for SoftwareSerial, in the class there is a member ringbuf, which is a ring buffer of type char. This is the code for read():

int SoftwareSerial::read()
{
    int chr = -1;
    if (!rx_descr.ringbuf.empty()) {
        chr = rx_descr.ringbuf.get();
    }
    return chr;
}

ringbuf.get() should return a one-byte quantity, a char, which gets cast to an int. Somehow the upper three bytes of that int are getting set to F.

Just to check, I did Serial.print(sizeof(char)), which gives 1, as expected.

Any thoughts what's going on?

A link to the SoftwareSerial::read() for the Arduino UNO R4 is here.

The ringbuffer is a template for any type. SoftwareSerial makes a "char" buffer with it here:

::RingBuffer<char> ringbuf;

When a byte is received, it is put as a uint8_t in the buffer here:

uint8_t data = 0;
...
rx_descr.ringbuf.put(data);

Suppose a 0xDD or a UTF-8 character is received, then the 'uint8_t' is put into a 'char' buffer. That's wrong. It does not come out in the same way when the 'char' is extended to a 'int'.
Using a signed character for 7-bit serial data is something from the 1970s.

Solution: Make it a uint8_t ringbuffer: ::RingBuffer<uint8_t> ringbuf;

Can you make an Issue on Github ?

Thanks. Here I am thinking there's something weird going on and actually the code is doing exactly what it says it should do. It's casting the read byte into a signed one-byte quantity, and then into a signed int. So any byte with the high bit set is going to get cast into a signed int with
all the high bits set.
Changing the definition in SoftwareSerial.h of ringbuf from:
::RingBuffer<uint8_t> ringbuf;
to
` ::RingBuffer<uint8_t> ringbuf;

makes it work.

It's kind of a dumb bug.

Do you have a link to the Github page for it? I can't find it in some cursory googling.

Never mind, you provided it in the response.

If you give a link to your Github Issue, then others can read it. You can also put a link in the Github Issue to this forum topic.
Your Issue about SoftwareSerial: https://github.com/arduino/ArduinoCore-renesas/issues/243

This is a classic legacy C gotcha, char can be signed or unsigned. If you are playing the drinking game, take a shot.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.