diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml index 1de1bc4c..5da349cf 100644 --- a/.github/workflows/compile-examples.yml +++ b/.github/workflows/compile-examples.yml @@ -95,6 +95,7 @@ jobs: - libraries/WiFiS3 - libraries/OTAUpdate - libraries/OPAMP + - libraries/Preferences - board: fqbn: "arduino-git:renesas:minima" additional-sketch-paths: | diff --git a/libraries/Preferences/examples/Prefs2Struct/Prefs2Struct.ino b/libraries/Preferences/examples/Prefs2Struct/Prefs2Struct.ino new file mode 100644 index 00000000..b8cc4dab --- /dev/null +++ b/libraries/Preferences/examples/Prefs2Struct/Prefs2Struct.ino @@ -0,0 +1,56 @@ +/* +This example shows how to use Preferences (nvs) to store a +structure. Note that the maximum size of a putBytes is 496K +or 97% of the nvs partition size. nvs has signifcant overhead, +so should not be used for data that will change often. +*/ +#include +Preferences prefs; + +typedef struct { + uint8_t hour; + uint8_t minute; + uint8_t setting1; + uint8_t setting2; +} schedule_t; + +void setup() { + Serial.begin(115200); + + if (!prefs.begin("schedule")) { // use "schedule" namespace + Serial.println("Cannot initialize preferences"); + Serial.println("Make sure your WiFi firmware version is greater than 0.3.0"); + while(1) {}; + } + uint8_t content[] = {9, 30, 235, 255, 20, 15, 0, 1}; // two entries + prefs.putBytes("schedule", content, sizeof(content)); + size_t schLen = prefs.getBytesLength("schedule"); + char buffer[schLen]; // prepare a buffer for the data + prefs.getBytes("schedule", buffer, schLen); + if (schLen % sizeof(schedule_t)) { // simple check that data fits + Serial.println("Data is not correct size!"); + return; + } + schedule_t *schedule = (schedule_t *) buffer; // cast the bytes into a struct ptr + Serial.print(schedule[1].hour); + Serial.print(":"); + Serial.print(schedule[1].minute); + Serial.print(" "); + Serial.print(schedule[1].setting1); + Serial.print("/"); + Serial.print(schedule[1].setting2); + Serial.println(); + + schedule[2] = {8, 30, 20, 21}; // add a third entry (unsafely) + + // force the struct array into a byte array + prefs.putBytes("schedule", schedule, 3*sizeof(schedule_t)); + schLen = prefs.getBytesLength("schedule"); + char buffer2[schLen]; + prefs.getBytes("schedule", buffer2, schLen); + for (int x=0; x + +Preferences preferences; + +void setup() { + Serial.begin(115200); + Serial.println(); + + // Open Preferences with my-app namespace. Each application module, library, etc + // has to use a namespace name to prevent key name collisions. We will open storage in + // RW-mode (second parameter has to be false). + // Note: Namespace name is limited to 15 chars. + if (!preferences.begin("my-app", false)) { + Serial.println("Cannot initialize preferences"); + Serial.println("Make sure your WiFi firmware version is greater than 0.3.0"); + while(1) {}; + } + + // Remove all preferences under the opened namespace + //preferences.clear(); + + // Or remove the counter key only + //preferences.remove("counter"); + + // Get the counter value, if the key does not exist, return a default value of 0 + // Note: Key name is limited to 15 chars. + unsigned int counter = preferences.getUInt("counter", 0); + + // Increase counter by 1 + counter++; + + // Print the counter to Serial Monitor + Serial.print("Current counter value: "); + Serial.println(counter); + + // Store the counter to the Preferences + preferences.putUInt("counter", counter); + + // Close the Preferences + preferences.end(); + + // Wait 10 seconds + Serial.println("Restarting in 10 seconds..."); + delay(10000); + + // Reset + NVIC_SystemReset(); +} + +void loop() {} diff --git a/libraries/Preferences/keywords.txt b/libraries/Preferences/keywords.txt new file mode 100644 index 00000000..fe2d4333 --- /dev/null +++ b/libraries/Preferences/keywords.txt @@ -0,0 +1,54 @@ +####################################### +# Syntax Coloring Map NVS +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +Preferences KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +begin KEYWORD2 +end KEYWORD2 + +clear KEYWORD2 +remove KEYWORD2 + +putChar KEYWORD2 +putUChar KEYWORD2 +putShort KEYWORD2 +putUShort KEYWORD2 +putInt KEYWORD2 +putUInt KEYWORD2 +putLong KEYWORD2 +putULong KEYWORD2 +putLong64 KEYWORD2 +putULong64 KEYWORD2 +putFloat KEYWORD2 +putDouble KEYWORD2 +putBool KEYWORD2 +putString KEYWORD2 +putBytes KEYWORD2 + +getChar KEYWORD2 +getUChar KEYWORD2 +getShort KEYWORD2 +getUShort KEYWORD2 +getInt KEYWORD2 +getUInt KEYWORD2 +getLong KEYWORD2 +getULong KEYWORD2 +getLong64 KEYWORD2 +getULong64 KEYWORD2 +getFloat KEYWORD2 +getDouble KEYWORD2 +getBool KEYWORD2 +getString KEYWORD2 +getBytes KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/libraries/Preferences/library.properties b/libraries/Preferences/library.properties new file mode 100644 index 00000000..4c68c133 --- /dev/null +++ b/libraries/Preferences/library.properties @@ -0,0 +1,9 @@ +name=Preferences +version=2.0.0 +author=Hristo Gochkov +maintainer=Hristo Gochkov +sentence=Provides friendly access to ESP32's Non-Volatile Storage +paragraph= +category=Data Storage +url= +architectures=renesas,renesas_uno diff --git a/libraries/Preferences/src/Preferences.cpp b/libraries/Preferences/src/Preferences.cpp new file mode 100644 index 00000000..9bdac488 --- /dev/null +++ b/libraries/Preferences/src/Preferences.cpp @@ -0,0 +1,361 @@ +// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "Preferences.h" + + +using namespace std; + +Preferences::Preferences() { + +} + +Preferences::~Preferences() { + end(); +} + +bool Preferences::begin(const char * name, bool readOnly, const char* partition_label){ + string res = ""; + modem.begin(); + if (name != nullptr && strlen(name) > 0) { + if (modem.write(string(PROMPT(_PREF_BEGIN)), res, "%s%s,%d,%s\r\n", CMD_WRITE(_PREF_BEGIN), name, readOnly, partition_label != NULL ? partition_label : "")) { + return (atoi(res.c_str()) != 0) ? true : false; + } + } + return false; +} + +void Preferences::end() { + string res = ""; + modem.write(string(PROMPT(_PREF_END)), res, "%s", CMD(_PREF_END)); +} + +/* + * Clear all keys in opened preferences + * */ + +bool Preferences::clear() { + string res = ""; + if (modem.write(string(PROMPT(_PREF_CLEAR)), res, "%s", CMD(_PREF_CLEAR))) { + return (atoi(res.c_str()) != 0) ? true : false; + } + return false; +} + +/* + * Remove a key + * */ + +bool Preferences::remove(const char * key) { + string res = ""; + if (key != nullptr && strlen(key) > 0) { + if (modem.write(string(PROMPT(_PREF_REMOVE)), res, "%s%s\r\n", CMD_WRITE(_PREF_REMOVE), key)) { + return (atoi(res.c_str()) != 0) ? true : false; + } + } + return false; +} + +/* + * Put a key value + * */ + +size_t Preferences::putChar(const char* key, int8_t value) { + string res = ""; + if (key != nullptr && strlen(key) > 0) { + if (modem.write(string(PROMPT(_PREF_PUT)), res, "%s%s,%d,%hd\r\n", CMD_WRITE(_PREF_PUT), key, PT_I8, value)) { + return atoi(res.c_str()); + } + } + return 0; +} + +size_t Preferences::putUChar(const char* key, uint8_t value) { + string res = ""; + if (key != nullptr && strlen(key) > 0) { + if (modem.write(string(PROMPT(_PREF_PUT)), res, "%s%s,%d,%hu\r\n", CMD_WRITE(_PREF_PUT), key, PT_U8, value)) { + return atoi(res.c_str()); + } + } + return 0; +} + +size_t Preferences::putShort(const char* key, int16_t value) { + string res = ""; + if (key != nullptr && strlen(key) > 0) { + if (modem.write(string(PROMPT(_PREF_PUT)), res, "%s%s,%d,%hd\r\n", CMD_WRITE(_PREF_PUT), key, PT_I16, value)) { + return atoi(res.c_str()); + } + } + return 0; +} + +size_t Preferences::putUShort(const char* key, uint16_t value) { + string res = ""; + if (key != nullptr && strlen(key) > 0) { + if (modem.write(string(PROMPT(_PREF_PUT)), res, "%s%s,%d,%hu\r\n", CMD_WRITE(_PREF_PUT), key, PT_U16, value)) { + return atoi(res.c_str()); + } + } + return 0; +} + +size_t Preferences::putInt(const char* key, int32_t value) { + string res = ""; + if (key != nullptr && strlen(key) > 0) { + if (modem.write(string(PROMPT(_PREF_PUT)), res, "%s%s,%d,%d\r\n", CMD_WRITE(_PREF_PUT), key, PT_I32, value)) { + return atoi(res.c_str()); + } + } + return 0; +} + +size_t Preferences::putUInt(const char* key, uint32_t value) { + string res = ""; + if (key != nullptr && strlen(key) > 0) { + if (modem.write(string(PROMPT(_PREF_PUT)), res, "%s%s,%d,%u\r\n", CMD_WRITE(_PREF_PUT), key, PT_U32, value)) { + return atoi(res.c_str()); + } + } + return 0; +} + +size_t Preferences::putLong(const char* key, int32_t value) { + return putInt(key, value); +} + +size_t Preferences::putULong(const char* key, uint32_t value) { + return putUInt(key, value); +} + +size_t Preferences::putLong64(const char* key, int64_t value) { + return putBytes(key, (void*)&value, sizeof(int64_t)); +} + +size_t Preferences::putULong64(const char* key, uint64_t value) { + return putBytes(key, (void*)&value, sizeof(uint64_t)); +} + +size_t Preferences::putFloat(const char* key, const float_t value) { + return putBytes(key, (void*)&value, sizeof(float_t)); +} + +size_t Preferences::putDouble(const char* key, const double_t value) { + return putBytes(key, (void*)&value, sizeof(double_t)); +} + +size_t Preferences::putBool(const char* key, const bool value) { + return putUChar(key, (uint8_t) (value ? 1 : 0)); +} + +size_t Preferences::putString(const char* key, const char* value) { + string res = ""; + if (key != nullptr && strlen(key) > 0 && value != nullptr && strlen(value) > 0) { + if (modem.write(string(PROMPT(_PREF_PUT)), res, "%s%s,%d,%s\r\n", CMD_WRITE(_PREF_PUT), key, PT_STR, value)) { + return atoi(res.c_str()); + } + } + return 0; +} + +size_t Preferences::putString(const char* key, const String value) { + return putString(key, value.c_str()); +} + +size_t Preferences::putBytes(const char* key, const void* value, size_t len) { + string res = ""; + if ( key != nullptr && strlen(key) > 0 && value != nullptr && len > 0) { + modem.write_nowait(string(PROMPT(_PREF_PUT)), res, "%s%s,%d,%d\r\n", CMD_WRITE(_PREF_PUT), key, PT_BLOB, len); + if(modem.passthrough((uint8_t *)value, len)) { + return len; + } + } + return 0; +} + +PreferenceType Preferences::getType(const char* key) { + string res = ""; + if (key != nullptr && strlen(key) > 0) { + if (modem.write(string(PROMPT(_PREF_PUT)), res, "%s%s\r\n", CMD_WRITE(_PREF_PUT), key)) { + return static_cast(atoi(res.c_str())); + } + } + return PT_INVALID; +} + +bool Preferences::isKey(const char* key) { + return getType(key) != PT_INVALID; +} + +/* + * Get a key value + * */ + +int8_t Preferences::getChar(const char* key, const int8_t defaultValue) { + int16_t value = defaultValue; + string res = ""; + if (key != nullptr && strlen(key) > 0) { + if (modem.write(string(PROMPT(_PREF_GET)), res, "%s%s,%d,%hd\r\n", CMD_WRITE(_PREF_GET), key, PT_I8, defaultValue)) { + sscanf(res.c_str(), "%hd", &value); + } + } + return value; +} + +uint8_t Preferences::getUChar(const char* key, const uint8_t defaultValue) { + uint16_t value = defaultValue; + string res = ""; + if (key != nullptr && strlen(key) > 0) { + if (modem.write(string(PROMPT(_PREF_GET)), res, "%s%s,%d,%hu\r\n", CMD_WRITE(_PREF_GET), key, PT_U8, defaultValue)) { + sscanf(res.c_str(), "%hu", &value); + } + } + return value; +} + +int16_t Preferences::getShort(const char* key, const int16_t defaultValue) { + int16_t value = defaultValue; + string res = ""; + if (key != nullptr && strlen(key) > 0) { + if (modem.write(string(PROMPT(_PREF_GET)), res, "%s%s,%d,%hd\r\n", CMD_WRITE(_PREF_GET), key, PT_I16, defaultValue)) { + sscanf(res.c_str(), "%hd", &value); + } + } + return value; +} + +uint16_t Preferences::getUShort(const char* key, const uint16_t defaultValue) { + uint16_t value = defaultValue; + string res = ""; + if (key != nullptr && strlen(key) > 0) { + if (modem.write(string(PROMPT(_PREF_GET)), res, "%s%s,%d,%hu\r\n", CMD_WRITE(_PREF_GET), key, PT_U16, defaultValue)) { + sscanf(res.c_str(), "%hu", &value); + } + } + return value; +} + +int32_t Preferences::getInt(const char* key, const int32_t defaultValue) { + int32_t value = defaultValue; + string res = ""; + if (key != nullptr && strlen(key) > 0) { + if (modem.write(string(PROMPT(_PREF_GET)), res, "%s%s,%d,%d\r\n", CMD_WRITE(_PREF_GET), key, PT_I32, defaultValue)) { + sscanf(res.c_str(), "%d", &value); + } + } + return value; +} + +uint32_t Preferences::getUInt(const char* key, const uint32_t defaultValue) { + uint32_t value = defaultValue; + string res = ""; + if (key != nullptr && strlen(key) > 0) { + if (modem.write(string(PROMPT(_PREF_GET)), res, "%s%s,%d,%u\r\n", CMD_WRITE(_PREF_GET), key, PT_U32, defaultValue)) { + sscanf(res.c_str(), "%u", &value); + } + } + return value; +} + +int32_t Preferences::getLong(const char* key, const int32_t defaultValue) { + return getInt(key, defaultValue); +} + +uint32_t Preferences::getULong(const char* key, const uint32_t defaultValue) { + return getUInt(key, defaultValue); +} + +int64_t Preferences::getLong64(const char* key, const int64_t defaultValue) { + int64_t value = defaultValue; + getBytes(key, (void*) &value, sizeof(int64_t)); + return value; +} + +uint64_t Preferences::getULong64(const char* key, const uint64_t defaultValue) { + uint64_t value = defaultValue; + getBytes(key, (void*) &value, sizeof(uint64_t)); + return value; +} + +float_t Preferences::getFloat(const char* key, const float_t defaultValue) { + float_t value = defaultValue; + getBytes(key, (void*) &value, sizeof(float_t)); + return value; +} + +double_t Preferences::getDouble(const char* key, const double_t defaultValue) { + double_t value = defaultValue; + getBytes(key, (void*) &value, sizeof(double_t)); + return value; +} + +bool Preferences::getBool(const char* key, const bool defaultValue) { + return getUChar(key, defaultValue ? 1 : 0) == 1; +} + +size_t Preferences::getString(const char* key, char* value, const size_t maxLen) { + string res = ""; + if (key != nullptr && strlen(key) > 0 && value != nullptr) { + if (modem.write(string(PROMPT(_PREF_GET)), res, "%s%s,%d\r\n", CMD_WRITE(_PREF_GET), key, PT_STR)) { + if (res.length() < maxLen) { + strncpy(value, res.c_str(), res.length()); + return res.length(); + } + } + } + return 0; +} + +String Preferences::getString(const char* key, const String defaultValue) { + string res = ""; + if (key != nullptr && strlen(key) > 0) { + if (modem.write(string(PROMPT(_PREF_GET)), res, "%s%s,%d,%s\r\n", CMD_WRITE(_PREF_GET), key, PT_STR, defaultValue.c_str())) { + return String(res.c_str()); + } + } + return defaultValue; +} + +size_t Preferences::getBytesLength(const char* key) { + string res = ""; + if (key != nullptr && strlen(key) > 0) { + if (modem.write(string(PROMPT(_PREF_LEN)), res, "%s%s\r\n", CMD_WRITE(_PREF_LEN), key)) { + return atoi(res.c_str()); + } + } + return 0; +} + +size_t Preferences::getBytes(const char* key, void * buf, size_t maxLen) { + size_t len = getBytesLength(key); + string res = ""; + if (key != nullptr && strlen(key) > 0 && buf != nullptr && len > 0) { + modem.avoid_trim_results(); + modem.read_using_size(); + if (modem.write(string(PROMPT(_PREF_GET)), res, "%s%s,%d\r\n", CMD_WRITE(_PREF_GET), key, PT_BLOB)) { + if (res.size() >= len && len <= maxLen) { + memcpy(buf, (uint8_t*)&res[0], len); + } + } + } + return 0; +} + +size_t Preferences::freeEntries() { + string res = ""; + if (modem.write(string(PROMPT(_PREF_STAT)), res, "%s", CMD(_PREF_STAT))) { + return atoi(res.c_str()); + } + return 0; +} diff --git a/libraries/Preferences/src/Preferences.h b/libraries/Preferences/src/Preferences.h new file mode 100644 index 00000000..a4f76223 --- /dev/null +++ b/libraries/Preferences/src/Preferences.h @@ -0,0 +1,70 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _PREFERENCES_H_ +#define _PREFERENCES_H_ + +#include "Arduino.h" +#include "Modem.h" + +typedef enum { + PT_I8, PT_U8, PT_I16, PT_U16, PT_I32, PT_U32, PT_I64, PT_U64, PT_STR, PT_BLOB, PT_INVALID +} PreferenceType; + +class Preferences { +public: + Preferences(); + ~Preferences(); + bool begin(const char * name, bool readOnly=false, const char* partition_label=NULL); + void end(); + bool clear(); + bool remove(const char * key); + size_t putChar(const char* key, int8_t value); + size_t putUChar(const char* key, uint8_t value); + size_t putShort(const char* key, int16_t value); + size_t putUShort(const char* key, uint16_t value); + size_t putInt(const char* key, int32_t value); + size_t putUInt(const char* key, uint32_t value); + size_t putLong(const char* key, int32_t value); + size_t putULong(const char* key, uint32_t value); + size_t putLong64(const char* key, int64_t value); + size_t putULong64(const char* key, uint64_t value); + size_t putFloat(const char* key, float_t value); + size_t putDouble(const char* key, double_t value); + size_t putBool(const char* key, bool value); + size_t putString(const char* key, const char* value); + size_t putString(const char* key, String value); + size_t putBytes(const char* key, const void* value, size_t len); + bool isKey(const char* key); + PreferenceType getType(const char* key); + int8_t getChar(const char* key, int8_t defaultValue = 0); + uint8_t getUChar(const char* key, uint8_t defaultValue = 0); + int16_t getShort(const char* key, int16_t defaultValue = 0); + uint16_t getUShort(const char* key, uint16_t defaultValue = 0); + int32_t getInt(const char* key, int32_t defaultValue = 0); + uint32_t getUInt(const char* key, uint32_t defaultValue = 0); + int32_t getLong(const char* key, int32_t defaultValue = 0); + uint32_t getULong(const char* key, uint32_t defaultValue = 0); + int64_t getLong64(const char* key, int64_t defaultValue = 0); + uint64_t getULong64(const char* key, uint64_t defaultValue = 0); + float_t getFloat(const char* key, float_t defaultValue = NAN); + double_t getDouble(const char* key, double_t defaultValue = NAN); + bool getBool(const char* key, bool defaultValue = false); + size_t getString(const char* key, char* value, size_t maxLen); + String getString(const char* key, String defaultValue = String()); + size_t getBytesLength(const char* key); + size_t getBytes(const char* key, void * buf, size_t maxLen); + size_t freeEntries(); +}; + +#endif