Configuration & Platforms¶
SignalCore needs almost no configuration. The defaults adapt to the target board on their own. The defines below exist for the cases where you want to override that behavior, or understand what the library chose for you.
Build-time defines¶
Set any of these before #include <SignalCore.h> to change the library's
behavior.
| Macro | Default | What it controls |
|---|---|---|
SIGNALCORE_FORCE_CMSIS |
(off) | On a non-Teensy ARM core, switch to the CMSIS-DSP FFT backend. The switch is unconditional — it does not detect support. If the core does not ship arm_math.h the build stops with a clear #error; if the core ships the header but does not link the DSP objects, expect undefined reference to arm_rfft_fast_* linker errors. Ignored on non-ARM boards. |
SIGNALCORE_MAX_FFT_SIZE |
256 (AVR) · 1024 (ESP8266) · 4096 (other) | The largest FFT size the build will accept. The FFT<> template checks its size against this with a static_assert, so an oversized transform fails to compile instead of exhausting RAM at run time. Define it before the include to raise the cap on a larger AVR part (or lower it to guard a tight build). |
The library also defines three mutually exclusive backend-selection macros that
you are not expected to set: SIGNALCORE_USE_CMSIS, SIGNALCORE_USE_ESP_DSP
(reserved for a future ESP-DSP wrapper), and SIGNALCORE_USE_GENERIC.
Platform.h sets exactly one of them based on the target board. A sketch can
test them to report which path is active:
#if defined(SIGNALCORE_USE_CMSIS)
Serial.println("FFT backend: CMSIS-DSP");
#else
Serial.println("FFT backend: generic C++");
#endif
Supported platforms¶
| Board family | Backend | Notes |
|---|---|---|
| Teensy 4.x | CMSIS-DSP | Auto-selected via TEENSYDUINO |
| Other ARM (STM32, Giga, SAMD51) | Generic C++ | Define SIGNALCORE_FORCE_CMSIS to opt in to CMSIS if the core links it |
| ESP32 / ESP32-S3 | Generic C++ | ESP-DSP wrapper reserved for future use |
| RP2040 / RP2350 | Generic C++ | |
| SAMD21 | Generic C++ | |
| AVR (UNO, Nano, Pro Micro) | Generic C++ | FFT capped at 256 points by SIGNALCORE_MAX_FFT_SIZE; ATmega328 boards (2 KB SRAM) fit FFT<128> at most in practice |
| ESP8266 | Generic C++ | FFT capped at 1024 points |
| Any Arduino-compatible board with a C++11 compiler | Generic C++ |
The generic backend produces the same output as the CMSIS backend, bin for bin
(it matches the arm_rfft_fast_f32 packing), so spectrum-analysis sketches
behave the same across all of these boards.
Memory footprint¶
FFT<N> reserves 2·N + N/2 floats statically. That memory is fixed at compile
time and never comes from the heap. The three pieces are the input samples (N
floats), the result buffer (N floats), and the half-window coefficients (N/2
floats):
| FFT size | Static RAM |
|---|---|
| 256 | ~2.5 KB |
| 512 | ~5 KB |
| 1024 | ~10 KB |
| 2048 | ~20 KB |
| 4096 | ~40 KB |
At a size of 1024 that works out to 1024 + 1024 + 512 = 2560 floats, or 10 KB.
On AVR the compile-time static_assert blocks any size above
SIGNALCORE_MAX_FFT_SIZE (256 by default). Note that the 256 cap targets
larger AVR parts such as the Mega 2560 (8 KB of SRAM): per the table above,
FFT<256> alone needs ~2.5 KB, more than an ATmega328's entire 2 KB — on an
UNO R3 or Nano the practical ceiling is FFT<128> (~1.3 KB plus your own
sample buffer).
PeakHoldProcessor and ExponentialMovingAverage size their buffers when
constructed, or when you call init(), and they take that memory from the heap
rather than reserving it statically. On a RAM-tight board such as an AVR, create
them once at the size you need early in setup(), rather than resizing them in
the loop. Repeated resizing leaves small unusable gaps in the heap, a problem
called fragmentation, that can eventually starve later allocations.
Created by VoidLoop · Founded by Gregory Kovacs · Written by Zachariah Magee