Char is not signed - no reference in the documentation

Hello,

I have detected a problem with Serial.read(). It behaves differently with an Arduino Nano ESP32 and IDE 2.3.2 than I am used to. An ATmega2560 and IDE 2.3.2 behaves as usual. Likewise ATmega2560 and IDE 1.8.19.

When I read with Serial.read(), I get question marks in the serial monitor. Normally, if no data comes in, a -1 should be returned and my if() query should be skipped. Only when I also use Serial.available() does everything work correctly. Line 27 and 39.

Does anyone have an explanation for this?

Additional question. Why do I not see the START output in setup() in the serial monitor with Arduino Nano ESP32?

Stream &stream {Serial};

constexpr uint8_t BUFFER_SIZE {5};

struct ReceiveData {
  char buffer [BUFFER_SIZE + 1]; // + Null-Terminator
  size_t index {0};
};
ReceiveData rx;

void setup()
{
  Serial.begin(115200);
  stream.println("\nSTART\n");
}

void loop()
{
  if (readSerial(rx, BUFFER_SIZE)) {
    //
  } 
}

bool readSerial (ReceiveData &data, const uint8_t LENGTH)
{
  bool status {false}; 
  //if (stream.available()) {
    char c = stream.read();
   
    if (c == '\r' || c == '\n') {             // if CR or LF is read in,
      data.buffer[ data.index] = '\0';        // then termination
      data.index = 0;
      status = true;                          // character completely read in
    }
    else if ( (c >= 32) && ( data.index < LENGTH) ) { // if there is still space in the buffer, 
      data.buffer[data.index++] = c;                  // save characters and increment index
      stream.print(F("read c: ")); stream.println(c); 
    }
  //}
  return status;
}

a

The solution is, of course, to do it properly and use Serial.available() rather than relying on receiving -1 if no data is available

Hi,

That is too simple. That would be a workaround and does not eliminate the problem. :wink:

My question would be why does read() behave differently? available() gives me the number of characters in the buffer. I do not need this information. Almost nobody needs it. I must be able to rely on the fact that read() returns a clean -1 if there are no characters. The question is, why is no -1 returned?

using C++ is too simple.
using computer is too simple.
using electricity is too simple, smoke signals is something for you

Why is it a workaround ?
What do you think the available() function is for if not to establish whether data is available ?

You need to understand the need of the Serial.available() function.

Assume Bd = 960, frame length = 10, No line ending option. One character (one frame) takes about 42 us ((1/9600)*10) time to arrive From Serial Monitor to Nano ESP32.

When a character arrives at the UART Port of NANO, the NANO is interrupted and the 8-bit character code (START and STOP bits are discarded) is automatically stored in a FIFO type Buffer.

Now, if you tell the MCU via your Serial.read() function to read a character from that FIFO Buffer, most probably, the function will not find a character in the Buffer as the character has not yet arrived because of 42 us transmission delay. That is what has happened in your case.

So, in order to be sure that Serial.read() function does not fail, one should check first that at least one charater has arrived/stored in the Buffer and then issue the read() method. The Serial.available() function does check the presence of at least one charcater in the Buffer.

void loop()
{
     byte n = Serial.available();   //check that at least one character is in Buffer
     if(n != 0)
     {
           char ch = Serial.read();
           Serial.println(ch);
     }
}

That's the point. read() reads from the buffer. If the buffer is empty, I expect -1. I don't see a problem with your 42µs. As long as the FIFO does not overflow, nothing bad should happen.

char is unsigned on ESP32. Use int instead.

Thank you. I would never have thought of that.
Do you perhaps know why char has set ESP to unsigned? It's a bit strange.

Test okay. :+1:

Stream &stream {Serial};

constexpr uint8_t BUFFER_SIZE {5};

struct ReceiveData {
  char buffer [BUFFER_SIZE + 1]; // + Null-Terminator
  size_t index {0};
};
ReceiveData rx;

void setup()
{
  Serial.begin(115200);
  stream.println("\nSTART\n");
}

void loop()
{
  if (readSerial(rx, BUFFER_SIZE)) {
    //
  } 
}

bool readSerial (ReceiveData &data, const uint8_t LENGTH)
{
  bool status {false}; 
  //if (stream.available()) {
    int c = stream.read();
   
    if (c == '\r' || c == '\n') {             // if CR or LF is read in,
      data.buffer[ data.index] = '\0';        // then termination
      data.index = 0;
      status = true;                          // character completely read in
    }
    else if ( (c >= 32) && ( data.index < LENGTH) ) { // if there is still space in the buffer, 
      data.buffer[data.index++] = static_cast<char>(c);                  // save characters and increment index
      stream.print(F("read c: ")); stream.println(static_cast<char>(c)); // Debug
    }
  //}
  return status;
}

Hi,

Somehow there is a problem. When I read the preferences, char should actually be int8_t and therefore signed.

Screenshot 2024-09-01 at 16.41.38

The definition of char in the Preferences Library is different from that in the core.

I have looked all over the ESP idf and the esp arduino core, and can not find the fundamental type definition for char as uint8_t but it is indeed unsigned on the ESP8266 and ESP32.

My guess is that @oqibidipo knows where the type definition is to be found.

@Doc_Arduino I don't think it is so much whether it is signed or unsigned I think it is a matter of using the int data type to get the correct return value.

Hi,

Let's hold on for a moment.
I had assumed char as signed char because that's what I'm usually used to. I checked this with the type traits query, char is really unsigned :wink: The Cpp language standard defines signed char and unsigned char. char is not defined, it can be signed or unsigned. With another test I checked the value range, which is between 0 ... 255, so char practically corresponds to byte or uint8_t. Exactly as oqibidipo has written.
So far so good.
And now comes the ESP documentation.
preferences and tells me that char corresponds to an int8_t.
If there is a discrepancy between the core and the documentation, then there is either an error in the core or in the documentation. This will lead other people into problems, independent of what char is used for. You actually rely on the documentation and don't start checking the documentation every time.

I continue programming with int as the read() return type and cast to char. Only what char is really supposed to be needs to be clarified and corrected.

A signed data type is required for correct processing.

Why make things complicated when the available() function is there do what you need ?

Wait a minute I just remembered
Apparently that would be

Hi,

yes, good. I can also use available(). :wink:
However, I must again emphasize that the query is basically not necessary.
The real problem is with char. :thinking:
However, a problem was discovered with the value range of char which needs to be clarified. I think this is important to understand.

What problem ?

A char can have a value between 0 and 255 and it is used to represent a single character

I have to be clearer now. Why doesn't anyone read what I write and what the documentation says? What's so difficult to understand?
The ESP documentation says that char = int8_t.
Either the core or the documentation needs to be corrected. Depending on what the target data type should really be.

For ATmega/AVR char also corresponds to int8_t and matches with its documentation.

My aplogies

You are right on this issue but I still contend that you should use the available() function to do what it is there for