Repository files navigation

Fresh

Fresh is a RAM-first document database for ESP32 with owned, pluggable storage.

Fresh keeps small document collections and append-style logs in RAM while a background task persists them through a selected ESP-IDF storage backend.

CIReleaseLicense: MIT

Features

  • RAM-first create, update, delete, and append operations.
  • General JSON document models and append-style stream models.
  • Owned LittleFS, SDSPI, SDMMC, eMMC, and custom storage backends.
  • Application-file access through db.storage().
  • Destructive whole-volume formatting through db.format().
  • Background persistence, forced sync, streaming backup, and restore.
  • FreshResult error handling without exceptions.
  • FreeRTOS mutex protection and explicit shutdown behavior.

Install

PlatformIO

[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/fresh.git
bblanchon/ArduinoJson@>=7.0.0
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11

Arduino IDE

Fresh is not published to Arduino Library Manager yet. Download the repository ZIP or clone it into:

Arduino/libraries/Fresh

Quick start

#include<Arduino.h>
#include<Fresh.h>
Fresh db;
voidsetup() {
Serial.begin(115200);
FreshInitResult initialized = db.init("/fresh_app");
if (!initialized) {
Serial.println(initialized.message.c_str());
return;
}
FreshModelResult usersResult = db.createModel("User");
if (!usersResult && usersResult.status != FreshStatus::ModelExists) {
Serial.println(usersResult.message.c_str());
return;
}
FreshModel users = db.model("User");
JsonDocument user;
user["name"] = "Panna";
user["age"] = 19;
FreshResult created = users.create(user);
if (!created) {
Serial.println(created.message.c_str());
return;
}
FreshResult found = users.findById(user["_id"].as<constchar*>());
if (found) {
serializeJson(found.doc, Serial);
Serial.println();
}
}
voidloop() {
delay(1000);
}

The convenience overload above creates a default FreshLittleFSStorage.

Explicit storage

FreshConfig contains database settings only. Construct storage independently and pass it to init():

FreshConfig config;
config.syncIntervalMS = 5000;
FreshLittleFSConfig storageConfig;
storageConfig.partitionLabel = "spiffs";
storageConfig.mountPath = "/littlefs";
storageConfig.maxOpenFiles = 12;
storageConfig.formatOnMountFailure = false;
FreshInitResult initialized = db.init(
"/fresh_app",
config,
FreshLittleFSStorage(storageConfig)
);

Fresh owns the backend after successful initialization. A named backend must be moved:

FreshLittleFSStorage storage(storageConfig);
db.init("/fresh_app", config, std::move(storage));

Supported built-in backends:

  • FreshLittleFSStorage
  • FreshSDStorage with SDSPI
  • FreshSDStorage with SDMMC
  • FreshEMMCStorage

Custom classes can derive from FreshStorage.

Application files

Database and application files can share the selected backend without a separate filesystem wrapper:

FreshResult directory = db.storage().ensureDirectory("/backups");
constuint8_t marker[] = {1, 2, 3, 4};
FreshResult written = db.storage().writeFile(
"/backups/marker.bin",
marker,
sizeof(marker)
);
bool exists = db.storage().exists("/backups/marker.bin");

For streaming:

FreshFile file;
FreshResult opened = db.storage().open(
"/backups/system.fresh",
FreshOpenMode::Write,
file
);
if (!opened) return;
file.write(buffer, length);
file.syncAndClose();

The configured database root is protected from application storage operations. Open application files cause deinit() to return FreshStatus::Busy until they are closed.

Fresh uses ESP-IDF filesystem and media drivers directly. It does not include or synchronize Arduino's global LittleFS, SD, or SD_MMC objects.

Formatting storage

Caution

db.format() formats the complete configured storage volume. It deletes the Fresh database, application files, and unrelated files stored on the same filesystem.

FreshResult formatted = db.format();
if (!formatted) {
Serial.println(formatted.message.c_str());
return;
}
// The same Fresh instance is initialized again as an empty database.
db.createModel("settings");

Formatting stops background work, closes and invalidates all tracked files, invalidates existing model handles, writes an empty durable manifest, and restarts the sync task. Use dropAllModels() when files outside the database must be preserved. See Formatting storage for the custom-backend contract and failure behavior.

Persistence behavior

Important

A successful public mutation means the change was accepted in RAM. It does not necessarily mean the change has reached storage.

OperationRAM updatedStorage updated before return
create() / update() / delete() / append()yesno
flush()yescaptured journal operations
forceSyncAsync()yesno
forceSync()yesyes, when successful
format()resetempty database manifest
deinit({ .sync = true })yesyes, when successful

Additional lifecycle rules:

  • Background sync captures dirty state under a short database lock and performs storage I/O outside that lock.
  • forceSync() performs a blocking forced checkpoint in the caller context.
  • format() performs a destructive synchronous lifecycle transition without a final sync.
  • deinit() performs a final sync by default and waits for the sync task to exit.
  • A timed-out deinit() may be called again to finish shutdown.
  • FreshFile operations are mutex-protected.
  • Callbacks are notifications; schedule blocking database or storage work on another task.

Examples

ExampleDescription
BasicMinimal model and document usage.
CrudGeneral-model CRUD operations.
StreamModelAppend and retrieve stream records.
BackupStreamStreaming backup lifecycle.
LittleFSStorageExplicit LittleFS backend and application files.
SDSPIStorageSD card over SPI.
SDMMCStorageSD card over SDMMC, including Waveshare ESP32-P4 pins and power setup.
EMMCStorageDedicated eMMC backend.
SameFilesystemBackupWrite a backup archive through db.storage().
CustomStorageOwned custom backend over an external medium.
StorageLifecycleRegressionTestStorage ownership, path protection, and shutdown.
StorageFormatRegressionTestWhole-volume format lifecycle and failure behavior.
StorageFailureRegressionTestInject file and backend failures.
HardeningRegressionTestMutation and shutdown hardening.

Regression sketches are compiled in CI but require manual execution on hardware.

Documentation

Compatibility

ItemSupport
FrameworkArduino as an ESP-IDF component / Arduino ESP32
LanguageC++20
Storage driversESP-IDF LittleFS, SDSPI, SDMMC, eMMC, custom
Persistence encodingArduinoJson MessagePack
PSRAMUsed for eligible internal allocations when available
ExceptionsNot used by the Fresh API
Status0.2.0 pre-release

Limitations

Fresh is not intended for large datasets, high-frequency telemetry, SQL-style queries, multi-device concurrency, or data that must be durable after every public mutation.

Automatic SD hot-swap recovery, automatic remount, and multiple simultaneously managed volumes are not part of 0.2.0-rc.1.

License

MIT — see LICENSE.md.

ZekStack

Fresh is part of the ZekStack ESP32 library stack.

About

Fresh is a RAM-first document database for ESP32 with async LittleFS persistence.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Fresh

Fresh is a RAM-first document database for ESP32 with owned, pluggable storage.

Fresh keeps small document collections and append-style logs in RAM while a background task persists them through a selected ESP-IDF storage backend.

CIReleaseLicense: MIT

Features

  • RAM-first create, update, delete, and append operations.
  • General JSON document models and append-style stream models.
  • Owned LittleFS, SDSPI, SDMMC, eMMC, and custom storage backends.
  • Application-file access through db.storage().
  • Destructive whole-volume formatting through db.format().
  • Background persistence, forced sync, streaming backup, and restore.
  • FreshResult error handling without exceptions.
  • FreeRTOS mutex protection and explicit shutdown behavior.

Install

PlatformIO

[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/fresh.git
bblanchon/ArduinoJson@>=7.0.0
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11

Arduino IDE

Fresh is not published to Arduino Library Manager yet. Download the repository ZIP or clone it into:

Arduino/libraries/Fresh

Quick start

#include<Arduino.h>
#include<Fresh.h>
Fresh db;
voidsetup() {
Serial.begin(115200);
FreshInitResult initialized = db.init("/fresh_app");
if (!initialized) {
Serial.println(initialized.message.c_str());
return;
}
FreshModelResult usersResult = db.createModel("User");
if (!usersResult && usersResult.status != FreshStatus::ModelExists) {
Serial.println(usersResult.message.c_str());
return;
}
FreshModel users = db.model("User");
JsonDocument user;
user["name"] = "Panna";
user["age"] = 19;
FreshResult created = users.create(user);
if (!created) {
Serial.println(created.message.c_str());
return;
}
FreshResult found = users.findById(user["_id"].as<constchar*>());
if (found) {
serializeJson(found.doc, Serial);
Serial.println();
}
}
voidloop() {
delay(1000);
}

The convenience overload above creates a default FreshLittleFSStorage.

Explicit storage

FreshConfig contains database settings only. Construct storage independently and pass it to init():

FreshConfig config;
config.syncIntervalMS = 5000;
FreshLittleFSConfig storageConfig;
storageConfig.partitionLabel = "spiffs";
storageConfig.mountPath = "/littlefs";
storageConfig.maxOpenFiles = 12;
storageConfig.formatOnMountFailure = false;
FreshInitResult initialized = db.init(
"/fresh_app",
config,
FreshLittleFSStorage(storageConfig)
);

Fresh owns the backend after successful initialization. A named backend must be moved:

FreshLittleFSStorage storage(storageConfig);
db.init("/fresh_app", config, std::move(storage));

Supported built-in backends:

  • FreshLittleFSStorage
  • FreshSDStorage with SDSPI
  • FreshSDStorage with SDMMC
  • FreshEMMCStorage

Custom classes can derive from FreshStorage.

Application files

Database and application files can share the selected backend without a separate filesystem wrapper:

FreshResult directory = db.storage().ensureDirectory("/backups");
constuint8_t marker[] = {1, 2, 3, 4};
FreshResult written = db.storage().writeFile(
"/backups/marker.bin",
marker,
sizeof(marker)
);
bool exists = db.storage().exists("/backups/marker.bin");

For streaming:

FreshFile file;
FreshResult opened = db.storage().open(
"/backups/system.fresh",
FreshOpenMode::Write,
file
);
if (!opened) return;
file.write(buffer, length);
file.syncAndClose();

The configured database root is protected from application storage operations. Open application files cause deinit() to return FreshStatus::Busy until they are closed.

Fresh uses ESP-IDF filesystem and media drivers directly. It does not include or synchronize Arduino's global LittleFS, SD, or SD_MMC objects.

Formatting storage

Caution

db.format() formats the complete configured storage volume. It deletes the Fresh database, application files, and unrelated files stored on the same filesystem.

FreshResult formatted = db.format();
if (!formatted) {
Serial.println(formatted.message.c_str());
return;
}
// The same Fresh instance is initialized again as an empty database.
db.createModel("settings");

Formatting stops background work, closes and invalidates all tracked files, invalidates existing model handles, writes an empty durable manifest, and restarts the sync task. Use dropAllModels() when files outside the database must be preserved. See Formatting storage for the custom-backend contract and failure behavior.

Persistence behavior

Important

A successful public mutation means the change was accepted in RAM. It does not necessarily mean the change has reached storage.

OperationRAM updatedStorage updated before return
create() / update() / delete() / append()yesno
flush()yescaptured journal operations
forceSyncAsync()yesno
forceSync()yesyes, when successful
format()resetempty database manifest
deinit({ .sync = true })yesyes, when successful

Additional lifecycle rules:

  • Background sync captures dirty state under a short database lock and performs storage I/O outside that lock.
  • forceSync() performs a blocking forced checkpoint in the caller context.
  • format() performs a destructive synchronous lifecycle transition without a final sync.
  • deinit() performs a final sync by default and waits for the sync task to exit.
  • A timed-out deinit() may be called again to finish shutdown.
  • FreshFile operations are mutex-protected.
  • Callbacks are notifications; schedule blocking database or storage work on another task.

Examples

ExampleDescription
BasicMinimal model and document usage.
CrudGeneral-model CRUD operations.
StreamModelAppend and retrieve stream records.
BackupStreamStreaming backup lifecycle.
LittleFSStorageExplicit LittleFS backend and application files.
SDSPIStorageSD card over SPI.
SDMMCStorageSD card over SDMMC, including Waveshare ESP32-P4 pins and power setup.
EMMCStorageDedicated eMMC backend.
SameFilesystemBackupWrite a backup archive through db.storage().
CustomStorageOwned custom backend over an external medium.
StorageLifecycleRegressionTestStorage ownership, path protection, and shutdown.
StorageFormatRegressionTestWhole-volume format lifecycle and failure behavior.
StorageFailureRegressionTestInject file and backend failures.
HardeningRegressionTestMutation and shutdown hardening.

Regression sketches are compiled in CI but require manual execution on hardware.

Documentation

Compatibility

ItemSupport
FrameworkArduino as an ESP-IDF component / Arduino ESP32
LanguageC++20
Storage driversESP-IDF LittleFS, SDSPI, SDMMC, eMMC, custom
Persistence encodingArduinoJson MessagePack
PSRAMUsed for eligible internal allocations when available
ExceptionsNot used by the Fresh API
Status0.2.0 pre-release

Limitations

Fresh is not intended for large datasets, high-frequency telemetry, SQL-style queries, multi-device concurrency, or data that must be durable after every public mutation.

Automatic SD hot-swap recovery, automatic remount, and multiple simultaneously managed volumes are not part of 0.2.0-rc.1.

License

MIT — see LICENSE.md.

ZekStack

Fresh is part of the ZekStack ESP32 library stack.

About

Fresh is a RAM-first document database for ESP32 with async LittleFS persistence.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Fresh

Fresh is a RAM-first document database for ESP32 with owned, pluggable storage.

Fresh keeps small document collections and append-style logs in RAM while a background task persists them through a selected ESP-IDF storage backend.

CIReleaseLicense: MIT

Features

  • RAM-first create, update, delete, and append operations.
  • General JSON document models and append-style stream models.
  • Owned LittleFS, SDSPI, SDMMC, eMMC, and custom storage backends.
  • Application-file access through db.storage().
  • Destructive whole-volume formatting through db.format().
  • Background persistence, forced sync, streaming backup, and restore.
  • FreshResult error handling without exceptions.
  • FreeRTOS mutex protection and explicit shutdown behavior.

Install

PlatformIO

[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/fresh.git
bblanchon/ArduinoJson@>=7.0.0
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11

Arduino IDE

Fresh is not published to Arduino Library Manager yet. Download the repository ZIP or clone it into:

Arduino/libraries/Fresh

Quick start

#include<Arduino.h>
#include<Fresh.h>
Fresh db;
voidsetup() {
Serial.begin(115200);
FreshInitResult initialized = db.init("/fresh_app");
if (!initialized) {
Serial.println(initialized.message.c_str());
return;
}
FreshModelResult usersResult = db.createModel("User");
if (!usersResult && usersResult.status != FreshStatus::ModelExists) {
Serial.println(usersResult.message.c_str());
return;
}
FreshModel users = db.model("User");
JsonDocument user;
user["name"] = "Panna";
user["age"] = 19;
FreshResult created = users.create(user);
if (!created) {
Serial.println(created.message.c_str());
return;
}
FreshResult found = users.findById(user["_id"].as<constchar*>());
if (found) {
serializeJson(found.doc, Serial);
Serial.println();
}
}
voidloop() {
delay(1000);
}

The convenience overload above creates a default FreshLittleFSStorage.

Explicit storage

FreshConfig contains database settings only. Construct storage independently and pass it to init():

FreshConfig config;
config.syncIntervalMS = 5000;
FreshLittleFSConfig storageConfig;
storageConfig.partitionLabel = "spiffs";
storageConfig.mountPath = "/littlefs";
storageConfig.maxOpenFiles = 12;
storageConfig.formatOnMountFailure = false;
FreshInitResult initialized = db.init(
"/fresh_app",
config,
FreshLittleFSStorage(storageConfig)
);

Fresh owns the backend after successful initialization. A named backend must be moved:

FreshLittleFSStorage storage(storageConfig);
db.init("/fresh_app", config, std::move(storage));

Supported built-in backends:

  • FreshLittleFSStorage
  • FreshSDStorage with SDSPI
  • FreshSDStorage with SDMMC
  • FreshEMMCStorage

Custom classes can derive from FreshStorage.

Application files

Database and application files can share the selected backend without a separate filesystem wrapper:

FreshResult directory = db.storage().ensureDirectory("/backups");
constuint8_t marker[] = {1, 2, 3, 4};
FreshResult written = db.storage().writeFile(
"/backups/marker.bin",
marker,
sizeof(marker)
);
bool exists = db.storage().exists("/backups/marker.bin");

For streaming:

FreshFile file;
FreshResult opened = db.storage().open(
"/backups/system.fresh",
FreshOpenMode::Write,
file
);
if (!opened) return;
file.write(buffer, length);
file.syncAndClose();

The configured database root is protected from application storage operations. Open application files cause deinit() to return FreshStatus::Busy until they are closed.

Fresh uses ESP-IDF filesystem and media drivers directly. It does not include or synchronize Arduino's global LittleFS, SD, or SD_MMC objects.

Formatting storage

Caution

db.format() formats the complete configured storage volume. It deletes the Fresh database, application files, and unrelated files stored on the same filesystem.

FreshResult formatted = db.format();
if (!formatted) {
Serial.println(formatted.message.c_str());
return;
}
// The same Fresh instance is initialized again as an empty database.
db.createModel("settings");

Formatting stops background work, closes and invalidates all tracked files, invalidates existing model handles, writes an empty durable manifest, and restarts the sync task. Use dropAllModels() when files outside the database must be preserved. See Formatting storage for the custom-backend contract and failure behavior.

Persistence behavior

Important

A successful public mutation means the change was accepted in RAM. It does not necessarily mean the change has reached storage.

OperationRAM updatedStorage updated before return
create() / update() / delete() / append()yesno
flush()yescaptured journal operations
forceSyncAsync()yesno
forceSync()yesyes, when successful
format()resetempty database manifest
deinit({ .sync = true })yesyes, when successful

Additional lifecycle rules:

  • Background sync captures dirty state under a short database lock and performs storage I/O outside that lock.
  • forceSync() performs a blocking forced checkpoint in the caller context.
  • format() performs a destructive synchronous lifecycle transition without a final sync.
  • deinit() performs a final sync by default and waits for the sync task to exit.
  • A timed-out deinit() may be called again to finish shutdown.
  • FreshFile operations are mutex-protected.
  • Callbacks are notifications; schedule blocking database or storage work on another task.

Examples

ExampleDescription
BasicMinimal model and document usage.
CrudGeneral-model CRUD operations.
StreamModelAppend and retrieve stream records.
BackupStreamStreaming backup lifecycle.
LittleFSStorageExplicit LittleFS backend and application files.
SDSPIStorageSD card over SPI.
SDMMCStorageSD card over SDMMC, including Waveshare ESP32-P4 pins and power setup.
EMMCStorageDedicated eMMC backend.
SameFilesystemBackupWrite a backup archive through db.storage().
CustomStorageOwned custom backend over an external medium.
StorageLifecycleRegressionTestStorage ownership, path protection, and shutdown.
StorageFormatRegressionTestWhole-volume format lifecycle and failure behavior.
StorageFailureRegressionTestInject file and backend failures.
HardeningRegressionTestMutation and shutdown hardening.

Regression sketches are compiled in CI but require manual execution on hardware.

Documentation

Compatibility

ItemSupport
FrameworkArduino as an ESP-IDF component / Arduino ESP32
LanguageC++20
Storage driversESP-IDF LittleFS, SDSPI, SDMMC, eMMC, custom
Persistence encodingArduinoJson MessagePack
PSRAMUsed for eligible internal allocations when available
ExceptionsNot used by the Fresh API
Status0.2.0 pre-release

Limitations

Fresh is not intended for large datasets, high-frequency telemetry, SQL-style queries, multi-device concurrency, or data that must be durable after every public mutation.

Automatic SD hot-swap recovery, automatic remount, and multiple simultaneously managed volumes are not part of 0.2.0-rc.1.

License

MIT — see LICENSE.md.

ZekStack

Fresh is part of the ZekStack ESP32 library stack.

About

Fresh is a RAM-first document database for ESP32 with async LittleFS persistence.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Fresh

Fresh is a RAM-first document database for ESP32 with owned, pluggable storage.

Fresh keeps small document collections and append-style logs in RAM while a background task persists them through a selected ESP-IDF storage backend.

CIReleaseLicense: MIT

Features

  • RAM-first create, update, delete, and append operations.
  • General JSON document models and append-style stream models.
  • Owned LittleFS, SDSPI, SDMMC, eMMC, and custom storage backends.
  • Application-file access through db.storage().
  • Destructive whole-volume formatting through db.format().
  • Background persistence, forced sync, streaming backup, and restore.
  • FreshResult error handling without exceptions.
  • FreeRTOS mutex protection and explicit shutdown behavior.

Install

PlatformIO

[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/fresh.git
bblanchon/ArduinoJson@>=7.0.0
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11

Arduino IDE

Fresh is not published to Arduino Library Manager yet. Download the repository ZIP or clone it into:

Arduino/libraries/Fresh

Quick start

#include<Arduino.h>
#include<Fresh.h>
Fresh db;
voidsetup() {
Serial.begin(115200);
FreshInitResult initialized = db.init("/fresh_app");
if (!initialized) {
Serial.println(initialized.message.c_str());
return;
}
FreshModelResult usersResult = db.createModel("User");
if (!usersResult && usersResult.status != FreshStatus::ModelExists) {
Serial.println(usersResult.message.c_str());
return;
}
FreshModel users = db.model("User");
JsonDocument user;
user["name"] = "Panna";
user["age"] = 19;
FreshResult created = users.create(user);
if (!created) {
Serial.println(created.message.c_str());
return;
}
FreshResult found = users.findById(user["_id"].as<constchar*>());
if (found) {
serializeJson(found.doc, Serial);
Serial.println();
}
}
voidloop() {
delay(1000);
}

The convenience overload above creates a default FreshLittleFSStorage.

Explicit storage

FreshConfig contains database settings only. Construct storage independently and pass it to init():

FreshConfig config;
config.syncIntervalMS = 5000;
FreshLittleFSConfig storageConfig;
storageConfig.partitionLabel = "spiffs";
storageConfig.mountPath = "/littlefs";
storageConfig.maxOpenFiles = 12;
storageConfig.formatOnMountFailure = false;
FreshInitResult initialized = db.init(
"/fresh_app",
config,
FreshLittleFSStorage(storageConfig)
);

Fresh owns the backend after successful initialization. A named backend must be moved:

FreshLittleFSStorage storage(storageConfig);
db.init("/fresh_app", config, std::move(storage));

Supported built-in backends:

  • FreshLittleFSStorage
  • FreshSDStorage with SDSPI
  • FreshSDStorage with SDMMC
  • FreshEMMCStorage

Custom classes can derive from FreshStorage.

Application files

Database and application files can share the selected backend without a separate filesystem wrapper:

FreshResult directory = db.storage().ensureDirectory("/backups");
constuint8_t marker[] = {1, 2, 3, 4};
FreshResult written = db.storage().writeFile(
"/backups/marker.bin",
marker,
sizeof(marker)
);
bool exists = db.storage().exists("/backups/marker.bin");

For streaming:

FreshFile file;
FreshResult opened = db.storage().open(
"/backups/system.fresh",
FreshOpenMode::Write,
file
);
if (!opened) return;
file.write(buffer, length);
file.syncAndClose();

The configured database root is protected from application storage operations. Open application files cause deinit() to return FreshStatus::Busy until they are closed.

Fresh uses ESP-IDF filesystem and media drivers directly. It does not include or synchronize Arduino's global LittleFS, SD, or SD_MMC objects.

Formatting storage

Caution

db.format() formats the complete configured storage volume. It deletes the Fresh database, application files, and unrelated files stored on the same filesystem.

FreshResult formatted = db.format();
if (!formatted) {
Serial.println(formatted.message.c_str());
return;
}
// The same Fresh instance is initialized again as an empty database.
db.createModel("settings");

Formatting stops background work, closes and invalidates all tracked files, invalidates existing model handles, writes an empty durable manifest, and restarts the sync task. Use dropAllModels() when files outside the database must be preserved. See Formatting storage for the custom-backend contract and failure behavior.

Persistence behavior

Important

A successful public mutation means the change was accepted in RAM. It does not necessarily mean the change has reached storage.

OperationRAM updatedStorage updated before return
create() / update() / delete() / append()yesno
flush()yescaptured journal operations
forceSyncAsync()yesno
forceSync()yesyes, when successful
format()resetempty database manifest
deinit({ .sync = true })yesyes, when successful

Additional lifecycle rules:

  • Background sync captures dirty state under a short database lock and performs storage I/O outside that lock.
  • forceSync() performs a blocking forced checkpoint in the caller context.
  • format() performs a destructive synchronous lifecycle transition without a final sync.
  • deinit() performs a final sync by default and waits for the sync task to exit.
  • A timed-out deinit() may be called again to finish shutdown.
  • FreshFile operations are mutex-protected.
  • Callbacks are notifications; schedule blocking database or storage work on another task.

Examples

ExampleDescription
BasicMinimal model and document usage.
CrudGeneral-model CRUD operations.
StreamModelAppend and retrieve stream records.
BackupStreamStreaming backup lifecycle.
LittleFSStorageExplicit LittleFS backend and application files.
SDSPIStorageSD card over SPI.
SDMMCStorageSD card over SDMMC, including Waveshare ESP32-P4 pins and power setup.
EMMCStorageDedicated eMMC backend.
SameFilesystemBackupWrite a backup archive through db.storage().
CustomStorageOwned custom backend over an external medium.
StorageLifecycleRegressionTestStorage ownership, path protection, and shutdown.
StorageFormatRegressionTestWhole-volume format lifecycle and failure behavior.
StorageFailureRegressionTestInject file and backend failures.
HardeningRegressionTestMutation and shutdown hardening.

Regression sketches are compiled in CI but require manual execution on hardware.

Documentation

Compatibility

ItemSupport
FrameworkArduino as an ESP-IDF component / Arduino ESP32
LanguageC++20
Storage driversESP-IDF LittleFS, SDSPI, SDMMC, eMMC, custom
Persistence encodingArduinoJson MessagePack
PSRAMUsed for eligible internal allocations when available
ExceptionsNot used by the Fresh API
Status0.2.0 pre-release

Limitations

Fresh is not intended for large datasets, high-frequency telemetry, SQL-style queries, multi-device concurrency, or data that must be durable after every public mutation.

Automatic SD hot-swap recovery, automatic remount, and multiple simultaneously managed volumes are not part of 0.2.0-rc.1.

License

MIT — see LICENSE.md.

ZekStack

Fresh is part of the ZekStack ESP32 library stack.

About

Fresh is a RAM-first document database for ESP32 with async LittleFS persistence.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Fresh

Fresh is a RAM-first document database for ESP32 with owned, pluggable storage.

Fresh keeps small document collections and append-style logs in RAM while a background task persists them through a selected ESP-IDF storage backend.

CIReleaseLicense: MIT

Features

  • RAM-first create, update, delete, and append operations.
  • General JSON document models and append-style stream models.
  • Owned LittleFS, SDSPI, SDMMC, eMMC, and custom storage backends.
  • Application-file access through db.storage().
  • Destructive whole-volume formatting through db.format().
  • Background persistence, forced sync, streaming backup, and restore.
  • FreshResult error handling without exceptions.
  • FreeRTOS mutex protection and explicit shutdown behavior.

Install

PlatformIO

[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/fresh.git
bblanchon/ArduinoJson@>=7.0.0
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11

Arduino IDE

Fresh is not published to Arduino Library Manager yet. Download the repository ZIP or clone it into:

Arduino/libraries/Fresh

Quick start

#include<Arduino.h>
#include<Fresh.h>
Fresh db;
voidsetup() {
Serial.begin(115200);
FreshInitResult initialized = db.init("/fresh_app");
if (!initialized) {
Serial.println(initialized.message.c_str());
return;
}
FreshModelResult usersResult = db.createModel("User");
if (!usersResult && usersResult.status != FreshStatus::ModelExists) {
Serial.println(usersResult.message.c_str());
return;
}
FreshModel users = db.model("User");
JsonDocument user;
user["name"] = "Panna";
user["age"] = 19;
FreshResult created = users.create(user);
if (!created) {
Serial.println(created.message.c_str());
return;
}
FreshResult found = users.findById(user["_id"].as<constchar*>());
if (found) {
serializeJson(found.doc, Serial);
Serial.println();
}
}
voidloop() {
delay(1000);
}

The convenience overload above creates a default FreshLittleFSStorage.

Explicit storage

FreshConfig contains database settings only. Construct storage independently and pass it to init():

FreshConfig config;
config.syncIntervalMS = 5000;
FreshLittleFSConfig storageConfig;
storageConfig.partitionLabel = "spiffs";
storageConfig.mountPath = "/littlefs";
storageConfig.maxOpenFiles = 12;
storageConfig.formatOnMountFailure = false;
FreshInitResult initialized = db.init(
"/fresh_app",
config,
FreshLittleFSStorage(storageConfig)
);

Fresh owns the backend after successful initialization. A named backend must be moved:

FreshLittleFSStorage storage(storageConfig);
db.init("/fresh_app", config, std::move(storage));

Supported built-in backends:

  • FreshLittleFSStorage
  • FreshSDStorage with SDSPI
  • FreshSDStorage with SDMMC
  • FreshEMMCStorage

Custom classes can derive from FreshStorage.

Application files

Database and application files can share the selected backend without a separate filesystem wrapper:

FreshResult directory = db.storage().ensureDirectory("/backups");
constuint8_t marker[] = {1, 2, 3, 4};
FreshResult written = db.storage().writeFile(
"/backups/marker.bin",
marker,
sizeof(marker)
);
bool exists = db.storage().exists("/backups/marker.bin");

For streaming:

FreshFile file;
FreshResult opened = db.storage().open(
"/backups/system.fresh",
FreshOpenMode::Write,
file
);
if (!opened) return;
file.write(buffer, length);
file.syncAndClose();

The configured database root is protected from application storage operations. Open application files cause deinit() to return FreshStatus::Busy until they are closed.

Fresh uses ESP-IDF filesystem and media drivers directly. It does not include or synchronize Arduino's global LittleFS, SD, or SD_MMC objects.

Formatting storage

Caution

db.format() formats the complete configured storage volume. It deletes the Fresh database, application files, and unrelated files stored on the same filesystem.

FreshResult formatted = db.format();
if (!formatted) {
Serial.println(formatted.message.c_str());
return;
}
// The same Fresh instance is initialized again as an empty database.
db.createModel("settings");

Formatting stops background work, closes and invalidates all tracked files, invalidates existing model handles, writes an empty durable manifest, and restarts the sync task. Use dropAllModels() when files outside the database must be preserved. See Formatting storage for the custom-backend contract and failure behavior.

Persistence behavior

Important

A successful public mutation means the change was accepted in RAM. It does not necessarily mean the change has reached storage.

OperationRAM updatedStorage updated before return
create() / update() / delete() / append()yesno
flush()yescaptured journal operations
forceSyncAsync()yesno
forceSync()yesyes, when successful
format()resetempty database manifest
deinit({ .sync = true })yesyes, when successful

Additional lifecycle rules:

  • Background sync captures dirty state under a short database lock and performs storage I/O outside that lock.
  • forceSync() performs a blocking forced checkpoint in the caller context.
  • format() performs a destructive synchronous lifecycle transition without a final sync.
  • deinit() performs a final sync by default and waits for the sync task to exit.
  • A timed-out deinit() may be called again to finish shutdown.
  • FreshFile operations are mutex-protected.
  • Callbacks are notifications; schedule blocking database or storage work on another task.

Examples

ExampleDescription
BasicMinimal model and document usage.
CrudGeneral-model CRUD operations.
StreamModelAppend and retrieve stream records.
BackupStreamStreaming backup lifecycle.
LittleFSStorageExplicit LittleFS backend and application files.
SDSPIStorageSD card over SPI.
SDMMCStorageSD card over SDMMC, including Waveshare ESP32-P4 pins and power setup.
EMMCStorageDedicated eMMC backend.
SameFilesystemBackupWrite a backup archive through db.storage().
CustomStorageOwned custom backend over an external medium.
StorageLifecycleRegressionTestStorage ownership, path protection, and shutdown.
StorageFormatRegressionTestWhole-volume format lifecycle and failure behavior.
StorageFailureRegressionTestInject file and backend failures.
HardeningRegressionTestMutation and shutdown hardening.

Regression sketches are compiled in CI but require manual execution on hardware.

Documentation

Compatibility

ItemSupport
FrameworkArduino as an ESP-IDF component / Arduino ESP32
LanguageC++20
Storage driversESP-IDF LittleFS, SDSPI, SDMMC, eMMC, custom
Persistence encodingArduinoJson MessagePack
PSRAMUsed for eligible internal allocations when available
ExceptionsNot used by the Fresh API
Status0.2.0 pre-release

Limitations

Fresh is not intended for large datasets, high-frequency telemetry, SQL-style queries, multi-device concurrency, or data that must be durable after every public mutation.

Automatic SD hot-swap recovery, automatic remount, and multiple simultaneously managed volumes are not part of 0.2.0-rc.1.

License

MIT — see LICENSE.md.

ZekStack

Fresh is part of the ZekStack ESP32 library stack.

About

Fresh is a RAM-first document database for ESP32 with async LittleFS persistence.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Fresh

Fresh is a RAM-first document database for ESP32 with owned, pluggable storage.

Fresh keeps small document collections and append-style logs in RAM while a background task persists them through a selected ESP-IDF storage backend.

CIReleaseLicense: MIT

Features

  • RAM-first create, update, delete, and append operations.
  • General JSON document models and append-style stream models.
  • Owned LittleFS, SDSPI, SDMMC, eMMC, and custom storage backends.
  • Application-file access through db.storage().
  • Destructive whole-volume formatting through db.format().
  • Background persistence, forced sync, streaming backup, and restore.
  • FreshResult error handling without exceptions.
  • FreeRTOS mutex protection and explicit shutdown behavior.

Install

PlatformIO

[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/fresh.git
bblanchon/ArduinoJson@>=7.0.0
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11

Arduino IDE

Fresh is not published to Arduino Library Manager yet. Download the repository ZIP or clone it into:

Arduino/libraries/Fresh

Quick start

#include<Arduino.h>
#include<Fresh.h>
Fresh db;
voidsetup() {
Serial.begin(115200);
FreshInitResult initialized = db.init("/fresh_app");
if (!initialized) {
Serial.println(initialized.message.c_str());
return;
}
FreshModelResult usersResult = db.createModel("User");
if (!usersResult && usersResult.status != FreshStatus::ModelExists) {
Serial.println(usersResult.message.c_str());
return;
}
FreshModel users = db.model("User");
JsonDocument user;
user["name"] = "Panna";
user["age"] = 19;
FreshResult created = users.create(user);
if (!created) {
Serial.println(created.message.c_str());
return;
}
FreshResult found = users.findById(user["_id"].as<constchar*>());
if (found) {
serializeJson(found.doc, Serial);
Serial.println();
}
}
voidloop() {
delay(1000);
}

The convenience overload above creates a default FreshLittleFSStorage.

Explicit storage

FreshConfig contains database settings only. Construct storage independently and pass it to init():

FreshConfig config;
config.syncIntervalMS = 5000;
FreshLittleFSConfig storageConfig;
storageConfig.partitionLabel = "spiffs";
storageConfig.mountPath = "/littlefs";
storageConfig.maxOpenFiles = 12;
storageConfig.formatOnMountFailure = false;
FreshInitResult initialized = db.init(
"/fresh_app",
config,
FreshLittleFSStorage(storageConfig)
);

Fresh owns the backend after successful initialization. A named backend must be moved:

FreshLittleFSStorage storage(storageConfig);
db.init("/fresh_app", config, std::move(storage));

Supported built-in backends:

  • FreshLittleFSStorage
  • FreshSDStorage with SDSPI
  • FreshSDStorage with SDMMC
  • FreshEMMCStorage

Custom classes can derive from FreshStorage.

Application files

Database and application files can share the selected backend without a separate filesystem wrapper:

FreshResult directory = db.storage().ensureDirectory("/backups");
constuint8_t marker[] = {1, 2, 3, 4};
FreshResult written = db.storage().writeFile(
"/backups/marker.bin",
marker,
sizeof(marker)
);
bool exists = db.storage().exists("/backups/marker.bin");

For streaming:

FreshFile file;
FreshResult opened = db.storage().open(
"/backups/system.fresh",
FreshOpenMode::Write,
file
);
if (!opened) return;
file.write(buffer, length);
file.syncAndClose();

The configured database root is protected from application storage operations. Open application files cause deinit() to return FreshStatus::Busy until they are closed.

Fresh uses ESP-IDF filesystem and media drivers directly. It does not include or synchronize Arduino's global LittleFS, SD, or SD_MMC objects.

Formatting storage

Caution

db.format() formats the complete configured storage volume. It deletes the Fresh database, application files, and unrelated files stored on the same filesystem.

FreshResult formatted = db.format();
if (!formatted) {
Serial.println(formatted.message.c_str());
return;
}
// The same Fresh instance is initialized again as an empty database.
db.createModel("settings");

Formatting stops background work, closes and invalidates all tracked files, invalidates existing model handles, writes an empty durable manifest, and restarts the sync task. Use dropAllModels() when files outside the database must be preserved. See Formatting storage for the custom-backend contract and failure behavior.

Persistence behavior

Important

A successful public mutation means the change was accepted in RAM. It does not necessarily mean the change has reached storage.

OperationRAM updatedStorage updated before return
create() / update() / delete() / append()yesno
flush()yescaptured journal operations
forceSyncAsync()yesno
forceSync()yesyes, when successful
format()resetempty database manifest
deinit({ .sync = true })yesyes, when successful

Additional lifecycle rules:

  • Background sync captures dirty state under a short database lock and performs storage I/O outside that lock.
  • forceSync() performs a blocking forced checkpoint in the caller context.
  • format() performs a destructive synchronous lifecycle transition without a final sync.
  • deinit() performs a final sync by default and waits for the sync task to exit.
  • A timed-out deinit() may be called again to finish shutdown.
  • FreshFile operations are mutex-protected.
  • Callbacks are notifications; schedule blocking database or storage work on another task.

Examples

ExampleDescription
BasicMinimal model and document usage.
CrudGeneral-model CRUD operations.
StreamModelAppend and retrieve stream records.
BackupStreamStreaming backup lifecycle.
LittleFSStorageExplicit LittleFS backend and application files.
SDSPIStorageSD card over SPI.
SDMMCStorageSD card over SDMMC, including Waveshare ESP32-P4 pins and power setup.
EMMCStorageDedicated eMMC backend.
SameFilesystemBackupWrite a backup archive through db.storage().
CustomStorageOwned custom backend over an external medium.
StorageLifecycleRegressionTestStorage ownership, path protection, and shutdown.
StorageFormatRegressionTestWhole-volume format lifecycle and failure behavior.
StorageFailureRegressionTestInject file and backend failures.
HardeningRegressionTestMutation and shutdown hardening.

Regression sketches are compiled in CI but require manual execution on hardware.

Documentation

Compatibility

ItemSupport
FrameworkArduino as an ESP-IDF component / Arduino ESP32
LanguageC++20
Storage driversESP-IDF LittleFS, SDSPI, SDMMC, eMMC, custom
Persistence encodingArduinoJson MessagePack
PSRAMUsed for eligible internal allocations when available
ExceptionsNot used by the Fresh API
Status0.2.0 pre-release

Limitations

Fresh is not intended for large datasets, high-frequency telemetry, SQL-style queries, multi-device concurrency, or data that must be durable after every public mutation.

Automatic SD hot-swap recovery, automatic remount, and multiple simultaneously managed volumes are not part of 0.2.0-rc.1.

License

MIT — see LICENSE.md.

ZekStack

Fresh is part of the ZekStack ESP32 library stack.

About

Fresh is a RAM-first document database for ESP32 with async LittleFS persistence.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Fresh

Fresh is a RAM-first document database for ESP32 with owned, pluggable storage.

Fresh keeps small document collections and append-style logs in RAM while a background task persists them through a selected ESP-IDF storage backend.

CIReleaseLicense: MIT

Features

  • RAM-first create, update, delete, and append operations.
  • General JSON document models and append-style stream models.
  • Owned LittleFS, SDSPI, SDMMC, eMMC, and custom storage backends.
  • Application-file access through db.storage().
  • Destructive whole-volume formatting through db.format().
  • Background persistence, forced sync, streaming backup, and restore.
  • FreshResult error handling without exceptions.
  • FreeRTOS mutex protection and explicit shutdown behavior.

Install

PlatformIO

[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/fresh.git
bblanchon/ArduinoJson@>=7.0.0
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11

Arduino IDE

Fresh is not published to Arduino Library Manager yet. Download the repository ZIP or clone it into:

Arduino/libraries/Fresh

Quick start

#include<Arduino.h>
#include<Fresh.h>
Fresh db;
voidsetup() {
Serial.begin(115200);
FreshInitResult initialized = db.init("/fresh_app");
if (!initialized) {
Serial.println(initialized.message.c_str());
return;
}
FreshModelResult usersResult = db.createModel("User");
if (!usersResult && usersResult.status != FreshStatus::ModelExists) {
Serial.println(usersResult.message.c_str());
return;
}
FreshModel users = db.model("User");
JsonDocument user;
user["name"] = "Panna";
user["age"] = 19;
FreshResult created = users.create(user);
if (!created) {
Serial.println(created.message.c_str());
return;
}
FreshResult found = users.findById(user["_id"].as<constchar*>());
if (found) {
serializeJson(found.doc, Serial);
Serial.println();
}
}
voidloop() {
delay(1000);
}

The convenience overload above creates a default FreshLittleFSStorage.

Explicit storage

FreshConfig contains database settings only. Construct storage independently and pass it to init():

FreshConfig config;
config.syncIntervalMS = 5000;
FreshLittleFSConfig storageConfig;
storageConfig.partitionLabel = "spiffs";
storageConfig.mountPath = "/littlefs";
storageConfig.maxOpenFiles = 12;
storageConfig.formatOnMountFailure = false;
FreshInitResult initialized = db.init(
"/fresh_app",
config,
FreshLittleFSStorage(storageConfig)
);

Fresh owns the backend after successful initialization. A named backend must be moved:

FreshLittleFSStorage storage(storageConfig);
db.init("/fresh_app", config, std::move(storage));

Supported built-in backends:

  • FreshLittleFSStorage
  • FreshSDStorage with SDSPI
  • FreshSDStorage with SDMMC
  • FreshEMMCStorage

Custom classes can derive from FreshStorage.

Application files

Database and application files can share the selected backend without a separate filesystem wrapper:

FreshResult directory = db.storage().ensureDirectory("/backups");
constuint8_t marker[] = {1, 2, 3, 4};
FreshResult written = db.storage().writeFile(
"/backups/marker.bin",
marker,
sizeof(marker)
);
bool exists = db.storage().exists("/backups/marker.bin");

For streaming:

FreshFile file;
FreshResult opened = db.storage().open(
"/backups/system.fresh",
FreshOpenMode::Write,
file
);
if (!opened) return;
file.write(buffer, length);
file.syncAndClose();

The configured database root is protected from application storage operations. Open application files cause deinit() to return FreshStatus::Busy until they are closed.

Fresh uses ESP-IDF filesystem and media drivers directly. It does not include or synchronize Arduino's global LittleFS, SD, or SD_MMC objects.

Formatting storage

Caution

db.format() formats the complete configured storage volume. It deletes the Fresh database, application files, and unrelated files stored on the same filesystem.

FreshResult formatted = db.format();
if (!formatted) {
Serial.println(formatted.message.c_str());
return;
}
// The same Fresh instance is initialized again as an empty database.
db.createModel("settings");

Formatting stops background work, closes and invalidates all tracked files, invalidates existing model handles, writes an empty durable manifest, and restarts the sync task. Use dropAllModels() when files outside the database must be preserved. See Formatting storage for the custom-backend contract and failure behavior.

Persistence behavior

Important

A successful public mutation means the change was accepted in RAM. It does not necessarily mean the change has reached storage.

OperationRAM updatedStorage updated before return
create() / update() / delete() / append()yesno
flush()yescaptured journal operations
forceSyncAsync()yesno
forceSync()yesyes, when successful
format()resetempty database manifest
deinit({ .sync = true })yesyes, when successful

Additional lifecycle rules:

  • Background sync captures dirty state under a short database lock and performs storage I/O outside that lock.
  • forceSync() performs a blocking forced checkpoint in the caller context.
  • format() performs a destructive synchronous lifecycle transition without a final sync.
  • deinit() performs a final sync by default and waits for the sync task to exit.
  • A timed-out deinit() may be called again to finish shutdown.
  • FreshFile operations are mutex-protected.
  • Callbacks are notifications; schedule blocking database or storage work on another task.

Examples

ExampleDescription
BasicMinimal model and document usage.
CrudGeneral-model CRUD operations.
StreamModelAppend and retrieve stream records.
BackupStreamStreaming backup lifecycle.
LittleFSStorageExplicit LittleFS backend and application files.
SDSPIStorageSD card over SPI.
SDMMCStorageSD card over SDMMC, including Waveshare ESP32-P4 pins and power setup.
EMMCStorageDedicated eMMC backend.
SameFilesystemBackupWrite a backup archive through db.storage().
CustomStorageOwned custom backend over an external medium.
StorageLifecycleRegressionTestStorage ownership, path protection, and shutdown.
StorageFormatRegressionTestWhole-volume format lifecycle and failure behavior.
StorageFailureRegressionTestInject file and backend failures.
HardeningRegressionTestMutation and shutdown hardening.

Regression sketches are compiled in CI but require manual execution on hardware.

Documentation

Compatibility

ItemSupport
FrameworkArduino as an ESP-IDF component / Arduino ESP32
LanguageC++20
Storage driversESP-IDF LittleFS, SDSPI, SDMMC, eMMC, custom
Persistence encodingArduinoJson MessagePack
PSRAMUsed for eligible internal allocations when available
ExceptionsNot used by the Fresh API
Status0.2.0 pre-release

Limitations

Fresh is not intended for large datasets, high-frequency telemetry, SQL-style queries, multi-device concurrency, or data that must be durable after every public mutation.

Automatic SD hot-swap recovery, automatic remount, and multiple simultaneously managed volumes are not part of 0.2.0-rc.1.

License

MIT — see LICENSE.md.

ZekStack

Fresh is part of the ZekStack ESP32 library stack.

About

Fresh is a RAM-first document database for ESP32 with async LittleFS persistence.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Fresh

Fresh is a RAM-first document database for ESP32 with owned, pluggable storage.

Fresh keeps small document collections and append-style logs in RAM while a background task persists them through a selected ESP-IDF storage backend.

CIReleaseLicense: MIT

Features

  • RAM-first create, update, delete, and append operations.
  • General JSON document models and append-style stream models.
  • Owned LittleFS, SDSPI, SDMMC, eMMC, and custom storage backends.
  • Application-file access through db.storage().
  • Destructive whole-volume formatting through db.format().
  • Background persistence, forced sync, streaming backup, and restore.
  • FreshResult error handling without exceptions.
  • FreeRTOS mutex protection and explicit shutdown behavior.

Install

PlatformIO

[env:esp32dev]platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
https://github.com/ZekStack/fresh.git
bblanchon/ArduinoJson@>=7.0.0
build_flags =
-std=gnu++20
build_unflags =
-std=gnu++11

Arduino IDE

Fresh is not published to Arduino Library Manager yet. Download the repository ZIP or clone it into:

Arduino/libraries/Fresh

Quick start

#include<Arduino.h>
#include<Fresh.h>
Fresh db;
voidsetup() {
Serial.begin(115200);
FreshInitResult initialized = db.init("/fresh_app");
if (!initialized) {
Serial.println(initialized.message.c_str());
return;
}
FreshModelResult usersResult = db.createModel("User");
if (!usersResult && usersResult.status != FreshStatus::ModelExists) {
Serial.println(usersResult.message.c_str());
return;
}
FreshModel users = db.model("User");
JsonDocument user;
user["name"] = "Panna";
user["age"] = 19;
FreshResult created = users.create(user);
if (!created) {
Serial.println(created.message.c_str());
return;
}
FreshResult found = users.findById(user["_id"].as<constchar*>());
if (found) {
serializeJson(found.doc, Serial);
Serial.println();
}
}
voidloop() {
delay(1000);
}

The convenience overload above creates a default FreshLittleFSStorage.

Explicit storage

FreshConfig contains database settings only. Construct storage independently and pass it to init():

FreshConfig config;
config.syncIntervalMS = 5000;
FreshLittleFSConfig storageConfig;
storageConfig.partitionLabel = "spiffs";
storageConfig.mountPath = "/littlefs";
storageConfig.maxOpenFiles = 12;
storageConfig.formatOnMountFailure = false;
FreshInitResult initialized = db.init(
"/fresh_app",
config,
FreshLittleFSStorage(storageConfig)
);

Fresh owns the backend after successful initialization. A named backend must be moved:

FreshLittleFSStorage storage(storageConfig);
db.init("/fresh_app", config, std::move(storage));

Supported built-in backends:

  • FreshLittleFSStorage
  • FreshSDStorage with SDSPI
  • FreshSDStorage with SDMMC
  • FreshEMMCStorage

Custom classes can derive from FreshStorage.

Application files

Database and application files can share the selected backend without a separate filesystem wrapper:

FreshResult directory = db.storage().ensureDirectory("/backups");
constuint8_t marker[] = {1, 2, 3, 4};
FreshResult written = db.storage().writeFile(
"/backups/marker.bin",
marker,
sizeof(marker)
);
bool exists = db.storage().exists("/backups/marker.bin");

For streaming:

FreshFile file;
FreshResult opened = db.storage().open(
"/backups/system.fresh",
FreshOpenMode::Write,
file
);
if (!opened) return;
file.write(buffer, length);
file.syncAndClose();

The configured database root is protected from application storage operations. Open application files cause deinit() to return FreshStatus::Busy until they are closed.

Fresh uses ESP-IDF filesystem and media drivers directly. It does not include or synchronize Arduino's global LittleFS, SD, or SD_MMC objects.

Formatting storage

Caution

db.format() formats the complete configured storage volume. It deletes the Fresh database, application files, and unrelated files stored on the same filesystem.

FreshResult formatted = db.format();
if (!formatted) {
Serial.println(formatted.message.c_str());
return;
}
// The same Fresh instance is initialized again as an empty database.
db.createModel("settings");

Formatting stops background work, closes and invalidates all tracked files, invalidates existing model handles, writes an empty durable manifest, and restarts the sync task. Use dropAllModels() when files outside the database must be preserved. See Formatting storage for the custom-backend contract and failure behavior.

Persistence behavior

Important

A successful public mutation means the change was accepted in RAM. It does not necessarily mean the change has reached storage.

OperationRAM updatedStorage updated before return
create() / update() / delete() / append()yesno
flush()yescaptured journal operations
forceSyncAsync()yesno
forceSync()yesyes, when successful
format()resetempty database manifest
deinit({ .sync = true })yesyes, when successful

Additional lifecycle rules:

  • Background sync captures dirty state under a short database lock and performs storage I/O outside that lock.
  • forceSync() performs a blocking forced checkpoint in the caller context.
  • format() performs a destructive synchronous lifecycle transition without a final sync.
  • deinit() performs a final sync by default and waits for the sync task to exit.
  • A timed-out deinit() may be called again to finish shutdown.
  • FreshFile operations are mutex-protected.
  • Callbacks are notifications; schedule blocking database or storage work on another task.

Examples

ExampleDescription
BasicMinimal model and document usage.
CrudGeneral-model CRUD operations.
StreamModelAppend and retrieve stream records.
BackupStreamStreaming backup lifecycle.
LittleFSStorageExplicit LittleFS backend and application files.
SDSPIStorageSD card over SPI.
SDMMCStorageSD card over SDMMC, including Waveshare ESP32-P4 pins and power setup.
EMMCStorageDedicated eMMC backend.
SameFilesystemBackupWrite a backup archive through db.storage().
CustomStorageOwned custom backend over an external medium.
StorageLifecycleRegressionTestStorage ownership, path protection, and shutdown.
StorageFormatRegressionTestWhole-volume format lifecycle and failure behavior.
StorageFailureRegressionTestInject file and backend failures.
HardeningRegressionTestMutation and shutdown hardening.

Regression sketches are compiled in CI but require manual execution on hardware.

Documentation

Compatibility

ItemSupport
FrameworkArduino as an ESP-IDF component / Arduino ESP32
LanguageC++20
Storage driversESP-IDF LittleFS, SDSPI, SDMMC, eMMC, custom
Persistence encodingArduinoJson MessagePack
PSRAMUsed for eligible internal allocations when available
ExceptionsNot used by the Fresh API
Status0.2.0 pre-release

Limitations

Fresh is not intended for large datasets, high-frequency telemetry, SQL-style queries, multi-device concurrency, or data that must be durable after every public mutation.

Automatic SD hot-swap recovery, automatic remount, and multiple simultaneously managed volumes are not part of 0.2.0-rc.1.

License

MIT — see LICENSE.md.

ZekStack

Fresh is part of the ZekStack ESP32 library stack.

About

Fresh is a RAM-first document database for ESP32 with async LittleFS persistence.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages