├── .clang-format ├── .gitignore ├── .github ├── workflows │ ├── platformio.yml │ └── Arduino.yml └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── platformio.ini ├── library.properties ├── CONTRIBUTING.md ├── keywords.txt ├── LICENSE ├── src ├── feature_supported_types.h ├── feature_safe_set.h ├── FeatureVariables.h ├── feature_internal.h ├── feature_safe_get.h ├── feature_macros.h ├── feature_safe_cast.h ├── feature_base.h └── feature_impl.h ├── examples ├── Quick_Start │ └── Quick_Start.ino ├── Delayed_Saving │ └── Delayed_Saving.ino ├── Using_ArduinoJson │ └── Using_ArduinoJson.ino ├── Alternate_Set_Methods │ └── Alternate_Set_Methods.ino ├── Basic_Use │ └── Basic_Use.ino ├── Alternate_Get_Methods │ └── Alternate_Get_Methods.ino ├── Type_Testing │ └── Type_Testing.ino └── Container_Example │ └── Container_Example.ino ├── CODE_OF_CONDUCT.md └── README.md /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | IndentWidth: 2 3 | TabWidth: 2 4 | ColumnLimit: 0 5 | AccessModifierOffset: -1 6 | AllowShortIfStatementsOnASingleLine: true 7 | IndentCaseLabels: false 8 | NamespaceIndentation: All 9 | 10 | PointerAlignment : Left 11 | AlignConsecutiveDeclarations : true 12 | AlignAfterOpenBracket : true 13 | 14 | SortIncludes : false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | 35 | /.vscode 36 | /.pio -------------------------------------------------------------------------------- /.github/workflows/platformio.yml: -------------------------------------------------------------------------------- 1 | name: PlatformIO CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Set up Python 13 | uses: actions/setup-python@v1 14 | - name: Install dependencies 15 | run: | 16 | python -m pip install --upgrade pip 17 | pip install platformio 18 | - name: Run PlatformIO 19 | run: platformio run 20 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | [platformio] 2 | src_dir = examples/Basic_Use 3 | lib_dir = . 4 | test_dir = test 5 | 6 | [common] 7 | lib_deps = 8 | ArduinoJson 9 | Effortless-SPIFFS 10 | 11 | [env:nodemcuv2] 12 | platform = espressif8266 13 | board = nodemcuv2 14 | framework = arduino 15 | monitor_speed = 115200 16 | lib_deps = 17 | ${common.lib_deps} 18 | 19 | [env:ESP32] 20 | platform = espressif32 21 | framework = arduino 22 | board = esp32dev 23 | lib_deps = 24 | ${common.lib_deps} -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Feature-Variables 2 | version=1.0.0 3 | author=thebigpotatoe 4 | maintainer=thebigpotatoe 5 | sentence=Persistent event driven variables aimed at making complex designs much simpler 6 | paragraph=Feature Variables are designed to be persistent event driven variables that not only offer the standard method of working with variables, but also handle the complexities of storing and checking variables a thing of the past. 7 | category=Data Storage 8 | url=https://github.com/thebigpotatoe/Features-Variables 9 | architectures=esp8266,esp32 10 | depends=ArduinoJSON, Effortless-SPIFFS 11 | license=MIT -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: thebigpotatoe 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, 4 | email, or any other method with the owners of this repository before making a change. 5 | 6 | Please note we have a code of conduct, please follow it in all your interactions with the project. 7 | 8 | ## Pull Request Process 9 | 10 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a 11 | build. 12 | 2. Update the README.md with details of changes to the interface, this includes new environment 13 | variables, exposed ports, useful file locations and container parameters. 14 | 3. Increase the version numbers in any examples files and the README.md to the new version that this 15 | Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). 16 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | FeatureVariables.h KEYWORD1 2 | Feature KEYWORD1 3 | FeatureBase KEYWORD1 4 | 5 | NEW_FEATURE KEYWORD1 6 | FEATURE_FIRE_EVENTS_SAVE_VALUES KEYWORD1 7 | FEATURE_DO_NOT_FIRE_EVENTS_OR_SAVE_VALUES KEYWORD1 8 | FEATURE_DO_NOT_SAVE_VALUES KEYWORD1 9 | FEATURE_DO_NOT_FIRE_EVENTS KEYWORD1 10 | FEATURE_DEBUG_OBJECT KEYWORD1 11 | 12 | getName KEYWORD2 13 | setName KEYWORD2 14 | getType KEYWORD2 15 | 16 | getValue KEYWORD2 17 | getPreviousValue KEYWORD2 18 | setValue KEYWORD2 19 | toString KEYWORD2 20 | 21 | toJson KEYWORD2 22 | fromJson KEYWORD2 23 | 24 | allowSaving KEYWORD2 25 | setSaveTimeout KEYWORD2 26 | getFilename KEYWORD2 27 | openValue KEYWORD2 28 | saveValue KEYWORD2 29 | 30 | allowEvents KEYWORD2 31 | addEventCallback KEYWORD2 32 | removeLastCallback KEYWORD2 33 | removeSpecificCallback KEYWORD2 34 | removeAllCallbacks KEYWORD2 35 | 36 | setDebugOutput KEYWORD2 -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: thebigpotatoe 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Environment** 14 | - Arduino/PlatformIO 15 | - Versions of any libraries 16 | 17 | **Desktop (please complete the following information):** 18 | - OS: [e.g. iOS] 19 | - Browser [e.g. chrome, safari] 20 | - Version [e.g. 22] 21 | 22 | **To Reproduce** 23 | Steps to reproduce the behavior: 24 | 1. Go to '...' 25 | 2. Click on '....' 26 | 3. Scroll down to '....' 27 | 4. See error 28 | 29 | **Expected behavior** 30 | A clear and concise description of what you expected to happen. 31 | 32 | **Screenshots** 33 | If applicable, add screenshots to help explain your problem. 34 | 35 | 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/workflows/Arduino.yml: -------------------------------------------------------------------------------- 1 | name: Arduino 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | env: 11 | PLATFORM_DEFAULT_URL: https://arduino.esp8266.com/stable/package_esp8266com_index.json,https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json 12 | REQUIRED_LIBRARIES: ArduinoJSON,Effortless-SPIFFS 13 | strategy: 14 | matrix: 15 | arduino-boards-fqbn: 16 | - esp8266:esp8266:nodemcuv2:xtal=80,vt=flash,exception=disabled,eesz=4M1M,ip=lm2f,dbg=Disabled,lvl=None____,wipe=none 17 | # - esp32:esp32:esp32 18 | 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v2 22 | 23 | - name: Compile all examples 24 | uses: ArminJo/arduino-test-compile@v2 25 | with: 26 | arduino-board-fqbn: ${{ matrix.arduino-boards-fqbn }} 27 | platform-url: ${{ env.PLATFORM_DEFAULT_URL }} 28 | required-libraries: ${{ env.REQUIRED_LIBRARIES }} 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 thebigpotatoe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/feature_supported_types.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 thebigpotatoe 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | */ 14 | 15 | #pragma once 16 | 17 | #ifndef FEATURE_SUPPORTED_TYPES_h 18 | #define FEATURE_SUPPORTED_TYPES_h 19 | 20 | namespace FeatureVariables { 21 | // Supported types 22 | enum class SupportedTypes { 23 | BOOL, 24 | FLOAT, 25 | DOUBLE, 26 | SIGNED_CHAR, 27 | SIGNED_INT, 28 | SIGNED_SHORT, 29 | SIGNED_LONG, 30 | UNSIGNED_CHAR, 31 | UNSIGNED_INT, 32 | UNSIGNED_SHORT, 33 | UNSIGNED_LONG, 34 | CHAR_PTR, 35 | CONST_CHAR_PTR, 36 | STRING, 37 | STD_STRING, 38 | UNSUPPORTED 39 | }; 40 | } // namespace FeatureVariables 41 | 42 | #endif -------------------------------------------------------------------------------- /src/feature_safe_set.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 thebigpotatoe 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | */ 14 | 15 | #pragma once 16 | 17 | #ifndef FEATURE_SAFE_SET_h 18 | #define FEATURE_SAFE_SET_h 19 | 20 | // std libraries 21 | #include 22 | #include 23 | 24 | // Internal Includes 25 | #include "feature_supported_types.h" 26 | #include "feature_base.h" 27 | #include "feature_impl.h" 28 | #include "feature_safe_cast.h" 29 | 30 | namespace FeatureVariables { 31 | template 32 | bool safe_set(FeatureBase* feature, T value, uint8_t _fireEvents = 0xff) { 33 | Feature* upcast = safe_cast(feature); 34 | return (upcast) ? upcast->setValue(value, _fireEvents) : false; 35 | } 36 | } // namespace FeatureVariables 37 | 38 | #endif -------------------------------------------------------------------------------- /examples/Quick_Start/Quick_Start.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 thebigpotatoe 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | */ 14 | 15 | #include // Optional 16 | #include 17 | 18 | // Create a Global Feature - opens value from flash automatically when using Effortless_SPIFFS! 19 | Feature current_millis; 20 | 21 | // Create a callback 22 | void string_callback(unsigned long _current_millis) { 23 | Serial.printf("my_string has changed to: \"%u\"\n", _current_millis); 24 | } 25 | 26 | void setup() { 27 | // Start Serial 28 | Serial.begin(115200); 29 | Serial.println(); 30 | 31 | // Add callback to Feature 32 | current_millis.addEventCallback(string_callback); 33 | } 34 | 35 | void loop() { 36 | // Assign millis() to Feature every second - saves value automatically when using Effortless_SPIFFS! 37 | current_millis = millis(); 38 | delay(1000); 39 | } -------------------------------------------------------------------------------- /src/FeatureVariables.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 thebigpotatoe 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | */ 14 | 15 | #pragma once 16 | 17 | // c++ compiler check 18 | #ifndef __cplusplus 19 | #error Feature Variables require a C++ compiler 20 | #endif 21 | 22 | // Version Macros 23 | #define FEATURE_VARIABLES_VERSION_MAJOR 1 24 | #define FEATURE_VARIABLES_VERSION_MINOR 0 25 | #define FEATURE_VARIABLES_VERSION_PATCH 0 26 | 27 | // Arduino Framework Libraries 28 | #ifdef ARDUINO 29 | #include 30 | #include 31 | #else 32 | #warning "Feature Variables uses Arduino for some functionality. Compile with the Arduino framework for these features" 33 | #endif 34 | 35 | // Internal includes 36 | #include "feature_macros.h" 37 | #include "feature_internal.h" 38 | #include "feature_supported_types.h" 39 | #include "feature_base.h" 40 | #include "feature_impl.h" 41 | #include "feature_safe_cast.h" 42 | #include "feature_safe_get.h" 43 | #include "feature_safe_set.h" 44 | 45 | // Use relevant classes 46 | using FeatureVariables::Feature; 47 | using FeatureVariables::FeatureBase; -------------------------------------------------------------------------------- /src/feature_internal.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 thebigpotatoe 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | */ 14 | 15 | #ifndef FEATURE_INTERNAL_h 16 | #define FEATURE_INTERNAL_h 17 | 18 | // Ease of use macros 19 | #define FEATURE_ENABLE_IF(type1, type2) Feature_Internal::enable_if::type 20 | #define FEATURE_IS_SAME(type1, type2) Feature_Internal::is_same::value 21 | 22 | namespace FeatureVariables { 23 | // Internal feature name class for std::library implementations 24 | namespace Feature_Internal { 25 | // std:: enable_if implementation 26 | template 27 | struct enable_if { 28 | }; 29 | 30 | template 31 | struct enable_if { 32 | typedef T type; 33 | }; 34 | 35 | // std::is_same implementation 36 | template 37 | struct is_same { 38 | static const bool value = false; 39 | }; 40 | template 41 | struct is_same { 42 | static const bool value = true; 43 | }; 44 | } // namespace Feature_Internal 45 | } // namespace FeatureVariables 46 | 47 | #endif -------------------------------------------------------------------------------- /src/feature_safe_get.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 thebigpotatoe 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | */ 14 | 15 | #ifndef FEATURE_SAFE_GET_h 16 | #define FEATURE_SAFE_GET_h 17 | 18 | // std libraries 19 | #include 20 | #include 21 | 22 | // Internal Includes 23 | #include "feature_supported_types.h" 24 | #include "feature_base.h" 25 | #include "feature_impl.h" 26 | #include "feature_safe_cast.h" 27 | 28 | namespace FeatureVariables { 29 | template 30 | T safe_get(FeatureBase* feature) { 31 | Feature* upcast = safe_cast(feature); 32 | return (upcast) ? upcast->getValue() : T(0x00); 33 | } 34 | 35 | template <> 36 | std::string safe_get(FeatureBase* feature) { 37 | Feature* upcast = safe_cast(feature); 38 | return (upcast) ? upcast->getValue() : std::string(); 39 | } 40 | 41 | #ifdef ARDUINO 42 | template <> 43 | String safe_get(FeatureBase* feature) { 44 | Feature* upcast = safe_cast(feature); 45 | return (upcast) ? upcast->getValue() : String(); 46 | } 47 | #endif 48 | } // namespace FeatureVariables 49 | 50 | #endif -------------------------------------------------------------------------------- /src/feature_macros.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 thebigpotatoe 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | */ 14 | 15 | #ifndef FEATURE_MACROS_h 16 | #define FEATURE_MACROS_h 17 | 18 | // Feature Events Macros 19 | #define FEATURE_FIRE_EVENTS_SAVE_VALUES 0x81 20 | #define FEATURE_DO_NOT_FIRE_EVENTS_OR_SAVE_VALUES 0x00 21 | #define FEATURE_DO_NOT_SAVE_VALUES 0x80 22 | #define FEATURE_DO_NOT_FIRE_EVENTS 0x01 23 | #define FEATURE_SAVE_MASK 0x01 24 | #define FEATURE_EVENTS_MASK 0x80 25 | 26 | // Print Debugger Macro 27 | #ifndef FEATURE_DEBUG_OBJECT 28 | #define FEATURE_DEBUG_OBJECT nullptr 29 | #endif 30 | 31 | // New Features macro - for any member space 32 | #define NEW_MEMBER_FEATURE_NO_SAVE_ARG(name, type) \ 33 | Feature name { #name, (type)(0x00), FEATURE_FIRE_EVENTS_SAVE_VALUES } 34 | #define NEW_MEMBER_FEATURE_SAVE_ARG(name, type, save_file) \ 35 | Feature name { #name, (type)(0x00), save_file } 36 | #define GET_SAVE_ARG(name, type, save_file, command, ...) command 37 | #define NEW_MEMBER_FEATURE_MACRO_SELECTOR(...) GET_SAVE_ARG(__VA_ARGS__, NEW_MEMBER_FEATURE_SAVE_ARG, NEW_MEMBER_FEATURE_NO_SAVE_ARG, ) 38 | #define NEW_FEATURE(...) \ 39 | NEW_MEMBER_FEATURE_MACRO_SELECTOR(__VA_ARGS__) \ 40 | (__VA_ARGS__) 41 | 42 | #endif -------------------------------------------------------------------------------- /examples/Delayed_Saving/Delayed_Saving.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 thebigpotatoe 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | */ 14 | 15 | #define FEATURE_DEBUG_OBJECT &Serial 16 | 17 | // #include 18 | #include 19 | #include 20 | 21 | String getNewString() { 22 | char new_string[9] = {'\0'}; 23 | for (int i = 0; i < 8; i++) { 24 | byte randomChar = random(0, 36); 25 | char newChar = randomChar + 'a'; 26 | if (randomChar > 25) newChar = (randomChar - 26) + '0'; 27 | new_string[i] = newChar; 28 | } 29 | new_string[8] = '\0'; 30 | return (String)new_string; 31 | } 32 | 33 | void setup() { 34 | // Start Serial 35 | Serial.begin(115200); 36 | Serial.println(); 37 | Serial.println("********** Started Feature Variable Example **********"); 38 | 39 | // Create a Feature based on the Arduino String class - opens last value from flash automatically! 40 | Feature my_string("my_string_name"); 41 | Serial.printf("my_string opened as: \"%s\"\n", my_string.getValue().c_str()); 42 | 43 | // Set the debug output of the feature to Serial 44 | my_string.setDebugOutput(&Serial); 45 | 46 | // Set the save timeout value in milliseconds 47 | my_string.setSaveTimeout(1000); 48 | 49 | // Set the feature value and wait for the debug output to show it saved! 50 | my_string = getNewString(); 51 | Serial.printf("Pausing program for 2 seconds\n"); 52 | delay(2000); 53 | Serial.printf("Resuming program\n"); 54 | delay(1000); 55 | 56 | // Now try set it a bunch of times and watch the delay on saving the last value 57 | my_string = getNewString(); 58 | my_string = getNewString(); 59 | my_string = getNewString(); 60 | my_string = getNewString(); 61 | my_string = getNewString(); 62 | Serial.printf("Pausing program for 2 seconds\n"); 63 | delay(2000); 64 | Serial.printf("Resuming program\n"); 65 | 66 | // End of example 67 | Serial.println("********** End of Feature Variable Example **********"); 68 | delay(10000); 69 | ESP.restart(); 70 | } 71 | 72 | void loop() {} -------------------------------------------------------------------------------- /examples/Using_ArduinoJson/Using_ArduinoJson.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 thebigpotatoe 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | */ 14 | 15 | #define FEATURE_DEBUG_OBJECT &Serial 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | // Get random float method 22 | float get_random_float() { 23 | float new_float; 24 | new_float = (float)random(1, 1000) / (float)random(1, 1000); 25 | return new_float; 26 | } 27 | 28 | void setup() { 29 | // Start Serial 30 | Serial.begin(115200); 31 | Serial.println(); 32 | Serial.println("********** Started Feature Variable Example **********"); 33 | 34 | // Create a Feature based on a float 35 | Feature my_float("my_float_name", get_random_float()); 36 | 37 | // Create new JSON Document and fill it with data 38 | DynamicJsonDocument json(1024); 39 | JsonObject jsonObject = json.to(); 40 | jsonObject["value1"] = get_random_float(); 41 | jsonObject["value2"] = "hello world"; 42 | 43 | // Set the key value pair in the JSON Object 44 | bool retval = my_float.toJson(jsonObject); 45 | if (retval) { 46 | Serial.print("[setup] - Successfully set the JSON key value pair in object: \n"); 47 | serializeJsonPretty(json, Serial); 48 | Serial.println(); 49 | } else { 50 | Serial.print("[setup] - Failed to set JSON value from Feature value\n"); 51 | } 52 | 53 | // Let's change the value in the JSON object so we can use it to set the value of teh Feature 54 | Serial.print("\n[setup] - New JSON object values: \n"); 55 | jsonObject["my_float_name"] = get_random_float(); 56 | serializeJsonPretty(json, Serial); 57 | Serial.println(); 58 | 59 | // Set the Feature from the JSON object 60 | Serial.print("\n[setup] - Setting Feature value from JSON object:\n"); 61 | retval = my_float.fromJson(jsonObject); 62 | if (retval) { 63 | Serial.printf("[setup] - Successfully set Feature value from JSON object as: %.6f\n", my_float.getValue()); 64 | } else { 65 | Serial.print("[setup] - Failed to set Feature value from JSON object\n"); 66 | } 67 | } 68 | 69 | void loop() { 70 | } -------------------------------------------------------------------------------- /examples/Alternate_Set_Methods/Alternate_Set_Methods.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 thebigpotatoe 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | */ 14 | 15 | #define FEATURE_DEBUG_OBJECT &Serial 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | // Get random float method 22 | float get_random_float() { 23 | float new_float; 24 | new_float = (float)random(1, 1000) / (float)random(1, 1000); 25 | return new_float; 26 | } 27 | 28 | // String event callback 29 | void float_callback(float _new_float) { 30 | Serial.printf("(Global Function) my_float has changed to: \"%.6f\"\n", _new_float); 31 | } 32 | 33 | void setup() { 34 | // Start Serial 35 | Serial.begin(115200); 36 | Serial.println(); 37 | Serial.println("********** Started Feature Variable Example **********"); 38 | 39 | // Create a Feature based on a float 40 | Feature my_float("my_float_name"); 41 | my_float.addEventCallback(float_callback); 42 | 43 | // Assign the my_float a few values using the standard methods 44 | Serial.println("\n********** Standard Methods **********"); 45 | my_float = get_random_float(); 46 | my_float.setValue(get_random_float()); 47 | 48 | // There are a few overrides you can provide to the set values function depending on what you want to do 49 | Serial.println("\n********** Events and Saving Control **********"); 50 | my_float.setValue(get_random_float(), FEATURE_FIRE_EVENTS_SAVE_VALUES); 51 | my_float.setValue(get_random_float(), FEATURE_DO_NOT_FIRE_EVENTS); 52 | my_float.setValue(get_random_float(), FEATURE_DO_NOT_SAVE_VALUES); 53 | my_float.setValue(get_random_float(), FEATURE_DO_NOT_FIRE_EVENTS_OR_SAVE_VALUES); 54 | 55 | // Features also offer the operator() overloaded to set values 56 | Serial.println("\n********** Operator () Overloaded **********"); 57 | my_float(get_random_float()); 58 | my_float(get_random_float(), FEATURE_DO_NOT_FIRE_EVENTS_OR_SAVE_VALUES); 59 | 60 | // If you are using Arduino JSON you can set the value directly as a key value pair within an object 61 | #if defined ARDUINOJSON_VERSION_MAJOR && ARDUINOJSON_VERSION_MAJOR == 6 62 | // Create new JSON Document and parse a string 63 | DynamicJsonDocument json(1024); 64 | JsonObject jsonObject = json.to(); 65 | json["my_float_name"] = get_random_float(); 66 | 67 | // Set relevent features in container using the same methods but passing a JsonObject 68 | Serial.println("\n********** ArduinoJSON **********"); 69 | my_float.fromJson(jsonObject); 70 | // my_float.setValue(jsonObject, FEATURE_DO_NOT_FIRE_EVENTS); 71 | // my_float.setValue(jsonObject, FEATURE_DO_NOT_SAVE_VALUES); 72 | // my_float.setValue(jsonObject, FEATURE_DO_NOT_FIRE_EVENTS_OR_SAVE_VALUES); 73 | #endif 74 | } 75 | 76 | void loop() {} -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at . All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /examples/Basic_Use/Basic_Use.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 thebigpotatoe 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | */ 14 | 15 | #define FEATURE_DEBUG_OBJECT &Serial 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | // Random String creator 22 | String get_random_string() { 23 | char new_string[9] = {'\0'}; 24 | for (int i = 0; i < 8; i++) { 25 | byte randomChar = random(0, 36); 26 | char newChar = randomChar + 'a'; 27 | if (randomChar > 25) newChar = (randomChar - 26) + '0'; 28 | new_string[i] = newChar; 29 | } 30 | new_string[8] = '\0'; 31 | return String(new_string); 32 | } 33 | 34 | // String event callback 35 | void string_callback(String _new_string) { 36 | Serial.printf("(Global Function) my_string has changed to: \"%s\"\n", _new_string.c_str()); 37 | } 38 | 39 | void setup() { 40 | // Start Serial 41 | Serial.begin(115200); 42 | Serial.println(); 43 | Serial.println("********** Started Feature Variable Example **********"); 44 | 45 | // Create a Feature based on the Arduino String class - opens last value from flash automatically! 46 | Feature my_string("my_string_name"); 47 | Serial.printf("my_string opened as: \"%s\"\n", my_string.getValue().c_str()); 48 | 49 | // There also exists two other constructors to allow you to set without a name for the Feature or set its value and eventFlag on creation 50 | // Feature my_string; 51 | // Feature my_string("my_string_name", "default_string", FEATURE_FIRE_EVENTS_SAVE_VALUES); 52 | 53 | // Add as many callbacks as you want to the Features 54 | my_string.addEventCallback(string_callback); 55 | my_string.addEventCallback([&](String _new_string) { 56 | Serial.printf("(Lambda Function) my_string has changed to: \"%s\"\n", _new_string.c_str()); 57 | }); 58 | 59 | // Assign the new_string a few values - Watch the events fire and automatically save the values to flash! 60 | my_string = "Hello"; 61 | my_string.setValue("World"); 62 | 63 | // There is a global events flags value that allows you to control how the feature works internally by default. This is accessable through two helper methods. 64 | my_string.allowEvents(false); 65 | my_string.allowSaving(false); 66 | my_string.setValue("No Events or Saving Globally!"); 67 | 68 | // There are a few overrides you can provide to the setValues function which temporarily allow you to control the way the feature works 69 | my_string.setValue("Events and Saving!", FEATURE_FIRE_EVENTS_SAVE_VALUES); 70 | my_string.setValue("No Events!", FEATURE_DO_NOT_FIRE_EVENTS); 71 | my_string.setValue("No Saving!", FEATURE_DO_NOT_SAVE_VALUES); 72 | my_string.setValue("No Events or Saving!", FEATURE_DO_NOT_FIRE_EVENTS_OR_SAVE_VALUES); 73 | 74 | // Create a random 32 byte string for this example to save to flash for reboot 75 | my_string.allowEvents(); 76 | my_string.allowSaving(); 77 | my_string = get_random_string(); 78 | 79 | // End of example 80 | Serial.println("********** End of Feature Variable Example **********"); 81 | delay(10000); 82 | ESP.restart(); 83 | } 84 | 85 | void loop() {} -------------------------------------------------------------------------------- /examples/Alternate_Get_Methods/Alternate_Get_Methods.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 thebigpotatoe 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | */ 14 | 15 | #define FEATURE_DEBUG_OBJECT &Serial 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | // Get random float method 22 | float get_random_float() { 23 | float new_float; 24 | new_float = (float)random(1, 1000) / (float)random(1, 1000); 25 | return new_float; 26 | } 27 | 28 | void setup() { 29 | // Start Serial 30 | Serial.begin(115200); 31 | Serial.println(); 32 | Serial.println("********** Started Feature Variable Example **********"); 33 | 34 | // Create a Feature based on a float 35 | Feature my_float("my_float_name", get_random_float()); 36 | 37 | // Features offer the operator () overloaded to get values 38 | float float_value = my_float; 39 | Serial.printf("[setup] - float_value is \"%.6f\"\n", float_value); 40 | 41 | // There also exists a get method to get the value and previous values of the Feature 42 | my_float = get_random_float(); 43 | float current_value = my_float.getValue(); 44 | float previous_value = my_float.getPreviousValue(); 45 | Serial.printf("[setup] - current_value is \"%.6f\"\n", current_value); 46 | Serial.printf("[setup] - previous_value was \"%.6f\"\n", previous_value); 47 | 48 | // For convenience the value is can be converted to a string 49 | Serial.printf("[setup] - my_float as string is \"%s\"\n", my_float.toString().c_str()); 50 | 51 | // The name of the feature is accessable using its get method 52 | const char* feature_name = my_float.getName(); 53 | Serial.printf("[setup] - Name of feature is \"%s\"\n", feature_name); 54 | 55 | // You can also get the filename where the data is being stored. 56 | char filename[32]; 57 | my_float.getFilename(filename); 58 | Serial.printf("[setup] - Filename is \"%s\"\n", filename); 59 | 60 | // Features of the same type can be assigned from one to another safely also transfering all callbacks 61 | Feature my_float_2 = my_float; 62 | Feature my_float_3(my_float); 63 | Serial.printf("[setup] - %s value is \"%.6f\"\n", my_float_2.getName(), my_float_2.getValue()); 64 | Serial.printf("[setup] - %s value is \"%.6f\"\n", my_float_3.getName(), my_float_3.getValue()); 65 | 66 | // As a further convenience, ArduinoJson Objects can be passed to the feature to inject a new key value pair 67 | // of the name and value of the feature within the object 68 | #if defined ARDUINOJSON_VERSION_MAJOR && ARDUINOJSON_VERSION_MAJOR == 6 69 | // Create new JSON Document and parse a string 70 | DynamicJsonDocument json(1024); 71 | JsonObject jsonObject = json.to(); 72 | jsonObject["value1"] = 65403654.65464; 73 | jsonObject["value2"] = "hello world"; 74 | 75 | // Set the key value pair in the JSON Object 76 | bool retval = my_float.toJson(jsonObject); 77 | 78 | // Print the result 79 | if (retval) { 80 | Serial.print("[setup] - Successfully set the JSON key value pair in object: \n"); 81 | serializeJsonPretty(json, Serial); 82 | Serial.println(); 83 | } else { 84 | Serial.println("[setup] - Failed to set JSON key pair values in object"); 85 | } 86 | #endif 87 | } 88 | 89 | void loop() { 90 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Features-Variables 2 | 3 | [![GitHub release](https://img.shields.io/github/release/thebigpotatoe/Feature-Variables.svg)](https://github.com/thebigpotatoe/Feature-Variables/releases) 4 | [![arduino-library-badge](https://www.ardu-badge.com/badge/Feature-Variables.svg?)](https://www.ardu-badge.com/Feature-Variables) 5 | [![PlatformIO Build Status](https://github.com/thebigpotatoe/Feature-Variables/workflows/PlatformIO%20CI/badge.svg)](https://github.com/thebigpotatoe/Feature-Variables/actions?query=workflow%3A%22PlatformIO+CI%22) 6 | [![Arduino Build Status](https://github.com/thebigpotatoe/Feature-Variables/workflows/Arduino/badge.svg)](https://github.com/thebigpotatoe/Feature-Variables/actions?query=workflow%3A%22PlatformIO+CI%22) 7 | [![GitHub License](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/thebigpotatoe/Feature-Variables/blob/master/LICENSE) 8 | 9 | Features are persistent event driven variables specifically for use with the popular Arduino boards and Espressif series micro controllers. They simplify everything from loading and saving variable data into flash for persistance over reboots, to firing multiple events when the value of a variable has changed, to simplifying data passing when using ArduinoJSON, all in a few lines of easy to understand code. 10 | 11 | The library and examples are supported in the Arduino IDE and platformIO for building. Check out the library.properties or platfomio.ini file for idea on how to build the library in the Arduino IDE or on pio. Included are a heap of examples on how to use each of the advantages of Features in your application fully compatible with both build methods. 12 | 13 | If you would like to reach out, this project is open to comments, improvements, or general feedback, so feel free to open an issue or create a pull request with any ideas! 14 | 15 | ## Quick Start 16 | 17 | Starting or migrating a project to Features is easy. Just see this short example of how to create a very basic Feature application storing the value of millis() every second: 18 | 19 | ``` c++ 20 | #include // Optional 21 | #include 22 | 23 | // Create a Global Feature - opens value from flash automatically when using Effortless_SPIFFS! 24 | Feature current_millis; 25 | 26 | // Create a callback 27 | void string_callback(unsigned long _current_millis) { 28 | Serial.printf("my_string has changed to: \"%u\"\n", _current_millis); 29 | } 30 | 31 | void setup() { 32 | // Start Serial 33 | Serial.begin(115200); 34 | Serial.println(); 35 | 36 | // Add callback to Feature 37 | current_millis.addEventCallback(string_callback); 38 | } 39 | 40 | void loop() { 41 | // Assign millis() to Feature every second - saves value automatically when using Effortless_SPIFFS! 42 | current_millis = millis(); 43 | delay(1000); 44 | } 45 | ``` 46 | 47 | ## Examples 48 | For a comprehensive understanding of the API and how to use Features check out the examples folder. In there you can find several examples showing: 49 | 50 | - [Basic use of Features](examples/Basic_Use/Basic_Use.ino) 51 | - [How to use in a storage container like std::vector](examples/Container_Example/Container_Example.ino) 52 | - [All methods of getting the value from the Feature](examples/Alternate_Get_Methods/Alternate_Get_Methods.ino) 53 | - [All methods of setting the value from the Feature](examples/Alternate_Set_Methods/Alternate_Set_Methods.ino) 54 | - [Delay saving of the value to speed up the application](examples/Delayed_Saving/Delayed_Saving.ino) 55 | - [How to use with ArduinoJSON very easily](examples/Using_ArduinoJson/Using_ArduinoJson.ino) 56 | 57 | ## Contributing 58 | 59 | Contributions of any type are most welcome. Feel free to open an issues with feedback, questions, ideas, or anything else regarding Features or a pull request for new any ideas on code style or implementation. 60 | -------------------------------------------------------------------------------- /src/feature_safe_cast.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 thebigpotatoe 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | */ 14 | 15 | #ifndef FEATURE_SAFE_CAST_h 16 | #define FEATURE_SAFE_CAST_h 17 | 18 | // std libraries 19 | #include 20 | #include 21 | 22 | // Internal Includes 23 | #include "feature_supported_types.h" 24 | #include "feature_base.h" 25 | #include "feature_impl.h" 26 | 27 | namespace FeatureVariables { 28 | // Safe cast base template 29 | template 30 | Feature* safe_cast(FeatureBase* feature) { 31 | return nullptr; 32 | } 33 | 34 | // Safe cast boolean specialisation 35 | template <> 36 | Feature* safe_cast(FeatureBase* feature) { 37 | return (feature->getType() == SupportedTypes::BOOL) ? static_cast*>(feature) : nullptr; 38 | } 39 | 40 | // Safe cast floating point specialisations 41 | template <> 42 | Feature* safe_cast(FeatureBase* feature) { 43 | return (feature->getType() == SupportedTypes::FLOAT) ? static_cast*>(feature) : nullptr; 44 | } 45 | template <> 46 | Feature* safe_cast(FeatureBase* feature) { 47 | return (feature->getType() == SupportedTypes::DOUBLE) ? static_cast*>(feature) : nullptr; 48 | } 49 | 50 | // Safe cast signed specialisations 51 | template <> 52 | Feature* safe_cast(FeatureBase* feature) { 53 | return (feature->getType() == SupportedTypes::SIGNED_CHAR) ? static_cast*>(feature) : nullptr; 54 | } 55 | template <> 56 | Feature* safe_cast(FeatureBase* feature) { 57 | return (feature->getType() == SupportedTypes::SIGNED_INT) ? static_cast*>(feature) : nullptr; 58 | } 59 | template <> 60 | Feature* safe_cast(FeatureBase* feature) { 61 | return (feature->getType() == SupportedTypes::SIGNED_SHORT) ? static_cast*>(feature) : nullptr; 62 | } 63 | template <> 64 | Feature* safe_cast(FeatureBase* feature) { 65 | return (feature->getType() == SupportedTypes::SIGNED_LONG) ? static_cast*>(feature) : nullptr; 66 | } 67 | 68 | // Safe cast unsigned specialisations 69 | template <> 70 | Feature* safe_cast(FeatureBase* feature) { 71 | return (feature->getType() == SupportedTypes::UNSIGNED_CHAR) ? static_cast*>(feature) : nullptr; 72 | } 73 | template <> 74 | Feature* safe_cast(FeatureBase* feature) { 75 | return (feature->getType() == SupportedTypes::UNSIGNED_INT) ? static_cast*>(feature) : nullptr; 76 | } 77 | template <> 78 | Feature* safe_cast(FeatureBase* feature) { 79 | return (feature->getType() == SupportedTypes::UNSIGNED_SHORT) ? static_cast*>(feature) : nullptr; 80 | } 81 | template <> 82 | Feature* safe_cast(FeatureBase* feature) { 83 | return (feature->getType() == SupportedTypes::UNSIGNED_LONG) ? static_cast*>(feature) : nullptr; 84 | } 85 | 86 | // Safe cast string like specialisations 87 | template <> 88 | Feature* safe_cast(FeatureBase* feature) { 89 | return (feature->getType() == SupportedTypes::CHAR_PTR) ? static_cast*>(feature) : nullptr; 90 | } 91 | template <> 92 | Feature* safe_cast(FeatureBase* feature) { 93 | return (feature->getType() == SupportedTypes::CONST_CHAR_PTR) ? static_cast*>(feature) : nullptr; 94 | } 95 | template <> 96 | Feature* safe_cast(FeatureBase* feature) { 97 | return (feature->getType() == SupportedTypes::STD_STRING) ? static_cast*>(feature) : nullptr; 98 | } 99 | 100 | #ifdef ARDUINO 101 | template <> 102 | Feature* safe_cast(FeatureBase* feature) { 103 | return (feature->getType() == SupportedTypes::STRING) ? static_cast*>(feature) : nullptr; 104 | } 105 | #endif 106 | } // namespace FeatureVariables 107 | 108 | #endif -------------------------------------------------------------------------------- /examples/Type_Testing/Type_Testing.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 thebigpotatoe 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | */ 14 | 15 | #define FEATURE_DEBUG_OBJECT &Serial 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | void setup() { 22 | // Start Serial 23 | Serial.begin(115200); 24 | Serial.println(); 25 | Serial.println("********** Started Feature Variable Example **********"); 26 | 27 | // Booleans 28 | Feature Boolean("Bool"); 29 | 30 | // Floating Points 31 | Feature Float("Float"); 32 | Feature Double("double"); 33 | 34 | // Signed Integers 35 | Feature signedchar("signed char"); 36 | Feature signedint("signed int"); 37 | Feature signedshort("signed short"); 38 | Feature signedlong("signed long"); 39 | 40 | // Unsigned Integers 41 | Feature unsignedchar("unsigned char"); 42 | Feature unsignedint("unsigned int"); 43 | Feature unsignedshort("unsigned short"); 44 | Feature unsignedlong("unsigned long"); 45 | 46 | // Character Types 47 | Feature Char("char"); 48 | Feature constchar("const char"); 49 | Feature Stringy("String"); 50 | Feature stdString("std::string"); 51 | 52 | // Debug 53 | Serial.println("********** Setting New Values **********"); 54 | 55 | // Boolean 56 | Serial.println(Boolean.getName()); 57 | Serial.println(Boolean.getValue()); 58 | Boolean = true; 59 | Serial.println(Boolean.getValue()); 60 | Serial.println(); 61 | 62 | // Floats 63 | Serial.println(Float.getName()); 64 | Serial.println(Float.getValue()); 65 | Float = 10.102; 66 | Serial.println(Float.getValue()); 67 | Serial.println(); 68 | 69 | Serial.println(Double.getName()); 70 | Serial.println(Double.getValue()); 71 | Double = 10.102; 72 | Serial.println(Double.getValue()); 73 | Serial.println(); 74 | 75 | // Signed 76 | Serial.println(signedchar.getName()); 77 | Serial.println((signedchar.getValue())); 78 | signedchar = 10; 79 | Serial.println((signedchar.getValue())); 80 | Serial.println(); 81 | 82 | Serial.println(signedint.getName()); 83 | Serial.println((signedint.getValue())); 84 | signedint = 10; 85 | Serial.println((signedint.getValue())); 86 | Serial.println(); 87 | 88 | Serial.println(signedshort.getName()); 89 | Serial.println((signedshort.getValue())); 90 | signedshort = 10; 91 | Serial.println((signedshort.getValue())); 92 | Serial.println(); 93 | 94 | Serial.println(signedlong.getName()); 95 | Serial.println((signedlong.getValue())); 96 | signedlong = 10; 97 | Serial.println((signedlong.getValue())); 98 | Serial.println(); 99 | 100 | // Unsigned 101 | Serial.println(unsignedchar.getName()); 102 | Serial.println((unsignedchar.getValue())); 103 | unsignedchar = 10; 104 | Serial.println((unsignedchar.getValue())); 105 | Serial.println(); 106 | 107 | Serial.println(unsignedint.getName()); 108 | Serial.println((unsignedint.getValue())); 109 | unsignedint = 10; 110 | Serial.println((unsignedint.getValue())); 111 | Serial.println(); 112 | 113 | Serial.println(unsignedshort.getName()); 114 | Serial.println((unsignedshort.getValue())); 115 | unsignedshort = 10; 116 | Serial.println((unsignedshort.getValue())); 117 | Serial.println(); 118 | 119 | Serial.println(unsignedlong.getName()); 120 | Serial.println((unsignedlong.getValue())); 121 | unsignedlong = 10; 122 | Serial.println((unsignedlong.getValue())); 123 | Serial.println(); 124 | 125 | // Char* 126 | Serial.println(Char.getName()); 127 | Serial.println(Char.getValue()); 128 | Char = "Test"; 129 | Serial.println(Char.getValue()); 130 | Serial.println(); 131 | 132 | // const char* 133 | Serial.println(constchar.getName()); 134 | Serial.println(constchar.getValue()); 135 | Serial.write(constchar.getValue()); 136 | constchar = "Test"; 137 | Serial.println(constchar.getValue()); 138 | Serial.println(); 139 | 140 | // Arduino String 141 | Serial.println(Stringy.getName()); 142 | Serial.println(Stringy.getValue()); 143 | Stringy = "Test"; 144 | Serial.println(Stringy.getValue()); 145 | Serial.println(); 146 | 147 | // std::string 148 | Serial.println(stdString.getName()); 149 | Serial.println((stdString.getValue().c_str())); 150 | stdString = "Test"; 151 | Serial.println((stdString.getValue().c_str())); 152 | Serial.println(); 153 | 154 | // End of example 155 | Serial.println("********** End of Feature Variable Example **********"); 156 | } 157 | 158 | void loop() {} -------------------------------------------------------------------------------- /src/feature_base.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 thebigpotatoe 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | */ 14 | 15 | #ifndef FEATURE_BASE_h 16 | #define FEATURE_BASE_h 17 | 18 | // std libraries 19 | #include 20 | #include 21 | 22 | // Arduino Framework Libraries 23 | #ifdef ARDUINO 24 | #include 25 | #include 26 | #else 27 | #warning "Feature Variables uses Arduino for some functionality. Compile with the Arduino framework for these features" 28 | #endif 29 | 30 | // Internal Includes 31 | #include "feature_macros.h" 32 | #include "feature_internal.h" 33 | #include "feature_supported_types.h" 34 | 35 | namespace FeatureVariables { 36 | // Base Class 37 | class FeatureBase { 38 | public: // supported types 39 | public: // Constructors 40 | FeatureBase() { 41 | // Assign a random name on init 42 | char newName[33] = {'\0'}; 43 | for (int i = 0; i < 32; i++) { 44 | char randomChar = rand() % 37; 45 | char newChar = randomChar + 'a'; 46 | if (randomChar > 25) newChar = (randomChar - 26) + '0'; 47 | newName[i] = newChar; 48 | } 49 | newName[32] = '\0'; 50 | setName(newName); 51 | }; 52 | FeatureBase(const char* _name) { 53 | setName(_name); 54 | }; 55 | virtual ~FeatureBase(){}; 56 | 57 | public: // Name Methods 58 | const char* getName() { 59 | return name; 60 | } 61 | bool setName(const char* _name) { 62 | if (_name && snprintf(name, 32, "%s", _name) > 0) return true; 63 | return false; 64 | } 65 | 66 | public: // Public Get type methods 67 | SupportedTypes getType() { 68 | return type; 69 | } 70 | 71 | protected: // Internal set type methods 72 | // Bool setType implimentation 73 | template 74 | typename FEATURE_ENABLE_IF(FEATURE_IS_SAME(T, bool), void) setType() { 75 | type = SupportedTypes::BOOL; 76 | } 77 | 78 | // Floating point setType implimentation 79 | template 80 | typename FEATURE_ENABLE_IF(FEATURE_IS_SAME(T, float), void) setType() { 81 | type = SupportedTypes::FLOAT; 82 | } 83 | template 84 | typename FEATURE_ENABLE_IF(FEATURE_IS_SAME(T, double), void) setType() { 85 | type = SupportedTypes::DOUBLE; 86 | } 87 | 88 | // Signed setType implimentation 89 | template 90 | typename FEATURE_ENABLE_IF(FEATURE_IS_SAME(T, signed char), void) setType() { 91 | type = SupportedTypes::SIGNED_CHAR; 92 | } 93 | template 94 | typename FEATURE_ENABLE_IF(FEATURE_IS_SAME(T, signed int), void) setType() { 95 | type = SupportedTypes::SIGNED_INT; 96 | } 97 | template 98 | typename FEATURE_ENABLE_IF(FEATURE_IS_SAME(T, signed short), void) setType() { 99 | type = SupportedTypes::SIGNED_SHORT; 100 | } 101 | template 102 | typename FEATURE_ENABLE_IF(FEATURE_IS_SAME(T, signed long), void) setType() { 103 | type = SupportedTypes::SIGNED_LONG; 104 | } 105 | 106 | // Unsigned setType implimentation 107 | template 108 | typename FEATURE_ENABLE_IF(FEATURE_IS_SAME(T, unsigned char), void) setType() { 109 | type = SupportedTypes::UNSIGNED_CHAR; 110 | } 111 | template 112 | typename FEATURE_ENABLE_IF(FEATURE_IS_SAME(T, unsigned int), void) setType() { 113 | type = SupportedTypes::UNSIGNED_INT; 114 | } 115 | template 116 | typename FEATURE_ENABLE_IF(FEATURE_IS_SAME(T, unsigned short), void) setType() { 117 | type = SupportedTypes::UNSIGNED_SHORT; 118 | } 119 | template 120 | typename FEATURE_ENABLE_IF(FEATURE_IS_SAME(T, unsigned long), void) setType() { 121 | type = SupportedTypes::UNSIGNED_LONG; 122 | } 123 | 124 | // String type setType implimentation 125 | template 126 | typename FEATURE_ENABLE_IF(FEATURE_IS_SAME(T, char*), void) setType() { 127 | type = SupportedTypes::CHAR_PTR; 128 | } 129 | template 130 | typename FEATURE_ENABLE_IF(FEATURE_IS_SAME(T, const char*), void) setType() { 131 | type = SupportedTypes::CONST_CHAR_PTR; 132 | } 133 | template 134 | typename FEATURE_ENABLE_IF(FEATURE_IS_SAME(T, std::string), void) setType() { 135 | type = SupportedTypes::STD_STRING; 136 | } 137 | 138 | #ifdef ARDUINO 139 | // Arduino String setType implimentation 140 | template 141 | typename FEATURE_ENABLE_IF(FEATURE_IS_SAME(T, String), void) setType() { 142 | type = SupportedTypes::STRING; 143 | } 144 | #endif 145 | 146 | #if defined ARDUINOJSON_VERSION_MAJOR && ARDUINOJSON_VERSION_MAJOR == 6 147 | public: // Arduino JSON Get and Set 148 | virtual bool toJson(JsonObject& json) = 0; 149 | virtual bool fromJson(JsonObject& json, uint8_t _fireEvents = FEATURE_FIRE_EVENTS_SAVE_VALUES) = 0; 150 | #endif 151 | 152 | protected: // Public type storage 153 | SupportedTypes type = SupportedTypes::UNSUPPORTED; 154 | 155 | protected: // Internal name storage 156 | char name[33]; 157 | }; 158 | 159 | } // namespace FeatureVariables 160 | 161 | #endif -------------------------------------------------------------------------------- /examples/Container_Example/Container_Example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 thebigpotatoe 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | */ 14 | 15 | #define FEATURE_DEBUG_OBJECT &Serial 16 | 17 | #include 18 | #include 19 | #include 20 | // #include 21 | 22 | // Feature Container 23 | std::vector feature_vector; 24 | 25 | // Helpful methods 26 | float get_random_float() { 27 | float new_float; 28 | new_float = (float)random(1, 1000) / (float)random(1, 1000); 29 | return new_float; 30 | } 31 | 32 | String get_random_string() { 33 | char new_string[9] = {'\0'}; 34 | for (int i = 0; i < 8; i++) { 35 | byte randomChar = random(0, 36); 36 | char newChar = randomChar + 'a'; 37 | if (randomChar > 25) newChar = (randomChar - 26) + '0'; 38 | new_string[i] = newChar; 39 | } 40 | new_string[8] = '\0'; 41 | return String(new_string); 42 | } 43 | 44 | // Callbacks 45 | void float_callback(float _new_float) { 46 | Serial.printf("(Global Function) my_float has changed to: \"%.6f\"\n", _new_float); 47 | } 48 | 49 | void string_callback(String _new_string) { 50 | Serial.printf("(Global Function) my_string has changed to: \"%s\"\n", _new_string.c_str()); 51 | } 52 | 53 | void double_callback(double _new_double) { 54 | Serial.printf("(Global Function) my_double has changed to: \"%.6f\"\n", _new_double); 55 | } 56 | 57 | void setup() { 58 | // Start Serial 59 | Serial.begin(115200); 60 | Serial.println(); 61 | Serial.println("********** Started Feature Variable Example **********"); 62 | 63 | // Create a few features 64 | Feature my_string("my_string_name"); 65 | Feature my_double("my_double_name"); 66 | Feature my_float("my_float_name"); 67 | 68 | // Set some event callbacks 69 | my_string.addEventCallback(string_callback); 70 | my_double.addEventCallback(double_callback); 71 | my_float.addEventCallback(float_callback); 72 | 73 | // Add the Features to the container 74 | feature_vector.push_back(&my_string); 75 | feature_vector.push_back(&my_double); 76 | feature_vector.push_back(&my_float); 77 | 78 | // When using Features in a container two methods to get or modify the feature exist depending on the use of ArduinoJSON. 79 | // The first is using inbuilt safe casting, the second leverages the ArduinoJSON library to syncronise the value of a JSON object 80 | // and the Feature. 81 | 82 | // Modify relevent features in container using safe casting methods 83 | for (auto feature : feature_vector) { 84 | // Cast feature safely - will only work for string types in this example, ignoring the other types 85 | Feature* my_string_ptr = FeatureVariables::safe_cast(feature); 86 | Serial.println("\n********** Pointer Safe Cast **********"); 87 | Serial.printf("Cast for feature named \"%s\" %s\n", feature->getName(), (my_string_ptr) ? "succeeded" : "failed"); 88 | 89 | // An example of how to use the pointer safely would be to check if it was null and check the name 90 | if (my_string_ptr && strcmp(my_string_ptr->getName(), "my_string_name") == 0) { 91 | my_string_ptr->setValue("Updated from safely cast pointer"); 92 | } 93 | 94 | // Without casting directly, you can get the value safe and easy using safe_get() 95 | // This will return the value if the cast succeeded or a null value of the type attempted to cast to 96 | String my_string_value = FeatureVariables::safe_get(feature); 97 | Serial.println("********** Pointer Safe Get **********"); 98 | Serial.printf("Value for feature named \"%s\" was \"%s\"\n", feature->getName(), (my_string_value.length()) ? my_string_value.c_str() : "EMPTY"); 99 | 100 | // Without casting directly, you can set the value safe and easy using safe_set() 101 | // This will set the value if the types requested and the feature's type match 102 | Serial.println("********** Pointer Safe Set **********"); 103 | FeatureVariables::safe_set(feature, "Saving with Events!"); 104 | FeatureVariables::safe_set(feature, "Saving Only!", FEATURE_DO_NOT_FIRE_EVENTS); 105 | FeatureVariables::safe_set(feature, "Events Only!", FEATURE_DO_NOT_SAVE_VALUES); 106 | FeatureVariables::safe_set(feature, "No Saving or Events!", FEATURE_DO_NOT_FIRE_EVENTS_OR_SAVE_VALUES); 107 | } 108 | 109 | 110 | #if defined ARDUINOJSON_VERSION_MAJOR && ARDUINOJSON_VERSION_MAJOR == 6 111 | // Create new JSON Document and parse a string 112 | DynamicJsonDocument json(1024); 113 | const char* jsonString = "{\"my_string_name\": 65403654.65464}"; 114 | deserializeJson(json, jsonString); 115 | JsonObject jsonObject = json.to(); 116 | 117 | // Set relevent features in container using the same methods but passing a JsonObject 118 | for (auto feature : feature_vector) { 119 | feature->fromJson(jsonObject); 120 | // feature->setValue(jsonObject, FEATURE_DO_NOT_FIRE_EVENTS); 121 | // feature->setValue(jsonObject, FEATURE_DO_NOT_SAVE_VALUES); 122 | // feature->setValue(jsonObject, FEATURE_DO_NOT_FIRE_EVENTS_OR_SAVE_VALUES); 123 | } 124 | #endif 125 | } 126 | 127 | void loop() {} -------------------------------------------------------------------------------- /src/feature_impl.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2020 thebigpotatoe 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | */ 14 | 15 | #ifndef FEATURE_IMPL_h 16 | #define FEATURE_IMPL_h 17 | 18 | // std libraries 19 | #include 20 | #include 21 | #include 22 | 23 | // Arduino Framework Libraries 24 | #ifdef ARDUINO 25 | #include 26 | #include 27 | #else 28 | #warning "Feature Variables uses Arduino for some functionality. Compile with the Arduino framework for these features" 29 | #endif 30 | 31 | // Internal Includes 32 | #include "feature_macros.h" 33 | #include "feature_internal.h" 34 | #include "feature_supported_types.h" 35 | #include "feature_base.h" 36 | 37 | namespace FeatureVariables { 38 | // Feature Template Class 39 | template 40 | class Feature : public FeatureBase { 41 | private: // Typedefs 42 | typedef std::function eventCallback; 43 | typedef std::vector eventCallbackVector; 44 | 45 | public: // Constructors 46 | Feature() : FeatureBase(), value(), previousValue() { init(nullptr); } 47 | Feature(const char* _name) : FeatureBase(_name), value(), previousValue() { init(nullptr); } 48 | Feature(const char* _name, T _value, uint8_t _eventSettings = true) : FeatureBase(_name), value(_value), previousValue(_value), eventFlags(_eventSettings) { init(&_value); } 49 | Feature(const Feature& other) { 50 | value = other.value; 51 | previousValue = other.previousValue; 52 | eventFlags = other.eventFlags; 53 | saveDelay = other.saveDelay; 54 | eventsVector = other.eventsVector; 55 | } 56 | ~Feature() {} 57 | 58 | protected: // Initialisation 59 | void init(T* init_value) { 60 | // Set the type for the base class 61 | setType(); 62 | 63 | #if defined EFFORTLESS_SPIFFS_VERSION_MAJOR && EFFORTLESS_SPIFFS_VERSION_MAJOR == 2 64 | #if defined ARDUINO 65 | // Add debug to the SPIFFS class 66 | fileSystem.setDebugOutput(logger); 67 | #endif 68 | 69 | // Open values from flash if set to 70 | if (!init_value && eventFlags & FEATURE_SAVE_MASK) openValue(); 71 | #endif 72 | } 73 | 74 | public: // Operators 75 | operator T() { 76 | return value; 77 | } 78 | bool operator()(const T _value) { 79 | return setValue(_value); 80 | } 81 | bool operator()(const T _value, uint8_t _fireEvents) { 82 | return setValue(_value, _fireEvents); 83 | } 84 | Feature& operator=(Feature other) { 85 | std::swap(other.value, value); 86 | std::swap(other.previousValue, previousValue); 87 | std::swap(other.eventFlags, eventFlags); 88 | std::swap(other.saveDelay, saveDelay); 89 | eventsVector.swap(other.eventsVector); 90 | return *this; 91 | } 92 | 93 | template 94 | void operator=(const T _value) { 95 | setValue(_value); 96 | } 97 | 98 | public: // Setters and Getters 99 | virtual T getValue() { 100 | return value; 101 | }; 102 | virtual T getPreviousValue() { 103 | return previousValue; 104 | }; 105 | virtual bool setValue(const T _value, uint8_t _fireEvents = 0xff) { 106 | // Check if the incoming value is different 107 | if (!valueCmp(_value)) { 108 | // Store the new and last value 109 | previousValue = value; 110 | value = _value; 111 | 112 | // Debug output 113 | if (logger) logger->printf("[%s] - Value set to \"%s\"\n", name, toString().c_str()); 114 | 115 | // Override current storage settings 116 | if (_fireEvents == 0xFF) _fireEvents = eventFlags; 117 | 118 | // Call the event callbacks 119 | if (_fireEvents & FEATURE_EVENTS_MASK) fireEventCallbacks(); 120 | 121 | #if defined EFFORTLESS_SPIFFS_VERSION_MAJOR && EFFORTLESS_SPIFFS_VERSION_MAJOR == 2 122 | // Save value 123 | if (_fireEvents & FEATURE_SAVE_MASK) saveValue(); 124 | #endif 125 | 126 | // Return true 127 | return true; 128 | } else { 129 | if (logger) logger->printf("[%s] - Values were the same\n", name); 130 | } 131 | return false; 132 | } 133 | 134 | protected: // Value Comparisson Methods 135 | template 136 | typename FEATURE_ENABLE_IF(!FEATURE_IS_SAME(U, char*) && !FEATURE_IS_SAME(U, const char*), bool) valueCmp(T _value) { 137 | return (_value == value); 138 | } 139 | template 140 | typename FEATURE_ENABLE_IF(FEATURE_IS_SAME(U, char*) || FEATURE_IS_SAME(U, const char*), bool) valueCmp(T _value) { 141 | if (_value && value) { 142 | return strcmp(_value, value) == 0; 143 | } else if (_value && !value) { 144 | return false; 145 | } else { 146 | return true; 147 | } 148 | } 149 | 150 | #ifdef ARDUINO 151 | public: // To String Methods 152 | template 153 | typename FEATURE_ENABLE_IF(!FEATURE_IS_SAME(U, std::string), String) toString() { 154 | return String(value); 155 | } 156 | template 157 | typename FEATURE_ENABLE_IF(FEATURE_IS_SAME(U, std::string), String) toString() { 158 | return String(value.c_str()); 159 | } 160 | #else 161 | public: // To String Methods 162 | template 163 | typename FEATURE_ENABLE_IF(!FEATURE_IS_SAME(U, std::string), std::string) toString() { 164 | return std::string(value); 165 | } 166 | #endif 167 | 168 | #if defined ARDUINOJSON_VERSION_MAJOR && ARDUINOJSON_VERSION_MAJOR == 6 169 | public: // JSON Setters and Getters 170 | virtual bool toJson(JsonObject& json) override { 171 | json[name] = getJsonSafeValue(); 172 | return true; 173 | } 174 | virtual bool fromJson(JsonObject& json, uint8_t _fireEvents = 0xFF) override { 175 | if (json.containsKey(name)) { 176 | // Set the current value using the JSON object 177 | T _value = value; 178 | setValueFromJson(json); 179 | 180 | // Check if the current value has changed 181 | if (!valueCmp(_value)) { 182 | // Set the previous value 183 | previousValue = _value; 184 | 185 | // Log the change 186 | if (logger) logger->printf("[%s] - Value set to \"%s\"\n", name, toString().c_str()); 187 | 188 | // Override current storage settings 189 | if (_fireEvents == 0xFF) _fireEvents = eventFlags; 190 | 191 | // Call the event callbacks 192 | if (_fireEvents & FEATURE_EVENTS_MASK) fireEventCallbacks(); 193 | 194 | #if defined EFFORTLESS_SPIFFS_VERSION_MAJOR && EFFORTLESS_SPIFFS_VERSION_MAJOR == 2 195 | // Store the value 196 | if (_fireEvents & FEATURE_SAVE_MASK) saveValue(); 197 | #endif 198 | 199 | // Return true 200 | return true; 201 | } else { 202 | if (logger) logger->printf("[%s] - Values were the same\n", name); 203 | } 204 | } 205 | return false; 206 | } 207 | 208 | protected: // Convert value to arduino json safe value 209 | template 210 | typename FEATURE_ENABLE_IF(!FEATURE_IS_SAME(U, std::string), T) 211 | getJsonSafeValue() { 212 | return value; 213 | } 214 | template 215 | typename FEATURE_ENABLE_IF(FEATURE_IS_SAME(U, std::string), const char*) 216 | getJsonSafeValue() { 217 | return value.c_str(); 218 | } 219 | 220 | private: // JSON Template Setters 221 | template 222 | typename FEATURE_ENABLE_IF(!FEATURE_IS_SAME(U, std::string) && !FEATURE_IS_SAME(U, char*), bool) 223 | setValueFromJson(JsonObject& jsonObject) { 224 | jsonObject[name] = value = jsonObject[name] | value; 225 | return true; 226 | } 227 | template 228 | typename FEATURE_ENABLE_IF(FEATURE_IS_SAME(U, std::string), bool) 229 | setValueFromJson(JsonObject& jsonObject) { 230 | jsonObject[name] = jsonObject[name] | value.c_str(); 231 | value = jsonObject[name].as(); 232 | return true; 233 | } 234 | template 235 | typename FEATURE_ENABLE_IF(FEATURE_IS_SAME(U, char*), bool) 236 | setValueFromJson(JsonObject& jsonObject) { 237 | size_t valueSize = strlen(value); 238 | // const char* buffer = value; 239 | jsonObject[name] = jsonObject[name] | const_cast(value); 240 | if (snprintf(value, valueSize, "%s", jsonObject[name].as())) return true; 241 | return false; 242 | } 243 | #endif 244 | 245 | #if defined EFFORTLESS_SPIFFS_VERSION_MAJOR && EFFORTLESS_SPIFFS_VERSION_MAJOR == 2 246 | public: // SPIFFS Methods 247 | void allowSaving(bool _saveValues = true) { 248 | (_saveValues) ? eventFlags |= FEATURE_SAVE_MASK : eventFlags &= FEATURE_SAVE_MASK; 249 | } 250 | void setSaveTimeout(uint32_t _delay) { 251 | saveDelay = _delay; 252 | } 253 | virtual inline void getFilename(char* _outputString) { 254 | char filename[32] = "/"; 255 | strncat(filename, name, 30); 256 | strcpy(_outputString, filename); 257 | } 258 | virtual bool openValue() { 259 | // Get the file name 260 | char filename[32]; 261 | getFilename(filename); // 80us 262 | 263 | // Open the value from teh filesystem 264 | T openedValue; 265 | bool retval = fileSystem.openFromFile(filename, openedValue); 266 | 267 | // Set the value from the contents from the filesystem 268 | if (retval) { 269 | if (logger) logger->printf("[%s] - Opened value from \"%s\"\n", name, filename); 270 | return setValue(openedValue, FEATURE_DO_NOT_SAVE_VALUES); 271 | } else { 272 | if (logger) logger->printf("[%s] - Failed to open value from \"%s\"\n", name, filename); 273 | } 274 | 275 | // Return status 276 | return retval; 277 | }; 278 | virtual bool saveValue() { 279 | #ifdef TICKER_H 280 | saveTimer.detach(); 281 | saveTimer.once_ms*>( 282 | saveDelay, [=](Feature* _this) { 283 | // Get filename 284 | char filename[32]; 285 | _this->getFilename(filename); 286 | 287 | // Save to file 288 | if (_this->fileSystem.saveToFile(filename, _this->value)) { 289 | if (_this->logger) _this->logger->printf("[%s] - Saving value to \"%s\"\n", _this->name, filename); 290 | } else { 291 | if (_this->logger) _this->logger->printf("[%s] - Failed to save value to \"%s\"\n", _this->name, filename); 292 | } 293 | }, 294 | this); 295 | delay(1); 296 | 297 | return true; 298 | #else 299 | // Create a file name 300 | char filename[32]; 301 | getFilename(filename); 302 | 303 | // Save to file 304 | bool retval = fileSystem.saveToFile(filename, value); 305 | 306 | // Log events 307 | if (retval) { 308 | if (logger) logger->printf("[%s] - Saving value to \"%s\"\n", name, filename); 309 | } else { 310 | if (logger) logger->printf("[%s] - Failed to save value to \"%s\"\n", name, filename); 311 | } 312 | 313 | // Return status 314 | return retval; 315 | #endif 316 | }; 317 | #endif 318 | 319 | public: // Event Callback Methods 320 | void allowEvents(bool _fireEvents = true) { 321 | (_fireEvents) ? eventFlags |= FEATURE_EVENTS_MASK : eventFlags &= FEATURE_EVENTS_MASK; 322 | } 323 | void addEventCallback(eventCallback _callback) { 324 | eventsVector.push_back(_callback); 325 | } 326 | bool removeLastCallback() { 327 | eventsVector.pop_back(); 328 | return true; 329 | } 330 | bool removeSpecificCallback(uint32_t _offset) { 331 | if (_offset < eventsVector.size()) { 332 | eventsVector.erase(eventsVector.begin() + _offset); 333 | return true; 334 | } else { 335 | return false; 336 | } 337 | } 338 | bool removeAllCallbacks() { 339 | eventsVector.clear(); 340 | return true; 341 | } 342 | 343 | protected: // Event Callback Execution 344 | void fireEventCallbacks() { 345 | if (logger) logger->printf("[%s] - Firing events\n", name); 346 | for (auto& callback : eventsVector) { 347 | callback(value); 348 | } 349 | } 350 | 351 | #ifdef ARDUINO 352 | public: // Debug Callback Methods 353 | void setDebugOutput(Print* _callback) { 354 | if (_callback) logger = _callback; 355 | } 356 | #endif 357 | 358 | protected: // Private Stored Data 359 | T value; 360 | T previousValue; 361 | uint8_t eventFlags = FEATURE_FIRE_EVENTS_SAVE_VALUES; 362 | uint32_t saveDelay = 0; 363 | eventCallbackVector eventsVector; 364 | 365 | #ifdef ARDUINO 366 | Print* logger = FEATURE_DEBUG_OBJECT; 367 | #endif 368 | 369 | #ifdef TICKER_H 370 | Ticker saveTimer; 371 | #endif 372 | 373 | #if defined EFFORTLESS_SPIFFS_VERSION_MAJOR && EFFORTLESS_SPIFFS_VERSION_MAJOR == 2 374 | eSPIFFS fileSystem; 375 | #endif 376 | }; 377 | } // namespace FeatureVariables 378 | 379 | #endif --------------------------------------------------------------------------------