Skip to content

Commit 70022e0

Browse files
committed
ESPBufferManager integration
1 parent 09034f6 commit 70022e0

7 files changed

Lines changed: 125 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file.
88
- `examples/json_export` streams newline-delimited JSON via `toJson()` + ArduinoJson for telemetry pipelines.
99
- Optional average smoothing in `CpuUsageSample` via `smoothedAverage`, with configurable `smoothingMode` (`None`, `RollingMean`, `Ewma`), rolling window size, and EWMA alpha.
1010
- `getLastSmoothedAverage()` helper for quickly reading the latest trend/baseline value when smoothing is enabled.
11+
- `CpuMonitorConfig::usePSRAMBuffers` toggle to prefer PSRAM-backed internal history and callback container/snapshot storage through `ESPBufferManager`, with automatic fallback to normal heap.
1112

1213
### Changed
1314
- Removed the library-provided global `cpuMonitor`; create and manage your own `ESPCpuMonitor` instance (only one active monitor at a time) and updated examples/docs accordingly.

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ ESPCpuMonitor is a tiny C++17 helper that estimates per-core CPU usage on ESP32
1010
## Features
1111
- FreeRTOS idle-hook based sampling with esp_timer; configurable period and calibration window so you can balance responsiveness vs. noise.
1212
- Per-core and averaged CPU usage (%) with timestamps from `esp_timer_get_time`, plus a ring-buffer history (default 60 entries) for charts/debug.
13+
- Optional PSRAM-backed internal buffering via `usePSRAMBuffers` (history + callback storage/snapshots, best effort with safe fallback when PSRAM is unavailable).
1314
- Optional smoothing helpers for average CPU usage (`RollingMean` or `EWMA`) so you can track baseline/trend load without forcing smoothing globally (disabled by default, fixed-size buffer when enabled).
1415
- Thread-safe per-sample callbacks for logging, telemetry, or UI updates; swap `enablePerCore` off to collapse cores to an overall average.
1516
- Manual `sampleNow()` path for users who already have their own schedulers (set `sampleIntervalMs` to `0`).
@@ -40,6 +41,7 @@ void setup() {
4041
cfg.sampleIntervalMs = 1000; // 1s cadence
4142
cfg.calibrationSamples = 5; // treat first 5 periods as "100% idle" baseline
4243
cfg.historySize = 30;
44+
cfg.usePSRAMBuffers = true; // optional: prefer PSRAM for internal buffers (history + callback storage/snapshots)
4345
cfg.enableTemperature = true; // disable if your target lacks a temp sensor
4446
cfg.smoothingMode = CpuSmoothingMode::Ewma;
4547
cfg.smoothingAlpha = 0.25f; // smaller alpha = smoother trend
@@ -93,6 +95,7 @@ If temperature is enabled, `getLastTemperature(current, average)` returns the la
9395
- `sampleIntervalMs` (default `1000`) – esp_timer period in milliseconds; `0` skips timer creation.
9496
- `calibrationSamples` (default `5`) – number of windows to treat as 100% idle baseline.
9597
- `historySize` (default `60`) – depth of stored samples; set to `0` to disable.
98+
- `usePSRAMBuffers` (default `false`) – when `true`, CPU monitor prefers PSRAM for internal dynamic buffers (history and callback containers/snapshots) and falls back automatically to normal heap if PSRAM is unavailable.
9699
- `enablePerCore` (default `true`) – when `false`, every `perCore` entry is set to the averaged usage.
97100
- `enableTemperature` (default `true`) – enable/disable temperature sensor readings.
98101
- `smoothingMode` (default `None`) – optional smoothing strategy for `average`: `None`, `RollingMean`, or `Ewma`.

library.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
{
2828
"name": "ArduinoJson",
2929
"version": "^7.0.0"
30+
},
31+
{
32+
"name": "ESPBufferManager",
33+
"version": "https://github.com/ESPToolKit/esp-buffer-manager.git"
3034
}
3135
],
3236
"headers": ["ESPCpuMonitor.h"],

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ category=Data Processing
88
url=https://github.com/ESPToolKit/esp-cpuMonitor
99
repository=https://github.com/ESPToolKit/esp-cpuMonitor.git
1010
architectures=esp32
11-
depends=ArduinoJson@^7
11+
depends=ArduinoJson@^7,ESPBufferManager
1212
license=MIT

