Skip to content

Commit

Permalink
feat(nvs): NVSHandle Wrapper (#257)
Browse files Browse the repository at this point in the history
* draft of nvs_handle wrapper to minimize opens, commits

* storing nvs_handle instance as a member variable

* renamed close_handle to commit to more accurately describe method

* added NVSHandle example app

* comments for NVSHandle methods

* support get/sets for bools with NVSHandle

* more precise printed error messages

* made check_lengths() and make_error_code() public for reuse in NVSHandle

* handle support for strings, string_view

* nvshandle example as section in main example app

* replaced printf with fmt::print

* minimized checks for constant ns_name in NVSHandle

* removed automatic restart

* extracted NVSHandle to new header file

* cleaned up formatting

* corrected include, removed redefinitions

* handle check before dereferencing with assoc. error code
  • Loading branch information
guraj authored Jun 18, 2024
1 parent 181b3d0 commit 8887bd4
Show file tree
Hide file tree
Showing 6 changed files with 446 additions and 71 deletions.
53 changes: 52 additions & 1 deletion components/nvs/example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,59 @@ idf.py -p PORT flash monitor

See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.

## Example Output
## NVS Example Output

![CleanShot 2024-05-18 at 12 54 46](https://github.com/esp-cpp/espp/assets/213467/60b2db2f-8796-4ae3-9a8c-51f69fa21911)
![CleanShot 2024-05-18 at 12 54 54](https://github.com/esp-cpp/espp/assets/213467/ddceddbf-0690-4590-93b6-66cf91ad1898)
![CleanShot 2024-05-18 at 12 55 02](https://github.com/esp-cpp/espp/assets/213467/1181fc79-f7bd-4b1d-b351-e5ca24ee7c55)

## NVSHandle Example Output

First run:
```
Opening Non-Volatile Storage (NVS) handle... Done
Reading restart counter from NVS ... [NVSHandle/E][0.061]: The value is not initialized in NVS, key = 'restart_counter'
The value is not initialized yet!
Updating restart counter in NVS ... Done
Committing updates in NVS ... Done
Restarting in 10 seconds...
Restarting in 9 seconds...
Restarting in 8 seconds...
Restarting in 7 seconds...
Restarting in 6 seconds...
Restarting in 5 seconds...
Restarting in 4 seconds...
Restarting in 3 seconds...
Restarting in 2 seconds...
Restarting in 1 seconds...
Restarting in 0 seconds...
Restarting now.
```

Subsequent runs:

```
Opening Non-Volatile Storage (NVS) handle... Done
Reading restart counter from NVS ... Done
Restart counter = 1
Updating restart counter in NVS ... Done
Committing updates in NVS ... Done
Restarting in 10 seconds...
Restarting in 9 seconds...
Restarting in 8 seconds...
Restarting in 7 seconds...
Restarting in 6 seconds...
Restarting in 5 seconds...
Restarting in 4 seconds...
Restarting in 3 seconds...
Restarting in 2 seconds...
Restarting in 1 seconds...
Restarting in 0 seconds...
Restarting now.
```

Restart counter will increment on each run.

To reset the counter, erase the contents of flash memory using `idf.py erase-flash`, then upload the program again as described above.
195 changes: 126 additions & 69 deletions components/nvs/example/main/nvs_example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,87 +5,144 @@

#include "format.hpp"
#include "nvs.hpp"
#include "nvs_handle_espp.hpp"

using namespace std::chrono_literals;

extern "C" void app_main(void) {
std::this_thread::sleep_for(200ms);
fmt::print("Starting NVS example!\n");

//! [nvs example]
std::error_code ec;
uint8_t counter = 0;
espp::Nvs nvs;
nvs.init(ec);
ec.clear();
// note that the namespace and key strings must be <= 15 characters
nvs.get_or_set_var("system", "reset_counter", counter, counter, ec);
ec.clear();
fmt::print("Reset Counter = {}\n", counter);

bool flag = false;
nvs.get_or_set_var("other-namespace", "flag", flag, flag, ec);
ec.clear();
fmt::print("Got / Set Flag = {}\n", flag);
// now toggle the flag
flag = !flag;
nvs.set_var("other-namespace", "flag", flag, ec);
ec.clear();
fmt::print("Toggled Flag = {}\n", flag);

// test protection against really long namespace names (length > 15)
std::string long_ns(16, 'a');
nvs.get_or_set_var(long_ns, "flag", flag, flag, ec);
if (ec) {
fmt::print("Expected error: {}\n", ec.message());
} else {
fmt::print("Unexpected success\n");
}
ec.clear();

// test getting a non-existent key
nvs.get_var("system", "not-here", counter, ec);
if (ec) {
fmt::print("Expected error: {}\n", ec.message());
} else {
fmt::print("Unexpected success\n");
}
ec.clear();

// test getting a string value
std::string str;
nvs.get_or_set_var("system", "string", str, std::string("default"), ec);
if (ec) {
fmt::print("Error: {}\n", ec.message());
} else {
fmt::print("String = {}\n", str);
}
ec.clear();

// test setting a string value
str = "hello";
nvs.set_var("system", "string", str, ec);
if (ec) {
fmt::print("Error: {}\n", ec.message());
} else {
fmt::print("String set to '{}'\n", str);
}
ec.clear();
{
//! [nvs example]
std::error_code ec;
uint8_t counter = 0;
espp::Nvs nvs;
nvs.init(ec);
ec.clear();
// note that the namespace and key strings must be <= 15 characters
nvs.get_or_set_var("system", "reset_counter", counter, counter, ec);
ec.clear();
fmt::print("Reset Counter = {}\n", counter);

bool flag = false;
nvs.get_or_set_var("other-namespace", "flag", flag, flag, ec);
ec.clear();
fmt::print("Got / Set Flag = {}\n", flag);
// now toggle the flag
flag = !flag;
nvs.set_var("other-namespace", "flag", flag, ec);
ec.clear();
fmt::print("Toggled Flag = {}\n", flag);

counter++;
// test protection against really long namespace names (length > 15)
std::string long_ns(16, 'a');
nvs.get_or_set_var(long_ns, "flag", flag, flag, ec);
if (ec) {
fmt::print("Expected error: {}\n", ec.message());
} else {
fmt::print("Unexpected success\n");
}
ec.clear();

if (counter > 10) {
nvs.erase(ec);
nvs.init(ec);
counter = 0;
fmt::print("NVS erased, Reset Counter set to 0\n");
// test getting a non-existent key
nvs.get_var("system", "not-here", counter, ec);
if (ec) {
fmt::print("Expected error: {}\n", ec.message());
} else {
fmt::print("Unexpected success\n");
}
ec.clear();

// test getting a string value
std::string str;
nvs.get_or_set_var("system", "string", str, std::string("default"), ec);
if (ec) {
fmt::print("Error: {}\n", ec.message());
} else {
fmt::print("String = {}\n", str);
}
ec.clear();

// test setting a string value
str = "hello";
nvs.set_var("system", "string", str, ec);
if (ec) {
fmt::print("Error: {}\n", ec.message());
} else {
fmt::print("String set to '{}'\n", str);
}
ec.clear();

counter++;

if (counter > 10) {
nvs.erase(ec);
nvs.init(ec);
counter = 0;
fmt::print("NVS erased, Reset Counter set to 0\n");
ec.clear();
}

nvs.set_var("system", "reset_counter", counter, ec);
fmt::print("Next Reset Counter will be = {}\n", counter);
fmt::print("NVS example complete!\n");
//! [nvs example]
}

nvs.set_var("system", "reset_counter", counter, ec);
fmt::print("Next Reset Counter will be = {}\n", counter);
fmt::print("NVS example complete!\n");
//! [nvs example]
{
//! [nvshandle example]
// Init NVS
std::error_code ec;
espp::Nvs nvs;
nvs.init(ec);
ec.clear();

// Open
fmt::print("\nOpening Non-Volatile Storage (NVS) handle... ");
// Handle will automatically close when going out of scope or when it's reset.
espp::NVSHandle storage("storage", ec);
fmt::print("Done\n");
ec.clear();

// Read
fmt::print("Reading restart counter from NVS ... ");
int32_t restart_counter = 0;
storage.get("restart_counter", restart_counter, ec);
if (ec) {
if (ec.value() == static_cast<int>(NvsErrc::Key_Not_Found)) {
fmt::print("The value is not initialized yet!\n");
} else if (ec.value() == static_cast<int>(NvsErrc::Read_NVS_Failed)) {
fmt::print("Failed to read from NVS: {}\n", ec.message().c_str());
} else {
fmt::print("An error occurred: {}\n", ec.message().c_str());
}
} else {
fmt::print("Done\n");
fmt::print("Restart counter = {}\n", restart_counter);
}
ec.clear();

// Write
fmt::print("Updating restart counter in NVS ... ");
restart_counter++;
storage.set("restart_counter", restart_counter, ec);
fmt::print("Done\n");
ec.clear();

// Commit written value.
// After setting any values, nvs_commit() must be called to ensure changes are written
// to flash storage. Implementations may write to storage at other times,
// but this is not guaranteed.
fmt::print("Committing updates in NVS ... ");
storage.commit(ec);
fmt::print("Done\n");
ec.clear();

fmt::print("\n");
fflush(stdout);
//! [nvshandle example]
}

while (true) {
std::this_thread::sleep_for(1s);
Expand Down
4 changes: 4 additions & 0 deletions components/nvs/include/nvs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ enum class NvsErrc {
Key_Not_Found,
Init_NVS_Failed,
Erase_NVS_Failed,
Handle_Uninitialized,
};

struct NvsErrCategory : std::error_category {
Expand Down Expand Up @@ -61,6 +62,9 @@ std::string NvsErrCategory::message(int ev) const {
case NvsErrc::Erase_NVS_Failed:
return "Failed to erase NVS";

case NvsErrc::Handle_Uninitialized:
return "Handle not initialized";

default:
return "(unrecognized error)";
}
Expand Down
Loading

0 comments on commit 8887bd4

Please sign in to comment.