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;
Currently, AVR HardwareSerial buffer sizes are controlled via global compile-time macros such as:
These settings apply uniformly to all
HardwareSerialinstances (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:
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
or alternatively per instance:
Expected Behavior
SERIALx_RX_BUFFER_SIZEis defined, it overrides the globalSERIAL_RX_BUFFER_SIZEfor that instance.Benefits
Compatibility
SERIAL_RX_BUFFER_SIZEcontinue to behave identicallyImplementation Suggestion
In the core implementation, buffer sizing could be resolved via conditional macros:
or via per-instance header logic tied to
HardwareSerialinstantiation.ChatGPT gave me this suggestion:
Core idea
Make
HardwareSeriala template where RX/TX buffer sizes are compile-time parameters.Implementation
Default compatibility typedef
Example usage