Skip to content

RingBuffer Moved Out of Core Into ring_buffer Helper Component

RingBuffer has moved from esphome/core/ring_buffer.h into a new ring_buffer helper component at esphome/components/ring_buffer/ring_buffer.h under the esphome::ring_buffer namespace. The old location is deprecated and will be removed in ESPHome 2026.11.0.

This is a breaking change for external components in ESPHome 2026.5.0 and later.

Background

PR #16298: Move core ring buffer to helper component

The core RingBuffer was an ESP32-only implementation used exclusively by audio components — audio, i2s_audio, micro_wake_word, mixer, resampler, sound_level, speaker, voice_assistant. Nothing outside that audio cluster touches it. Living in esphome/core/ meant:

  • The ring_buffer.h header was parsed for every build, regardless of platform or whether audio was in use. Linker stripping kept unused code out of the final binary, but build-time parsing and tooling overhead applied universally.
  • Changes to RingBuffer couldn't be staged in an external_components PR — they always required a core change.
  • The ESP32-only implementation lived alongside genuinely cross-platform core code.

Moving it into a real component (esphome::ring_buffer::RingBuffer) lets audio consumers AUTO_LOAD it the same way every other shared utility is pulled in, and opens the door to iterating on the implementation without touching core.

The old esphome/core/ring_buffer.h header now forwards to the new location and will be removed in 2026.11.0 per the standard 6-month policy. The behavior of the shim depends on whether your component declares AUTO_LOAD = ["ring_buffer"]:

  • With AUTO_LOAD: the shim forwards esphome::RingBuffer to esphome::ring_buffer::RingBuffer via a using alias marked ESPDEPRECATED(...). Code keeps compiling, but each use emits a deprecation warning at the call site.
  • Without AUTO_LOAD: the new header isn't on the include path, so the shim fires a hard #error telling you exactly what to do: "esphome/components/ring_buffer/ring_buffer.h not found. Add 'ring_buffer' to your component's AUTO_LOAD list to use esphome::ring_buffer::RingBuffer." The build fails immediately rather than producing a confusing missing-header diagnostic from somewhere else.

What's Changing

C++ include and namespace

// Before
#include "esphome/core/ring_buffer.h"

std::unique_ptr<esphome::RingBuffer> buf = esphome::RingBuffer::create(1024);
// After
#include "esphome/components/ring_buffer/ring_buffer.h"

std::unique_ptr<esphome::ring_buffer::RingBuffer> buf =
    esphome::ring_buffer::RingBuffer::create(1024);

Python codegen — declare AUTO_LOAD

Any component whose C++ uses RingBuffer must auto-load the helper component so the build system pulls in its sources. In your component's __init__.py:

AUTO_LOAD = ["ring_buffer"]

If your component already has an AUTO_LOAD, append "ring_buffer" to it.

Who This Affects

External audio / streaming components that use RingBuffer directly — typically:

  • Custom microphone / I²S audio pipelines
  • Speaker / playback components
  • Streaming protocol bridges (BLE audio, Nordic UART, etc.)
  • Forks or extensions of micro_wake_word, voice_assistant, speaker

All in-tree consumers (audio, i2s_audio, micro_wake_word, mixer, resampler, sound_level, speaker, voice_assistant) were migrated in esphome/esphome#16298.

Non-audio components are unaffected.

Migration Guide

  1. Update the include path:

    -#include "esphome/core/ring_buffer.h"
    +#include "esphome/components/ring_buffer/ring_buffer.h"
    
  2. Update the namespace at every callsite:

    -esphome::RingBuffer::create(size);
    +esphome::ring_buffer::RingBuffer::create(size);
    

    If you have many callsites, you can avoid editing each one by adding a using esphome::ring_buffer::RingBuffer; declaration inside your component's own namespace block so RingBuffer resolves correctly from unqualified references:

    namespace esphome::my_component {
    
    using esphome::ring_buffer::RingBuffer;   // ← here, not at file scope
    
    class MyComponent : public Component {
      std::unique_ptr<RingBuffer> buf_;        // unqualified still works
    };
    
    }  // namespace esphome::my_component
    

    Avoid putting the using at file scope (outside any namespace) — that pulls the name into the global namespace, which works for ::RingBuffer lookups but doesn't help code inside esphome::my_component that previously relied on esphome::RingBuffer being a sibling.

  3. Add AUTO_LOAD in your component's __init__.py:

    AUTO_LOAD = ["ring_buffer"]
    

The API of RingBuffer itself (create(), read(), write(), write_without_replacement(), available(), etc.) is unchanged — only its location and namespace move.

Timeline

  • 2026.5.0: New location active; old esphome/core/ring_buffer.h deprecated (compile warning).
  • 2026.11.0: Old header removed.

Finding Code That Needs Updates

# C++ — find the old include
grep -rn 'esphome/core/ring_buffer.h' your_component/

# C++ — find old namespace usage
grep -rn 'esphome::RingBuffer' your_component/

# Python — find AUTO_LOAD declarations that need ring_buffer added
grep -rn 'AUTO_LOAD' your_component/

Questions?

If you have questions about migrating your external component, please ask in:

  • PR #16298 — Move core ring buffer to helper component

Comments

Feel free to leave a comment here to discuss this post wth others. You can ask questions, share your experience, or suggest improvements. If you have a question about a specific feature or issue, please consider using the ESPHome Discord. Stick to English and follow ESPHome's code of conduct. These comments exist on a discussion on GitHub, so you can also comment there directly if you prefer.