src/esp_cpu_monitor/cpu_monitor.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ void ESPCpuMonitor::resetState(const CpuMonitorConfig &cfg) {
6161
calibrationSamplesDone_ = 0;
6262
hasSample_ = false;
6363
calibrated_ = false;
64-
history_.clear();
64+
history_ = CpuMonitorDeque<CpuUsageSample>(CpuMonitorAllocator<CpuUsageSample>(config_.usePSRAMBuffers));
65+
callbacks_ = CpuMonitorVector<CpuSampleCallback>(CpuMonitorAllocator<CpuSampleCallback>(config_.usePSRAMBuffers));
6566
resetSmoothingState();
6667
resetTemperatureState();
6768
temperatureEnabled_ = false;
@@ -272,7 +273,7 @@ bool ESPCpuMonitor::sampleNow(CpuUsageSample &out) {
272273
return false;
273274
}
274275

275-
std::vector<CpuSampleCallback> callbacks;
276+
CpuMonitorVector<CpuSampleCallback> callbacks{CpuMonitorAllocator<CpuSampleCallback>(config_.usePSRAMBuffers)};
276277
bool ready = captureSample(out, callbacks);
277278
for (const auto &cb : callbacks) {
278279
if (cb) {
@@ -309,7 +310,7 @@ void ESPCpuMonitor::timerCallback(void *arg) {
309310
return;
310311
}
311312
CpuUsageSample sample{};
312-
std::vector<CpuSampleCallback> callbacks;
313+
CpuMonitorVector<CpuSampleCallback> callbacks{CpuMonitorAllocator<CpuSampleCallback>(self->config_.usePSRAMBuffers)};
313314
if (self->captureSample(sample, callbacks)) {
314315
for (const auto &cb : callbacks) {
315316
if (cb) {
@@ -319,7 +320,7 @@ void ESPCpuMonitor::timerCallback(void *arg) {
319320
}
320321
}
321322

322-
bool ESPCpuMonitor::captureSample(CpuUsageSample &out, std::vector<CpuSampleCallback> &callbacksCopy) {
323+
bool ESPCpuMonitor::captureSample(CpuUsageSample &out, CpuMonitorVector<CpuSampleCallback> &callbacksCopy) {
323324
bool ready = false;
324325
lock();
325326
ready = computeSampleLocked(out);

src/esp_cpu_monitor/cpu_monitor.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,12 @@
2121

2222
#include <array>
2323
#include <cstdint>
24-
#include <deque>
2524
#include <functional>
2625
#include <limits>
2726
#include <vector>
2827

28+
#include "cpu_monitor_allocator.h"
29+
2930
extern "C" {
3031
#include <freertos/FreeRTOS.h>
3132
#include <freertos/semphr.h>
@@ -92,6 +93,7 @@ struct CpuMonitorConfig {
9293
uint32_t sampleIntervalMs = 1000; // Periodic sampling interval (0 = manual only)
9394
uint32_t calibrationSamples = 5; // Baseline windows that represent 100% idle
9495
size_t historySize = 60; // Ring buffer depth (0 = disable history)
96+
bool usePSRAMBuffers = false; // Prefer PSRAM for internal dynamic buffers when available
9597
bool enablePerCore = true; // When false, only average is reported
9698
bool enableTemperature = true; // When false, skip temperature readings
9799
CpuSmoothingMode smoothingMode = CpuSmoothingMode::None; // Optional average smoothing
@@ -148,7 +150,7 @@ class ESPCpuMonitor {
148150

149151
void resetState(const CpuMonitorConfig &cfg);
150152
bool computeSampleLocked(CpuUsageSample &out);
151-
bool captureSample(CpuUsageSample &out, std::vector<CpuSampleCallback> &callbacksCopy);
153+
bool captureSample(CpuUsageSample &out, CpuMonitorVector<CpuSampleCallback> &callbacksCopy);
152154
bool initTemperatureSensor();
153155
void deinitTemperatureSensor();
154156
bool readTemperature(float &outC);
@@ -171,8 +173,8 @@ class ESPCpuMonitor {
171173
uint32_t calibrationSamplesDone_ = 0;
172174
esp_timer_handle_t timer_ = nullptr;
173175
mutable SemaphoreHandle_t mutex_ = nullptr;
174-
std::deque<CpuUsageSample> history_;
175-
std::vector<CpuSampleCallback> callbacks_;
176+
CpuMonitorDeque<CpuUsageSample> history_;
177+
CpuMonitorVector<CpuSampleCallback> callbacks_;
176178
std::array<float, ESPCM_MAX_SMOOTHING_WINDOW> smoothingWindowValues_{};
177179
uint8_t smoothingWindowSize_ = 1;
178180
uint8_t smoothingCount_ = 0;
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#pragma once
2+
3+
#if __has_include(<ESPBufferManager.h>)
4+
#include <ESPBufferManager.h>
5+
#define ESP_CPU_MONITOR_HAS_BUFFER_MANAGER 1
6+
#elif __has_include(<esp_buffer_manager/buffer_manager.h>)
7+
#include <esp_buffer_manager/buffer_manager.h>
8+
#define ESP_CPU_MONITOR_HAS_BUFFER_MANAGER 1
9+
#else
10+
#define ESP_CPU_MONITOR_HAS_BUFFER_MANAGER 0
11+
#endif
12+
13+
#include <cstddef>
14+
#include <cstdlib>
15+
#include <deque>
16+
#include <limits>
17+
#include <new>
18+
#include <vector>
19+
20+
namespace cpu_monitor_allocator_detail {
21+
inline void *allocate(std::size_t bytes, bool usePSRAMBuffers) noexcept {
22+
#if ESP_CPU_MONITOR_HAS_BUFFER_MANAGER
23+
return ESPBufferManager::allocate(bytes, usePSRAMBuffers);
24+
#else
25+
(void)usePSRAMBuffers;
26+
return std::malloc(bytes);
27+
#endif
28+
}
29+
30+
inline void deallocate(void *ptr) noexcept {
31+
#if ESP_CPU_MONITOR_HAS_BUFFER_MANAGER
32+
ESPBufferManager::deallocate(ptr);
33+
#else
34+
std::free(ptr);
35+
#endif
36+
}
37+
} // namespace cpu_monitor_allocator_detail
38+
39+
template <typename T>
40+
class CpuMonitorAllocator {
41+
public:
42+
using value_type = T;
43+
using propagate_on_container_copy_assignment = std::true_type;
44+
using propagate_on_container_move_assignment = std::true_type;
45+
using propagate_on_container_swap = std::true_type;
46+
47+
CpuMonitorAllocator() noexcept = default;
48+
explicit CpuMonitorAllocator(bool usePSRAMBuffers) noexcept : usePSRAMBuffers_(usePSRAMBuffers) {}
49+
50+
template <typename U>
51+
CpuMonitorAllocator(const CpuMonitorAllocator<U> &other) noexcept : usePSRAMBuffers_(other.usePSRAMBuffers()) {}
52+
53+
T *allocate(std::size_t n) {
54+
if (n == 0) {
55+
return nullptr;
56+
}
57+
if (n > (std::numeric_limits<std::size_t>::max() / sizeof(T))) {
58+
#if defined(__cpp_exceptions)
59+
throw std::bad_alloc();
60+
#else
61+
std::abort();
62+
#endif
63+
}
64+
65+
void *memory = cpu_monitor_allocator_detail::allocate(n * sizeof(T), usePSRAMBuffers_);
66+
if (memory == nullptr) {
67+
#if defined(__cpp_exceptions)
68+
throw std::bad_alloc();
69+
#else
70+
std::abort();
71+
#endif
72+
}
73+
return static_cast<T *>(memory);
74+
}
75+
76+
void deallocate(T *ptr, std::size_t) noexcept {
77+
cpu_monitor_allocator_detail::deallocate(ptr);
78+
}
79+
80+
bool usePSRAMBuffers() const noexcept {
81+
return usePSRAMBuffers_;
82+
}
83+
84+
template <typename U>
85+
bool operator==(const CpuMonitorAllocator<U> &other) const noexcept {
86+
return usePSRAMBuffers_ == other.usePSRAMBuffers();
87+
}
88+
89+
template <typename U>
90+
bool operator!=(const CpuMonitorAllocator<U> &other) const noexcept {
91+
return !(*this == other);
92+
}
93+
94+
private:
95+
template <typename>
96+
friend class CpuMonitorAllocator;
97+
98+
bool usePSRAMBuffers_ = false;
99+
};
100+
101+
template <typename T>
102+
using CpuMonitorDeque = std::deque<T, CpuMonitorAllocator<T>>;
103+
104+
template <typename T>
105+
using CpuMonitorVector = std::vector<T, CpuMonitorAllocator<T>>;

0 commit comments

Comments
 (0)