Skip to content

Reference: clarify Serial.setRxBufferSize description #7484

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
5 of 6 tasks
wolfbert opened this issue Jul 24, 2020 · 9 comments · Fixed by #7521
Closed
5 of 6 tasks

Reference: clarify Serial.setRxBufferSize description #7484

wolfbert opened this issue Jul 24, 2020 · 9 comments · Fixed by #7521
Assignees

Comments

@wolfbert
Copy link

wolfbert commented Jul 24, 2020

Basic Infos

  • This issue complies with the issue POLICY doc.
  • I have read the documentation at readthedocs and the issue is not addressed there.
  • I have tested that the issue is present in current master branch (aka latest git).
  • I have searched the issue tracker for a similar issue.
  • If there is a stack dump, I have decoded it.
  • I have filled out all fields below.

Platform

  • Hardware: ESP8266
  • Core Version: 2.7.2
  • Development Env: Arduino IDE
  • Operating System: Windows

Problem Description

https://arduino-esp8266.readthedocs.io/en/latest/reference.html#serial

Serial has additional 256-byte TX and RX buffers.

Looking at uart.cpp, I can only find support for the RX buffer.

The method Serial.setRxBufferSize(size_t size) allows to define the receiving buffer depth. The default value is 256.

While this reflects the code, it is by no means the whole story. The RX buffer shadows the hardware FIFO. If the RX buffer size is set to 0, UART initialization will fail. After every 16 (or more) received (unread) characters, FIFO content will be moved to the RX buffer which wraps around. So you can buffer at most RX buffer size + 15 (possibly up to 128) characters. Therefore, any change in the buffer size must be carefully considered. In case the buffer is resized smaller after UART initialization, unread data may get truncated (and the overrun flag remains unchanged).

As an aside, if the 128 byte hardware FIFO buffer is sufficient for the application, the RX buffer wouldn't be needed (but that requires a change in the code, i.e. don't allocate RX buffer unless requested size > 128).

Edit: fixed link to documentation.

@d-a-v
Copy link
Collaborator

d-a-v commented Jul 24, 2020

To summarize, do you wish to clarify doc with the following statements ?

  • no SW tx buffer (edited)
  • mandatory 256 bytes by default software RX buffer on top of 128 bytes hw buffer

@wolfbert
Copy link
Author

wolfbert commented Jul 24, 2020

Let me suggest:

"Serial object works much the same way as on a regular Arduino. Apart from hardware FIFO (128 bytes for TX and RX) Serial has additional 256-byte TX and RX buffers a customizable RX buffer (default size 256 bytes) in RAM. Both transmit and receive is interrupt-driven. Write and read functions only block the sketch execution when the respective FIFO/buffers are full/empty. Note that the length of additional 256-bit buffer can be customized."

"The method Serial.setRxBufferSize(size_t size) allows to define the receiving buffer depth. It must be at least the size of the largest block of data you may need to buffer before reading. The default value size is 256. For transmit-only operation, the buffer can be switched off by passing mode SERIAL_TX_ONLY to Serial.begin()."

Edit: rephrased minimum size.

@Tech-TX
Copy link
Contributor

Tech-TX commented Jul 24, 2020

There is indeed some sort of TX buffer; I saw it in action when I was playing with the Low Power Demo in December/January. I don't know if it's managed by the core or SDK, nor how large it is. I had to do Serial.flush() or the CPU wouldn't sleep in a couple of places. Backtrack the flush should show you the buffer. It may be a hardware buffer in the UART.

@wolfbert
Copy link
Author

wolfbert commented Jul 24, 2020

@Tech-TX

There is indeed some sort of TX buffer; [...] Backtrack the flush should show you the buffer. It may be a hardware buffer in the UART.

It is indeed the hardware buffer.

Edit: removed incorrect comment about flush().

@TD-er
Copy link
Contributor

TD-er commented Jul 26, 2020

I always thought (not sure where I got it from) the hardware buffer was only 128 bytes in size, both for reading and writing.

@d-a-v d-a-v self-assigned this Jul 26, 2020
@wolfbert
Copy link
Author

@TD-er
It is, 128 bytes each for input and output, for each UART.

According to Esspressifs Technical Reference, Chapter 11, page 79:

Both UART0 and UART1 have a length of 128 Byte hardware, read and write FIFO operations are at the same address.

In the rest of the chapter, receive and transmit buffers are described separately (e.g. buffer full and empty interrupt triggers can be set in the range of 0-127). Also, issues #1683 and #2237 refer to the implementation of the serial buffer for ESP8266.

@d-a-v
Copy link
Collaborator

d-a-v commented Aug 12, 2020

@wolfbert here's an update:

"Serial object works much the same way as on a regular Arduino. Apart from hardware FIFO (128 bytes for TX and RX) Serial has a customizable RX buffer (default size 256 bytes, can be customized) in RAM. Both transmit and receive is interrupt-driven. ::write() and ::readBytes() functions only block the sketch execution when the respective FIFO/buffers are full/empty, but ::read(buffer, size) does not block and returns the number of bytes read.
::flush() will block until all output bytes are effectively sent and has no effect on input buffer."

"The method Serial.setRxBufferSize(size_t size) allows to define the receiving buffer depth. It must be at least the size of the largest block of data you may need to buffer before reading. The default value size is 256. For transmit-only operation, the buffer can be switched off by passing mode SERIAL_TX_ONLY to Serial.begin(). Other modes are SERIAL_RX_ONLY and SERIAL_FULL (the default). Default configuration mode is SERIAL_8N1. Possibilities are SERIAL_[5678][NEO][12]."

@wolfbert
Copy link
Author

@d-a-v
Thanks, fine with me, just a minor copy issue:

The default value size is 256.

Note that the two paragraphs are separated by a number of lines in the documentation.

@devyte
Copy link
Collaborator

devyte commented Aug 12, 2020

Both transmit and receive is interrupt-driven

Transmit is not interrupt driven, it is blocking due to polling busy-wait, ref here.

The Arduino AVR reference seems to be interrupt-driven, ref there.

Rx is interrupt-driven, i.e.: there is a sw buffer that has a default size of 256 bytes, that gets filled when there are bytes incoming. If I remember correctly, the ISR is triggered under 2 conditions:

  • there are less bytes than the threshold sitting in the FIFO, and have been sitting there for more than some timeout
  • there are more bytes than the threshold in the FIFO
    The ISR is in charge of reading from the FIFO into the sw buffer.

So that part is wrong in both current and proposed doc: Only Rx is interrupt driven.

Serial object works much the same way as on a regular Arduino

However, both are blocking, so the behavior from the user's POV is pretty much the same as for Arduino, i.e.: it makes no difference that our implementation for Tx isn't interrupt driven.

::write() and ::readBytes() functions only block the sketch execution when the respective FIFO/buffers are full/empty, but ::read(buffer, size) does not block and returns the number of bytes read.

Transmit, i.e.: write(), blocks when the Tx hw FIFO is full.
Receive:

  • readBytes(), blocks until the number of bytes received complies with the number requested.
  • read() is not blocking

Serial has a customizable RX buffer (default size 256 bytes, can be customized) in RAM

To reduce redundancy, I suggest along the lines of:
"Serial has a software RX buffer in RAM. The default size 256 bytes, but it can be customized as needed."

Possibilities are SERIAL_[5678][NEO][12]

I suggest adding an example, e.g.:
"For example, SERIAL_8N1 means 8 No parity 1 end bit."

devyte added a commit that referenced this issue Aug 12, 2020
devyte added a commit that referenced this issue Aug 13, 2020
Fixes #7484

Clarify blocking case for write()

Add flush() method.

Missing ), clarifications
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants