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.
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;
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;