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