1 #ifndef NVS_HANDLE_HPP_
2 #define NVS_HANDLE_HPP_
3
4 #include <string>
5 #include <memory>
6 #include <type_traits>
7
8 #include "nvs.h"
9
10 namespace nvs {
11
12 /**
13 * The possible blob types. This is a helper definition for template functions.
14 */
15 enum class ItemType : uint8_t {
16 U8 = NVS_TYPE_U8,
17 I8 = NVS_TYPE_I8,
18 U16 = NVS_TYPE_U16,
19 I16 = NVS_TYPE_I16,
20 U32 = NVS_TYPE_U32,
21 I32 = NVS_TYPE_I32,
22 U64 = NVS_TYPE_U64,
23 I64 = NVS_TYPE_I64,
24 SZ = NVS_TYPE_STR,
25 BLOB = 0x41,
26 BLOB_DATA = NVS_TYPE_BLOB,
27 BLOB_IDX = 0x48,
28 ANY = NVS_TYPE_ANY
29 };
30
31
32 /**
33 * @brief A handle allowing nvs-entry related operations on the NVS.
34 *
35 * @note The scope of this handle may vary depending on the implementation, but normally would be the namespace of
36 * a particular partition. Outside that scope, nvs entries can't be accessed/altered.
37 */
38 class NVSHandle {
39 public:
~NVSHandle()40 virtual ~NVSHandle() { }
41
42 /**
43 * @brief set value for given key
44 *
45 * Sets value for key. Note that physical storage will not be updated until nvs_commit function is called.
46 *
47 * @param[in] key Key name. Maximal length is (NVS_KEY_NAME_MAX_SIZE-1) characters. Shouldn't be empty.
48 * @param[in] value The value to set. Allowed types are the ones declared in ItemType as well as enums.
49 * For strings, the maximum length (including null character) is
50 * 4000 bytes, if there is one complete page free for writing.
51 * This decreases, however, if the free space is fragmented.
52 * Note that enums loose their type information when stored in NVS. Ensure that the correct
53 * enum type is used during retrieval with \ref get_item!
54 *
55 * @return
56 * - ESP_OK if value was set successfully
57 * - ESP_ERR_NVS_READ_ONLY if storage handle was opened as read only
58 * - ESP_ERR_NVS_INVALID_NAME if key name doesn't satisfy constraints
59 * - ESP_ERR_NVS_NOT_ENOUGH_SPACE if there is not enough space in the
60 * underlying storage to save the value
61 * - ESP_ERR_NVS_REMOVE_FAILED if the value wasn't updated because flash
62 * write operation has failed. The value was written however, and
63 * update will be finished after re-initialization of nvs, provided that
64 * flash operation doesn't fail again.
65 * - ESP_ERR_NVS_VALUE_TOO_LONG if the string value is too long
66 */
67 template<typename T>
68 esp_err_t set_item(const char *key, T value);
69 virtual
70 esp_err_t set_string(const char *key, const char* value) = 0;
71
72 /**
73 * @brief get value for given key
74 *
75 * These functions retrieve value for the key, given its name. If key does not
76 * exist, or the requested variable type doesn't match the type which was used
77 * when setting a value, an error is returned.
78 *
79 * In case of any error, out_value is not modified.
80 *
81 * @param[in] key Key name. Maximal length is (NVS_KEY_NAME_MAX_SIZE-1) characters. Shouldn't be empty.
82 * @param value The output value. All integral types which are declared in ItemType as well as enums
83 * are allowed. Note however that enums lost their type information when stored in NVS.
84 * Ensure that the correct enum type is used during retrieval with \ref get_item!
85 *
86 * @return
87 * - ESP_OK if the value was retrieved successfully
88 * - ESP_ERR_NVS_NOT_FOUND if the requested key doesn't exist
89 * - ESP_ERR_NVS_INVALID_NAME if key name doesn't satisfy constraints
90 * - ESP_ERR_NVS_INVALID_LENGTH if length is not sufficient to store data
91 */
92 template<typename T>
93 esp_err_t get_item(const char *key, T &value);
94
95 /**
96 * @brief set variable length binary value for given key
97 *
98 * This family of functions set value for the key, given its name. Note that
99 * actual storage will not be updated until nvs_commit function is called.
100 *
101 * @param[in] key Key name. Maximal length is (NVS_KEY_NAME_MAX_SIZE-1) characters. Shouldn't be empty.
102 * @param[in] blob The blob value to set.
103 * @param[in] len length of binary value to set, in bytes; Maximum length is
104 * 508000 bytes or (97.6% of the partition size - 4000) bytes
105 * whichever is lower.
106 *
107 * @return
108 * - ESP_OK if value was set successfully
109 * - ESP_ERR_NVS_READ_ONLY if storage handle was opened as read only
110 * - ESP_ERR_NVS_INVALID_NAME if key name doesn't satisfy constraints
111 * - ESP_ERR_NVS_NOT_ENOUGH_SPACE if there is not enough space in the
112 * underlying storage to save the value
113 * - ESP_ERR_NVS_REMOVE_FAILED if the value wasn't updated because flash
114 * write operation has failed. The value was written however, and
115 * update will be finished after re-initialization of nvs, provided that
116 * flash operation doesn't fail again.
117 * - ESP_ERR_NVS_VALUE_TOO_LONG if the value is too long
118 *
119 * @note compare to \ref nvs_set_blob in nvs.h
120 */
121 virtual esp_err_t set_blob(const char *key, const void* blob, size_t len) = 0;
122
123 /**
124 * @brief get value for given key
125 *
126 * These functions retrieve the data of an entry, given its key. If key does not
127 * exist, or the requested variable type doesn't match the type which was used
128 * when setting a value, an error is returned.
129 *
130 * In case of any error, out_value is not modified.
131 *
132 * Both functions expect out_value to be a pointer to an already allocated variable
133 * of the given type.
134 *
135 * It is suggested that nvs_get/set_str is used for zero-terminated short C strings, and
136 * nvs_get/set_blob is used for arbitrary data structures and long C strings.
137 *
138 * @param[in] key Key name. Maximum length is (NVS_KEY_NAME_MAX_SIZE-1) characters. Shouldn't be empty.
139 * @param out_str/ Pointer to the output value.
140 * out_blob
141 * @param[inout] len The length of the output buffer pointed to by out_str/out_blob.
142 * Use \c get_item_size to query the size of the item beforehand.
143 *
144 * @return
145 * - ESP_OK if the value was retrieved successfully
146 * - ESP_ERR_NVS_NOT_FOUND if the requested key doesn't exist
147 * - ESP_ERR_NVS_INVALID_NAME if key name doesn't satisfy constraints
148 * - ESP_ERR_NVS_INVALID_LENGTH if length is not sufficient to store data
149 */
150 virtual esp_err_t get_string(const char *key, char* out_str, size_t len) = 0;
151 virtual esp_err_t get_blob(const char *key, void* out_blob, size_t len) = 0;
152
153 /**
154 * @brief Look up the size of an entry's data.
155 *
156 * @param[in] datatype Data type to search for.
157 * @param[in] key Key name. Maximum length is (NVS_KEY_NAME_MAX_SIZE-1) characters. Shouldn't be empty.
158 * @param[out] size Size of the item, if it exists.
159 * For strings, this size includes the zero terminator.
160 *
161 * @return - ESP_OK if the item with specified type and key exists. Its size will be returned via \c size.
162 * - ESP_ERR_NVS_NOT_FOUND if an item with the requested key and type doesn't exist or any other
163 * error occurs.
164 */
165 virtual esp_err_t get_item_size(ItemType datatype, const char *key, size_t &size) = 0;
166
167 /**
168 * @brief Erases an entry.
169 */
170 virtual esp_err_t erase_item(const char* key) = 0;
171
172 /**
173 * Erases all entries in the scope of this handle. The scope may vary, depending on the implementation.
174 *
175 * @not If you want to erase the whole nvs flash (partition), refer to \ref
176 */
177 virtual esp_err_t erase_all() = 0;
178
179 /**
180 * Commits all changes done through this handle so far.
181 * Currently, NVS writes to storage right after the set and get functions,
182 * but this is not guaranteed.
183 */
184 virtual esp_err_t commit() = 0;
185
186 /**
187 * @brief Calculate all entries in the scope of the handle.
188 *
189 * @param[out] used_entries Returns amount of used entries from a namespace on success.
190 *
191 *
192 * @return
193 * - ESP_OK if the changes have been written successfully.
194 * Return param used_entries will be filled valid value.
195 * - ESP_ERR_NVS_NOT_INITIALIZED if the storage driver is not initialized.
196 * Return param used_entries will be filled 0.
197 * - ESP_ERR_INVALID_ARG if nvs_stats equal to NULL.
198 * - Other error codes from the underlying storage driver.
199 * Return param used_entries will be filled 0.
200 */
201 virtual esp_err_t get_used_entry_count(size_t& usedEntries) = 0;
202
203 protected:
204 virtual esp_err_t set_typed_item(ItemType datatype, const char *key, const void* data, size_t dataSize) = 0;
205
206 virtual esp_err_t get_typed_item(ItemType datatype, const char *key, void* data, size_t dataSize) = 0;
207 };
208
209 /**
210 * @brief Opens non-volatile storage and returns a handle object.
211 *
212 * The handle is automatically closed on desctruction. The scope of the handle is the namespace ns_name
213 * in a particular partition partition_name.
214 * The parameters partition_name, ns_name and open_mode have the same meaning and restrictions as the parameters
215 * part_name, name and open_mode in \ref nvs_open_from_partition, respectively.
216 *
217 * @param err an optional pointer to an esp_err_t result of the open operation, having the same meaning as the return
218 * value in \ref nvs_open_from_partition:
219 * - ESP_OK if storage handle was opened successfully
220 * - ESP_ERR_NVS_NOT_INITIALIZED if the storage driver is not initialized
221 * - ESP_ERR_NVS_PART_NOT_FOUND if the partition with label "nvs" is not found
222 * - ESP_ERR_NVS_NOT_FOUND id namespace doesn't exist yet and
223 * mode is NVS_READONLY
224 * - ESP_ERR_NVS_INVALID_NAME if namespace name doesn't satisfy constraints
225 * - other error codes from the underlying storage driver
226 *
227 * @return shared pointer of an nvs handle on success, an empty shared pointer otherwise
228 */
229 std::unique_ptr<NVSHandle> open_nvs_handle_from_partition(const char *partition_name,
230 const char *ns_name,
231 nvs_open_mode_t open_mode,
232 esp_err_t *err = nullptr);
233
234 /**
235 * @brief This function does the same as \ref open_nvs_handle_from_partition but uses the default nvs partition
236 * instead of a partition_name parameter.
237 */
238 std::unique_ptr<NVSHandle> open_nvs_handle(const char *ns_name,
239 nvs_open_mode_t open_mode,
240 esp_err_t *err = nullptr);
241
242 // Helper functions for template usage
243 /**
244 * Help to translate all integral types into ItemType.
245 */
246 template<typename T, typename std::enable_if<std::is_integral<T>::value, void*>::type = nullptr>
itemTypeOf()247 constexpr ItemType itemTypeOf()
248 {
249 return static_cast<ItemType>(((std::is_signed<T>::value)?0x10:0x00) | sizeof(T));
250 }
251
252 /**
253 * Help to translate all enum types into integral ItemType.
254 */
255 template<typename T, typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
itemTypeOf()256 constexpr ItemType itemTypeOf()
257 {
258 return static_cast<ItemType>(((std::is_signed<T>::value)?0x10:0x00) | sizeof(T));
259 }
260
261 template<typename T>
itemTypeOf(const T &)262 constexpr ItemType itemTypeOf(const T&)
263 {
264 return itemTypeOf<T>();
265 }
266
267 // Template Implementations
268 template<typename T>
set_item(const char * key,T value)269 esp_err_t NVSHandle::set_item(const char *key, T value) {
270 return set_typed_item(itemTypeOf(value), key, &value, sizeof(value));
271 }
272
273 template<typename T>
get_item(const char * key,T & value)274 esp_err_t NVSHandle::get_item(const char *key, T &value) {
275 return get_typed_item(itemTypeOf(value), key, &value, sizeof(value));
276 }
277
278 } // nvs
279
280 #endif // NVS_HANDLE_HPP_
281