Uniot Core
0.9.0
Loading...
Searching...
No Matches
NVSFS.h
Go to the documentation of this file.
1/*
2 * This is a part of the Uniot project.
3 * Copyright (C) 2016-2025 Uniot <contact@uniot.io>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
28
29#pragma once
30
31#ifdef ESP32
32
33#include <Bytes.h>
34#include <Logger.h>
35#include <Preferences.h>
36
37namespace uniot {
38
60class NVSFile {
61 public:
76 NVSFile(const String& path, const String& mode, Preferences& prefs)
77 : mKey(path), mMode(mode), mPrefs(prefs), mValid(true) {
78 mKey = normalizePath(mKey);
79 if (mMode == "r") {
80 _loadData();
81 } else if (mMode == "w") {
82 mBuffer = Bytes();
83 } else {
84 mValid = false;
85 }
86 }
87
94 operator bool() const { return mValid; }
95
111 size_t write(const uint8_t* data, size_t size) {
112 if (!mValid || mMode != "w") {
113 return 0;
114 }
115
116 mBuffer = Bytes(data, size);
117 return size;
118 }
119
133 if (!mValid || mMode != "r") {
134 return Bytes();
135 }
136
137 return mBuffer;
138 }
139
153 void close() {
154 if (!mValid) {
155 return;
156 }
157
158 if (mMode == "w") {
159 if (mBuffer.size() == 0) {
160 mPrefs.remove(mKey.c_str());
161 } else {
162 mPrefs.putBytes(mKey.c_str(), mBuffer.raw(), mBuffer.size());
163 }
164 }
165
166 mValid = false;
167 }
168
183 static String normalizePath(const String& path) {
184 String normalized = path;
185 if (normalized.startsWith("/")) {
186 normalized = normalized.substring(1);
187 }
188 if (normalized.length() > 15) {
189 normalized = normalized.substring(0, 15);
190 UNIOT_LOG_WARN("NVS key truncated to 15 chars: %s", normalized.c_str());
191 }
192 return normalized;
193 }
194
195 private:
196 String mKey;
197 String mMode;
198 Bytes mBuffer;
199 Preferences& mPrefs;
200 bool mValid;
201
212 void _loadData() {
213 if (!mPrefs.isKey(mKey.c_str())) {
214 mBuffer = Bytes();
215 return;
216 }
217
218 size_t dataSize = mPrefs.getBytesLength(mKey.c_str());
219 if (dataSize == 0) {
220 mBuffer = Bytes();
221 return;
222 }
223
224 mBuffer = Bytes(nullptr, dataSize);
225 if (mBuffer.size() != dataSize) {
226 UNIOT_LOG_ERROR("NVS read failed, no memory for %u bytes: %s", dataSize, mKey.c_str());
227 mBuffer = Bytes();
228 return;
229 }
230
231 // Sized by the buffer, not dataSize, so a buffer that does not match can never be
232 // overrun. A short read keeps only what was read: the zero padding left behind would
233 // otherwise decode as data.
234 auto read = mBuffer.fill([this](uint8_t* buf, size_t size) {
235 return mPrefs.getBytes(mKey.c_str(), buf, size);
236 });
237 mBuffer.prune(read);
238 }
239};
240
241
270 public:
277 NVSFileSystem() : mInitialized(false) {}
278
292 bool begin(bool formatOnFail = false) {
293 if (mInitialized) return true;
294
295 mInitialized = mPrefs.begin("uniot_files", false);
296 if (!mInitialized) {
297 UNIOT_LOG_ERROR("failed to open NVS namespace 'uniot_files'");
298 }
299
300 return mInitialized;
301 }
302
309 void end() {
310 if (mInitialized) {
311 mPrefs.end();
312 mInitialized = false;
313 }
314 }
315
332 NVSFile open(const String& path, const String& mode) {
333 if (!mInitialized) {
334 UNIOT_LOG_WARN("NVS not initialized");
335 return NVSFile("", "", mPrefs);
336 }
337 return NVSFile(path, mode, mPrefs);
338 }
339
353 bool remove(const String& path) {
354 if (!mInitialized) {
355 return false;
356 }
357
358 String key = NVSFile::normalizePath(path);
359 return mPrefs.remove(key.c_str());
360 }
361
362 private:
363 Preferences mPrefs;
364 bool mInitialized;
365};
366
367
379extern NVSFileSystem NVSFS;
380
381} // namespace uniot
382
383#else
384#error "NVSFS is only supported on ESP32. Please define UNIOT_USE_NVSFS=1 for ESP32 projects."
385#endif
Definition Bytes.h:38
Definition NVSFS.h:60
size_t write(const uint8_t *data, size_t size)
Write data to the file buffer.
Definition NVSFS.h:111
static String normalizePath(const String &path)
Normalize a file path to a valid NVS key.
Definition NVSFS.h:183
Bytes getBytes()
Get the entire file content as Bytes object.
Definition NVSFS.h:132
void close()
Close the file and commit any pending write operations.
Definition NVSFS.h:153
NVSFile(const String &path, const String &mode, Preferences &prefs)
Constructs an NVSFile object for a specific path and mode.
Definition NVSFS.h:76
NVSFileSystem()
Default constructor.
Definition NVSFS.h:277
bool remove(const String &path)
Remove a file from NVS storage.
Definition NVSFS.h:353
void end()
Deinitialize the NVS file system.
Definition NVSFS.h:309
bool begin(bool formatOnFail=false)
Initialize the NVS file system.
Definition NVSFS.h:292
NVSFile open(const String &path, const String &mode)
Open a file for reading or writing.
Definition NVSFS.h:332
#define UNIOT_LOG_WARN(...)
Log an WARN level message Used for warnings about potentially problematic situations....
Definition Logger.h:294
#define UNIOT_LOG_ERROR(...)
Log an ERROR level message Used for critical errors that may prevent normal operation....
Definition Logger.h:273
Contains all classes and functions related to the Uniot Core.
NVSFileSystem NVSFS
Global NVS file system instance.
Definition NVSFS.cpp:5