Skip to content

Feature request: CPP Templetable Per-HardwareSerial-instance RX/TX buffer sizes via compile-time defines #374

@DjordjeMandic

Description

@DjordjeMandic

Currently, AVR HardwareSerial buffer sizes are controlled via global compile-time macros such as:

-D SERIAL_RX_BUFFER_SIZE=XX
-D SERIAL_TX_BUFFER_SIZE=XX

These settings apply uniformly to all HardwareSerial instances (e.g. Serial, Serial1, etc.), since the buffer sizes are defined globally in the core implementation.

Problem

This global configuration model prevents optimizing memory usage across multiple UARTs with different requirements:

  • All serial ports must use the same RX/TX buffer size
  • Memory cannot be tailored per UART instance
  • Leads to either wasted SRAM or under-provisioned buffers depending on the chosen global value

Proposed Improvement

Introduce per-instance compile-time buffer size overrides for HardwareSerial, allowing different serial ports to use different buffer sizes via build flags or defines.

Example Usage

-D SERIAL_RX_BUFFER_SIZE=16
-D SERIAL_TX_BUFFER_SIZE=16

-D SERIAL1_RX_BUFFER_SIZE=255
-D SERIAL1_TX_BUFFER_SIZE=255

or alternatively per instance:

-D SERIAL_RX_BUFFER_SIZE=16
-D SERIAL1_RX_BUFFER_SIZE=255

Expected Behavior

  • If SERIALx_RX_BUFFER_SIZE is defined, it overrides the global SERIAL_RX_BUFFER_SIZE for that instance.
  • If not defined, fallback to global default.
  • Existing behavior remains unchanged (backward compatible).

Benefits

  • Fine-grained SRAM optimization per UART instance
  • Better support for mixed workloads (debug vs streaming)
  • No runtime overhead (compile-time configuration only)
  • Fully compatible with embedded AVR constraints

Compatibility

  • Fully backward compatible if no per-instance defines are provided
  • Existing builds using only SERIAL_RX_BUFFER_SIZE continue to behave identically
  • No API changes required in sketches, only core build system changes

Implementation Suggestion

In the core implementation, buffer sizing could be resolved via conditional macros:

#ifndef SERIAL1_RX_BUFFER_SIZE
  #define SERIAL1_RX_BUFFER_SIZE SERIAL_RX_BUFFER_SIZE
#endif

or via per-instance header logic tied to HardwareSerial instantiation.

ChatGPT gave me this suggestion:

Core idea

Make HardwareSerial a template where RX/TX buffer sizes are compile-time parameters.


Implementation

template<uint16_t RX_BUFFER_SIZE, uint16_t TX_BUFFER_SIZE>
class HardwareSerial : public Stream
{
  protected:
    volatile uint8_t * const _ubrrh;
    volatile uint8_t * const _ubrrl;
    volatile uint8_t * const _ucsra;
    volatile uint8_t * const _ucsrb;
    volatile uint8_t * const _ucsrc;
    volatile uint8_t * const _udr;

    bool _written;

    volatile rx_buffer_index_t _rx_buffer_head;
    volatile rx_buffer_index_t _rx_buffer_tail;
    volatile tx_buffer_index_t _tx_buffer_head;
    volatile tx_buffer_index_t _tx_buffer_tail;

    unsigned char _rx_buffer[RX_BUFFER_SIZE];
    unsigned char _tx_buffer[TX_BUFFER_SIZE];

  public:
    inline HardwareSerial(
      volatile uint8_t *ubrrh, volatile uint8_t *ubrrl,
      volatile uint8_t *ucsra, volatile uint8_t *ucsrb,
      volatile uint8_t *ucsrc, volatile uint8_t *udr);

    void begin(unsigned long baud) { begin(baud, SERIAL_8N1); }
    void begin(unsigned long, uint8_t);
    void end();

    int available(void);
    int peek(void);
    int read(void);
    int availableForWrite(void);
    void flush(void);
    size_t write(uint8_t);

    using Print::write;
    operator bool() { return true; }

    inline void _rx_complete_irq(void);
    void _tx_udr_empty_irq(void);
};

Default compatibility typedef

#ifndef SERIAL_RX_BUFFER_SIZE
  #define SERIAL_RX_BUFFER_SIZE 64
#endif

#ifndef SERIAL_TX_BUFFER_SIZE
  #define SERIAL_TX_BUFFER_SIZE 64
#endif

using HardwareSerial = HardwareSerial<SERIAL_RX_BUFFER_SIZE, SERIAL_TX_BUFFER_SIZE>;

Example usage

HardwareSerial<SERIAL0_RX_BUFFER_SIZE, SERIAL0_RX_BUFFER_SIZE> Serial;
HardwareSerial<SERIAL1_RX_BUFFER_SIZE, SERIAL1_RX_BUFFER_SIZE> Serial1;

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions