C++ 23 on an ESP32 with PlatformIO
How to get C++ 23 working on an ESP32 with the Arduino framework and PlatformIO.
There can be many reasons why you would want to use the latest C++ standard in any project.
Maybe you want to use the latest features, or you want to ensure that your code is future-proof.
When I first learned about the new C++ 20 and C++ 23 features, I was immediately excited to try them on embedded projects. Especially the extended constexpr support caught my attention because I could see many use cases for precomputing data at compile time, especially on memory and speed-constrained devices like the ESP32.
TL:DR
Anyway, if you want to skip the details and just want to get C++ 23 working on your ESP32 with the Arduino framework and PlatformIO, here is the platformio.ini configuration you need:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[env:esp32dev]
platform = espressif32 
; adjust to your board
board = esp32dev 
framework = arduino
; choose the latest espressif compiler toolchain
; as of october 2025, this is version 12.2.0+20230208
platform_packages = 
    espressif/toolchain-xtensa-esp32@12.2.0+20230208
; disable default C++ 11/17
build_unflags = 
    -std=gnu++11
    -std=gnu++17
; enable C++ 23
build_flags = 
    -std=gnu++23
If you are VSCode along with the PlatformIO extension, updating the platformio.ini file should reconfigure the project automatically, e.g. make IntelliSense (auto-completion) aware of the new C++ standard.
If it doesn’t work automatically, make sure you have the C/C++ extension installed and reload the VSCode window.
This is what the .vscode/c_cpp_properties.json should roughly look like for a PlatformIO project that has been configured for C++ 23:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
    "configurations": [
        {
            "name": "PlatformIO",
            "includePath":[ /* loads of paths*/],
            "browse": {/* also loads of paths */},
            "defines": [...],
            "cStandard": "gnu99",
            "cppStandard": "gnu++23",
            "compilerPath": "C:/Users/MindStudio/.platformio/packages/toolchain-xtensa-esp32/bin/xtensa-esp32-elf-gcc.exe",
            "compilerArgs": [...]
        }
    ],
    "version": 4
}
Note, the important thing here is the "cppStandard": "gnu++23" entry, which should be set automatically by PlatformIO when you change the platformio.ini file. If it doesn’t match what you have in your platformio.ini, there is either something wrong with your PlatformIO installation, or the platformio.ini file might have a syntax error that prevents PlatformIO from parsing it correctly.
You can also change the C standard which can be useful if you want to mix C and C++ code in your project. The default is gnu99, but you can change it to gnu11 or gnu18 if you want to use a newer C standard.
Why use C++ 23 on an ESP32?
As mentioned earlier, there are many reasons why you would want to use the latest C++ standard in your projects. Some of the new features that caught my attention are:
- constexprenhancements: C++ 23 has extended the capabilities of- constexpr, allowing more complex computations to be performed at compile time. This is particularly useful for embedded systems where runtime performance and memory usage are critical. I for example used it to initialize a large lookup table at compile time, which improved the startup time because the data didn’t have to be computed at runtime and obviously saves memory space because only the final relevant data is stored in flash memory.
- std::span: This is part of the C++ 20 standard library, but it’s worth mentioning here because it provides a safer and more convenient way to handle arrays and buffers of different underlying types. It allows you to pass around views of contiguous sequences of elements without worrying about ownership or lifetime issues, which is very useful in embedded programming where you often deal with raw buffers.
- std::chrono: The chrono library is not a new C++ 20/23 feature, but it might not be contained in older releases of the espressif toolchain. It provides a type-safe way to handle time durations and points in time, which can be very useful for timing operations, delays, and scheduling tasks in embedded systems.
Note: Using the
<chrono>header on an ESP32 with C++23 might break. I noticed that the built-in clocks likestd::chrono::system_clockorstd::chrono::steady_clockare not implemented correctly in the espressif toolchain as of October 2025. But luckily, I found a simple workaround:
This simple custom chrono-compatible clock uses the
esp_timer_get_time()function from the Arduino/ESP32 core library to provide a clock that works correctly on the ESP32. You can then useesp_clock::now()to get the current time point and use it with other<chrono>features like chrono duration literals.I have used this clock in several projects now, and it works perfectly fine.
- std::format: This is a new feature in C++ 20 that provides a type-safe and efficient way to format strings. It can be very useful for logging and debugging in embedded systems, where you often need to format messages with variable data. It is basically a modern replacement for- sprintfand similar functions, which are not type-safe and can lead to memory corruption if used incorrectly.
- std::ranges,- std::views: These are also part of C++ 20 and was extended in C++ 23. They provide a powerful and expressive way to work with collections of data, allowing you to create pipelines of operations that can be applied to ranges of elements. This can lead to more readable and maintainable code, which is always a good thing, especially in embedded systems where code complexity can quickly grow.
- std::optional,- std::expected: I found myself trying different error handling methods over the years, but I’ve never really been satisfied with any of them. Especially exceptions are a controversial topic in the C++ community, and many embedded systems do not support them at all.- std::optionaland- std::expectedprovide a way to represent values that may or may not be present, or operations that may succeed or fail, without using exceptions. This can lead to more robust and predictable code, which is especially important in embedded systems where reliability is critical.
Getting C++ 23 working on an ESP32 with PlatformIO
Getting C++ 23 working on an ESP32 with the Arduino framework and PlatformIO is not as difficult as you might think. The main thing I had to realize was, that C++ basically has two parts to it: the compiler and the standard library. You can use a more recent compiler with an older version of the standard library and vendors like espressif might not always ship the whole standard library as described in the C++ standard with their latest compiler toolchain.
By default, PlatformIO uses the espressif toolchain that comes with the ESP32 Arduino core. This one uses C++11 by default but can be set to use C++17. A Compiler that supports the full C++20/23 is not included by default. To use either C++20 or C++23, I needed to find a toolchain that has a recent enough version of the GCC compiler and also includes the C++ standard library features I wanted to use.
PlatformIO makes it easy to change the toolchain by using the platformio package registry at registry.platformio.org. It doesn’t only host libraries, but also toolchains and other packages that can be used in PlatformIO projects.
I found this official package which contains the espressif toolchain: espressif/toolchain-xtensa-esp32.
Looking at the available versions, I noticed that the latest version as of October 2025 is 12.2.0+20230208, which is relatively old, but includes GCC 12.2.0. In the CXX-Status page of GCC you can see which GCC versions support which C++ standard/standard library features. According to this page, GCC 12.x.x supports all but a few of the C++20 features and most of the C++23 features, at least the ones I care about.
So by specifying platform_packages = espressif/toolchain-xtensa-esp32@12.2.0+20230208 in the platformio.ini file, I was able to use this toolchain in my project.
Now I had to pass some flags to the compiler to tell it to use C++23 instead of the default C++11/17. This is done by adding build_flags = -std=gnu++23 to the platformio.ini file. If you just try this, you might notice error messages about conflicting C++ standard versions. This is because the Arduino framework (and some other frameworks) add their own flags to the compiler command line, which in this case is -std=gnu++11 or -std=gnu++17. To override these flags, you need to remove them first by adding build_unflags = -std=gnu++11 -std=gnu++17 to the platformio.ini file.
Now you should be good to go and can use C++23 features in your ESP32 Arduino projects with PlatformIO.