diff --git a/dist/Release_notes.txt b/dist/Release_notes.txt index b90cb1b356..cd5bf594d6 100644 --- a/dist/Release_notes.txt +++ b/dist/Release_notes.txt @@ -1,3 +1,203 @@ +------------------------------------------------- +Changes in release mega-20230930 (since mega-20230822) +------------------------------------------------- + +Release date: za 30 sep 2023 15:46:23 CEST + +Adam Groszer (1): + added InfluxDB v2 HTTP API docs and some other minors + +Fabio Ancona (3): + Update README.md adding "Climate" + [DOC] Update P014 to avoid to use this Plugin with SHT2x devices + Update P014.rst + +Michael Schröder (1): + Ignore more variants of own Custom.h + +TD-er (83): + Update issue templates + [ADS1x15] Support ADS1015 & add sample rate selector + [ADS1x15] Add ADS1015/ADS1115 sensor type detection to task config page + [Cleanup] Add macro NR_ELEMENTS for array counting + [ADS1x15] Add option to select multiple task value outputs + [ADS1x15] Simplify storage of settings + [ADS1x15] Update documentation + [ADS1x15] Update documentation + [ADS1x15] Replace "ADS1115" with "ADS1x15" in docs and I2C scan result + [ADS1x15] Fix PBC -> PCB typo in documentation + [BMP3xx] Add support for Bosch BMP384/388/390 temp/pressure modules + [BMP3xx] Fix saving I2C address + [BMP3xx] Perform a few reads after init to initialize the IIR filter + [BMP3xx] Add detected chip + altitude compensation + [BMP3xx] Document P154 BMP388/BMP390 + [BME280] Show detected sensor model like is done for ADS1x15 and BMP3xx + [Scheduler] Cleanup of Scheduler code + [Scheduler] Allow to schedule task calls to specific functions + [Scheduler] Split Various Scheduler timer types into separate .cpp files + [Cleanup] Fix build issue in WOL plugin + [Scheduler] Remove unneeded scheduler call type + [Scheduler] Cleanup includes and fix decode() + [Documentation] Document ESPEasy Functional Blocks + [Documentation] Add ESPEasy Functional block diagram + [Docs] Mark arrow between controller and MQTT broker as bidirectional + [Docs] Clarify some arrows in ESPEasy block diagram + [Task] Revert 'soft disable' task (for now...) + [Cleanup] Make deviceIndex_t a strict class + [Cleanup] Only allow PLUGIN_ADD to change Device[] vector + [Cleanup] Make pluginID_t a strict class + [Cleanup] Fix build error on ETH builds with pluginID_t + [Cleanup] Partial conversion npluginID_T to class + [Cleanup] Complete convert npluginID_t to strict class + [Cleanup] Reduce build size on ESP8266 making DeviceVector less strict + [Cleanup] Fix crashing compiler on ESP8266 builds + [Cleanup] Reduce build size to 'undo' extra added bytes in this cleanup + [Cleanup] Further reduction of build size + [Cleanup] Fix build on ESP8266 Collection A + [Cleanup] Fix build error on ESP8266 Display + [Cleanup] Reduce build size by moving small functions to .h files + [Cleanup] Use constexpr for static pluginID compares + [Cleanup] Use more constexpr for static pluginID compares + [Controller] Simplify ESPEasy core administration of CPlugins + [Cleanup] Misc code cleanup + add comments from PVS Studio + [Plugin] Simplify ESPEasy plugin registration + reduce build size + [Cleanup] Simplify code for return values internal ESPEasy commands + [Cleanup] Simplify get/set functions of VariousBits1 in SettingsStruct + [Cleanup] Remove no longer needed globals/Protocol.h/.cpp + [Neopixel] Fix build error signed/unsigned compare + [Cleanup] Apply GPIO log optimizations to reduce build size (@Ton) + [Cleanup] Fix storing new task (taskenable still doesn't work...) + [Cleanup] Fix silly bug setting tasks disabled. + [Cleanup] Reduce size of embedded lookup table to only included plugins + [Cleanup] Do not use 'Number' in DeviceStruct class + [Cleanup] Simplify DeviceIndex_sorted + [Cleanup] Fix merge issue + [Cleanup] Minor code tweaks to reduce build size + [Debug] Add "minimal" special build for analysis + [PVS Studio] Fix issues found by PVS Studio + [Cleanup] Reduce build size by moving isInitialized() to .h files + [Servo] Fix servo for ESP32-S2/C3 + [Cleanup] Reduce build size for bitlevel access in Controller Settings + [Cleanup] Remove code redundancy from DevicesPage and UploadPage + [PluginStats] Fix showing stats when using 256 samples buffer + [Cleanup] Fix crash when adding new controller + [Cleanup] Disable drawing partition layout for limit build size builds + [Cleanup] Fix isNaN check in thermocouple plugin + [Debug] Add special debug build env for Display builds + [Cleanup] Reduce build size making Improv smaller + [Debug] Set special debug 'display' build to BUILD_NO_DEBUG + [Cleanup] Shorten (very) elaborate notes in P039 ThermoCouple + [Cleanup] clarify single event note + reduce GPIO command logs + [I2C scanner] Fix missing ++x; causing reboot during I2C scan + [ADC] Fix handling negative ADC values from Hall Effect sensor + [Notifications] Fix deleting notifier + [GPIO] Fix check for valid GPIO + [RTTTL] Fix parsing correct command argument + [RTTTL] Fix argument index for melody + prevent crash when changing song + [RTTTL] Fix stupid mix-up of old/new command argument parsing + [RTTTL] Only check for old/new syntax in the 1st argument + [RTTTL] I need some coffee.... + [Internal temp] Fix crashes on ESP32-S2/S3/C3 + filter + [ESP32 temp] Fix specific includes for ESP32-S2 + +Ton Huisman (77): + [P036] Show current display content on Devices page + [P036] Wrap on-display output in `pre` tag to keep spaces and similar formatting + [P036] Remove unreachable code, other optimizations + [addHtmlDiv] Add optional `attribute` argument + [P036] Reduce Show Display font-size to 75%, code optimizations + [P023] Add Show Values for on-Display content, code improvements and optimizations + [P036] Make Interval optional + [P036] Update documentation + [P023] Add documentation + [P023] Updated screenshot + [P036] Add center/right align in preview, make preview compile-optional, code optimizations + [P036] Only disable alignment feature for 1M builds + [P036] Add new compile-time directives to Custom-sample.h + [Build] Make `Climate` build fit + [P036] Code optimizations + [P023] Minor code optimizations + [P036] Add optional user-defined display headers + [P036] Change compile-time defines to bool, change enums to uint8_t type, update changelog + [P036] Fix copy/paste error for `userDef2` + [P028] Add detection-mode setting + [P028] Ignore humidity if Sensor model BMP280 is selected + [Devices] Add event PLUGIN_WEBFORM_LOAD_ALWAYS that is also called for remote data-feed devices + [P028] Fix VType matching for remote data-feed, implement new WEBFORM_LOAD_ALWAYS event + [P028] Update documentation + [Controllers] Show correct data (or nothing) for controllers without Host or Port settings + [Controllers] Code improvement + [P036] Enable Userdefined headers by default for LIMIT_BUILD_SIZE builds + [P104] Add Dot subcommand to draw individual dots in a zone + [P104] Update documentation + [P104] Code improvements and a bugfix + [P028] Fix merge conflict + [P036] Code improvements using `constexpr` + [Rules] Improving removal of comments + [Rules] Separate comment removal from reading a line + [Release] `release.yml` script improvements + [P036] Fix merge conflicts, small source formatting improvements + [SD-card] Bugfix: Opening a file from SD-card doesn't always work + [SD-browser] Code improvements + [P153] Fix incorrect Sensor_VType setting + [SD-Card] Show SD-card CS GPIO pin in pin-selection + [P036] Bugfix in documentation-syntax + [P051] Fix device being automatically disabled and I2C scan issue + [I2C Scan] Make I2C scan more robust by adding extra wakeup + [Docs] Add I2C Scan documentation + [Docs] Additional I2C Scan documentation (mux) + [Docs] Rules example for PostToHTTP/PutToHTTP + [UI] Bugfix: Don't show - None - [SD-CARD] in GPIO selector when SD-Card is not configured (improves #4780) + [Build] Restore RTTTL feature in ESP8266 Climate build + [RTTTL] Add Async play and other improvements + [RTTTL] update documentation + [GPIO] Decrease buildsize by optimizing logging + [RTTTL] Add FEATURE_ flags to Custom-sample.h + [GPIO] Adopt optimized logging improvements (@gijs) + [Build] Add Collection G builds + [Build] Move [P154] BMP3xx to Collection G + [Build] Add checks for some external defined compiletime settings + [P036] Code formatting and optimizations + [P037] Build: Include all features in ESP32 builds + [P073] Reduce logging for BUILD_NO_DEBUG builds to reduce size + [P036] Updates for documentation + [P026][Sysvars] Add Internal temperature sensor value for ESP32 + [P026][Sysvars] Feedback and fixes + [P026][Sysvars] Fix typo in comment + [P026] Add Get Config feature for all values + [P026] Add documentation + [Scripts] Handle 8M builds in WebFlasher + [P026][Sysvars] Avoid temperature reading errors on ESP32 classic (2 tries only) + [P026][Sysvars] Read temperature every second on ESP32 classic for better results + [Build] Try to use real branch name on GH Actions builds + [Build] Ignore Docs commits to trigger an Actions run + [P026][Sysvars] Explicitly disable FEATURE_INTERNAL_TEMPERATURE for ESP8266 + [P026][Sysvars] Add delay() call + [Build][P026] Update documentation (without Actions run...) + [P037] Update documentation + [RTTTL] Add events for RTTTL play (async only), fix intermitted/skipped async playing + [RTTTL] Restore Cancelled event, update documentation + [P000 Docs] Update some internal links + +chromoxdor (2): + Fixed error in section TaskValueSet + Update GPIO.cpp + +dependabot[bot] (1): + Bump actions/checkout from 3 to 4 + +uwekaditz (9): + [P036] Add ticker as scroll option + Compiler error if P036_SendEvent was not set + Calculation for ticker IdxStart and IdxEnd was wrong for 64x48 display, Start page updates after network has connected in PLUGIN_ONCE_A_SECOND + Some bug fixes (only 1 line displayed) + Removed unnecessary clear() functions + Merged with mega from 2023-08-25 + Wrong #ifdef P036_SEND_EVENTS + CHG: Disable scrolling or ticker if new line content received (PLUGIN_WRITE) + CHG: Code reduced if P036_ENABLE_TICKER is not used + + ------------------------------------------------- Changes in release mega-20230822 (since mega-20230623) ------------------------------------------------- diff --git a/docs/source/Controller/C011.rst b/docs/source/Controller/C011.rst index d37662a46e..79d78022a3 100644 --- a/docs/source/Controller/C011.rst +++ b/docs/source/Controller/C011.rst @@ -62,6 +62,11 @@ You can use this placeholders in http header and in the http body: * ``%vname4%`` will be replaced with Valuename 4 * ``%val1%`` will be replaced with the value 1 of the device which is sending data to this controller * ``%val2%`` will be replaced with the value 2 of the device which is sending data to this controller +* ``%val3%`` will be replaced with the value 3 of the device which is sending data to this controller +* ``%val4%`` will be replaced with the value 4 of the device which is sending data to this controller +* ``%unixtime%`` will be replaced with the Unix time (seconds since epoch) + +See also System variables You can also write things like this: @@ -71,8 +76,8 @@ You can also write things like this: Examples -------- -InfluxDB HTTP Api -^^^^^^^^^^^^^^^^^ +InfluxDB v1 HTTP API +^^^^^^^^^^^^^^^^^^^^ * HTTP Method: ``POST`` * HTTP URI: ``write?db=testdb`` @@ -89,9 +94,33 @@ InfluxDB HTTP Api See also `InfluxDB API description `_ +InfluxDB v2 HTTP API +^^^^^^^^^^^^^^^^^^^^ + +* HTTP Method: ``POST`` +* HTTP URI: ``api/v2/write?bucket=yourbucket&org=yourorg`` + +* HTTP Header: + +.. code-block:: none + + Content-Type: application/x-www-form-urlencoded + Authorization: Token yourtoken + +* HTTP Body: + +.. code-block:: none + + esp,unit=%sysname%,device=%tskname%,sensor=%vname1% value=%val1% %unixtime%%LF%%2%esp,unit=%sysname%,device=%tskname%,sensor=%vname2% value=%val2% %unixtime%%/2%%LF%%3%esp,unit=%sysname%,device=%tskname%,sensor=%vname3% value=%val3% %unixtime%%/3%%LF%%4%esp,unit=%sysname%,device=%tskname%,sensor=%vname4% value=%val4% %unixtime%%/4%%LF% + +Make sure the body is all ONE single line, InfluxDB seems to be very picky about newlines. + +Response should be 204 on success. + + Nettemp HTTP Api ^^^^^^^^^^^^^^^^ untested but should work with something like this: -``/receiver.php?device=ip&type=%1%%vname1%%/1%%2%;%vname2%%/2%%3%;%vname3%%/3%%4%;%vname4%%/4%&value=%1%%val1%%/1%%2%;%val2%%/2%%3%;%val3%%/3%%4%;%val4%%/4%`` \ No newline at end of file +``/receiver.php?device=ip&type=%1%%vname1%%/1%%2%;%vname2%%/2%%3%;%vname3%%/3%%4%;%vname4%%/4%&value=%1%%val1%%/1%%2%;%val2%%/2%%3%;%val3%%/3%%4%;%val4%%/4%`` diff --git a/docs/source/Plugin/P000.rst b/docs/source/Plugin/P000.rst index 8726e08c70..d6cd32986b 100644 --- a/docs/source/Plugin/P000.rst +++ b/docs/source/Plugin/P000.rst @@ -27,6 +27,21 @@ Supported hardware |P000_usedby_GPIO|, |P000_usedby_RTTTL|, |P000_usedby_Relay|, |P000_usedby_Servo|, |P000_usedby_LevelConverter| +Switch: :ref:`P001_Switch_page` + +Doorswitch: :ref:`P001_Door_switch_page` + +Relay: :ref:`P000_Relay_page` + +PIR: :ref:`P001_PIR_sensor_page` + +Servo: :ref:`P000_Servo_motor_page` + +Buzzer: :ref:`P000_Buzzer_page` + +Speaker RTTTL: :ref:`P000_Speaker_page` + +Piezo element: :ref:`P000_Piezo_element_page` Commands available ------------------ diff --git a/docs/source/Plugin/P000_Buzzer_RTTTL.rst b/docs/source/Plugin/P000_Buzzer_RTTTL.rst index 11c0d77dad..399ccb0696 100644 --- a/docs/source/Plugin/P000_Buzzer_RTTTL.rst +++ b/docs/source/Plugin/P000_Buzzer_RTTTL.rst @@ -57,7 +57,7 @@ From ESP Easy v2.0.0 its possible to play melodies via `RTTTL /control?cmd=rtttl,14:d=4,o=5,b=112:8a,8a,a,8a,8a,a,8a,8c6,8f.,16g,2a,8a-,8a-,8a-.,16a-,8a-,8a,8a.,16a,8a,8g,8g,8a,g,c6 + http:///control?cmd=rtttl,14,:d=4,o=5,b=112:8a,8a,a,8a,8a,a,8a,8c6,8f.,16g,2a,8a-,8a-,8a-.,16a-,8a-,8a,8a.,16a,8a,8g,8g,8a,g,c6 This plays a melody on pin 14. @@ -68,7 +68,7 @@ To make a boot-sound on startup, create a rule like this: .. code-block:: none On System#Boot do - rtttl,14:d=10,o=6,b=180,c,e,g + rtttl,14,:d=10,o=6,b=180,c,e,g endon `A place to find more tunes `_, all saved as txt files. @@ -91,6 +91,11 @@ Commands .. include:: P001_commands_RTTTL.repl +Events +------ + +.. include:: P001_events_RTTTL.repl + Where to buy ------------ diff --git a/docs/source/Plugin/P000_Piezo_element_RTTTL.rst b/docs/source/Plugin/P000_Piezo_element_RTTTL.rst index cd28215a05..59d2f49692 100644 --- a/docs/source/Plugin/P000_Piezo_element_RTTTL.rst +++ b/docs/source/Plugin/P000_Piezo_element_RTTTL.rst @@ -54,7 +54,7 @@ From ESP Easy v2.0.0 its possible to play melodies via `RTTTL /control?cmd=rtttl,14:d=4,o=5,b=112:8a,8a,a,8a,8a,a,8a,8c6,8f.,16g,2a,8a-,8a-,8a-.,16a-,8a-,8a,8a.,16a,8a,8g,8g,8a,g,c6 + http:///control?cmd=rtttl,14,:d=4,o=5,b=112:8a,8a,a,8a,8a,a,8a,8c6,8f.,16g,2a,8a-,8a-,8a-.,16a-,8a-,8a,8a.,16a,8a,8g,8g,8a,g,c6 This plays a melody on pin 14. @@ -65,7 +65,7 @@ To make a boot-sound on startup, create a rule like this: .. code-block:: none On System#Boot do - rtttl,14:d=10,o=6,b=180,c,e,g + rtttl,14,:d=10,o=6,b=180,c,e,g endon `A place to find more tunes `_, all saved as txt files. diff --git a/docs/source/Plugin/P000_Speaker_RTTTL.rst b/docs/source/Plugin/P000_Speaker_RTTTL.rst index 67c860fda1..ecaccd9005 100644 --- a/docs/source/Plugin/P000_Speaker_RTTTL.rst +++ b/docs/source/Plugin/P000_Speaker_RTTTL.rst @@ -54,7 +54,7 @@ From ESP Easy v2.0.0 its possible to play melodies via `RTTTL /control?cmd=rtttl,14:d=4,o=5,b=112:8a,8a,a,8a,8a,a,8a,8c6,8f.,16g,2a,8a-,8a-,8a-.,16a-,8a-,8a,8a.,16a,8a,8g,8g,8a,g,c6 + http:///control?cmd=rtttl,14,:d=4,o=5,b=112:8a,8a,a,8a,8a,a,8a,8c6,8f.,16g,2a,8a-,8a-,8a-.,16a-,8a-,8a,8a.,16a,8a,8g,8g,8a,g,c6 This plays a melody on pin 14. @@ -65,7 +65,7 @@ To make a boot-sound on startup, create a rule like this: .. code-block:: none On System#Boot do - rtttl,14:d=10,o=6,b=180,c,e,g + rtttl,14,:d=10,o=6,b=180,c,e,g endon `A place to find more tunes `_, all saved as txt files. diff --git a/docs/source/Plugin/P001_commands_RTTTL.repl b/docs/source/Plugin/P001_commands_RTTTL.repl index 6fc613a9e2..7b2e0ef9d9 100644 --- a/docs/source/Plugin/P001_commands_RTTTL.repl +++ b/docs/source/Plugin/P001_commands_RTTTL.repl @@ -43,4 +43,10 @@ Supported hardware: |P000_usedby_RTTTL| (Ringtones etc.) ``rtttl,14,test:d=8,o=5,b=180,c6,b,c6,p,g,g-`` `More RTTTL Ringtone Downloads `_ + + Since 2023-09-15: + + The ``rtttl`` command, by default, now uses the `AnyRtttl library `_, allowing asynchronous handling of the command. This has the advantage of being non-blocking, allowing ESPEasy to handle other tasks while the tune is being played, but comes with the disadvantage of the output possibly being interrupted/delayed by other (possibly blocking) tasks and events, so the music may not play continuously. + + Also, the previous implementation may not have stopped sound correctly after playing a song, but as this has been corrected, so there is no longer a need to turn off the GPIO after playing a song. " diff --git a/docs/source/Plugin/P001_events_RTTTL.repl b/docs/source/Plugin/P001_events_RTTTL.repl new file mode 100644 index 0000000000..6822343880 --- /dev/null +++ b/docs/source/Plugin/P001_events_RTTTL.repl @@ -0,0 +1,41 @@ +.. csv-table:: + :header: "Event", "Example" + :widths: 30, 20 + + " + ``RTTTL#Started`` + Event generated when a melody/song is played using the Async RTTTL method. + "," + + .. code-block:: none + + on RTTTL#Started do + GPIO,13,1 // Turn on LED while playing a song + endon + + " + " + ``RTTTL#Finished`` + Event generated when the playback of a melody/song is finished using the Async RTTTL method. + "," + + .. code-block:: none + + on RTTTL#Finished do + GPIO,13,0 // Turn off LED after playing a song + event,nextSong // Select the next song to play + endon + + " + " + ``RTTTL#Cancelled`` + Event generated when the currently playing melody/song is cancelled by starting a new song using the Async RTTTL method. + "," + + .. code-block:: none + + on RTTTL#Cancelled do + GPIO,13,0 // Turn off LED + endon + + " diff --git a/docs/source/Plugin/P014.rst b/docs/source/Plugin/P014.rst index 546529e664..a87b8b0424 100644 --- a/docs/source/Plugin/P014.rst +++ b/docs/source/Plugin/P014.rst @@ -57,9 +57,9 @@ Do not use the single ended circuit as the Vout is connected directly to ground: **Note:** -The plugin reads the CHIP ID to determine if the ADC is available. +The plugin reads the CHIP ID to determine if the ADC is available. This Plugin is NOT compatible with SHT20, for SHT2x devices use Plugin 122. -SHT20 and HTU21D devices while using the same reading protocol do not have a CHIP ID capability and not able to detect them. +HTU21D devices while using the same reading protocol do not have a CHIP ID capability and not able to detect them. |P014_usedby| diff --git a/docs/source/Plugin/P037.rst b/docs/source/Plugin/P037.rst index 00c1d5096f..184813e953 100644 --- a/docs/source/Plugin/P037.rst +++ b/docs/source/Plugin/P037.rst @@ -31,7 +31,7 @@ Only numbers can be stored in variables (Plugin Generic - Dummy Device), so eith Device configuration -------------------- -NB: To save space it is possible that not all features are available in all builds. All features are available in ``normal`` builds with 2MB or more flash size, ``max`` builds, and when self-building in a ``Custom`` build. The screenshots are taken including all options. +NB: To save space it is possible that not all features are available in all builds. All features are available in ``normal`` builds with 2MB or more flash size, ``max`` builds, when self-building in a ``Custom`` build, and in all ``ESP32`` builds. The screenshots are taken including all options. The options that can be included or excluded are: diff --git a/docs/source/Reference/Events.rst b/docs/source/Reference/Events.rst index e8959a5cf4..63c11999b5 100644 --- a/docs/source/Reference/Events.rst +++ b/docs/source/Reference/Events.rst @@ -14,6 +14,12 @@ Plugin based events Besides the internal events there's also plugin specific events. These are listed here below. +P000 (core) :ref:`P000_Buzzer_page` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. include:: ../Plugin/P001_events_RTTTL.repl + + .. P001 :ref:`P001_page` .. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/lib/AnyRtttl/AUTHORS b/lib/AnyRtttl/AUTHORS new file mode 100644 index 0000000000..ef27329702 --- /dev/null +++ b/lib/AnyRtttl/AUTHORS @@ -0,0 +1,5 @@ +# This file contains a list of people who've made contribution to +# the project. People who commit code are encouraged to add +# their names here. Please keep the list sorted by first names. + +Antoine Beauchamp diff --git a/lib/AnyRtttl/CHANGES b/lib/AnyRtttl/CHANGES new file mode 100644 index 0000000000..491a02e4d1 --- /dev/null +++ b/lib/AnyRtttl/CHANGES @@ -0,0 +1,32 @@ +Changes for 2.3 + +* Fixed issue #2 - Support for PROGMEM / FLASH melodies in non-blocking mode. + + +Changes for 2.2.1: + +* Fixed issue #4: error compiling example code. +* Fixed issue #5: Refactor build process to use Arduino CLI instead of Arduino IDE. + + +Changes for 2.2.0: + +* New feature: Build option `ANYRTTTL_BUILD_EXAMPLES` to enable/disable building AnyRtttl examples. +* New feature: Changed file/folder structure to be compatible with Arduino Library Manager. +* New feature: Using RapidAssist 0.5.0 and win32Arduino 2.3.1. + + +Changes for 2.1.229: + +* New feature: Implemented support for RTTTL in Program Memory (PROGMEM). + + +Changes for 2.0.179: + +* Library converted to AnyRtttl. +* First github release. +* Code originally release at http://www.end2endzone.com/anyrtttl-a-feature-rich-arduino-library-for-playing-rtttl-melodies/ + + +Changes for 1.0.0: +* Initial release of NonBlockingRtttl. diff --git a/lib/AnyRtttl/CMakeLists.txt b/lib/AnyRtttl/CMakeLists.txt new file mode 100644 index 0000000000..1e51d78965 --- /dev/null +++ b/lib/AnyRtttl/CMakeLists.txt @@ -0,0 +1,160 @@ +cmake_minimum_required(VERSION 3.4.3) +project(AnyRtttl) + +# Set the output folder where your program will be created +set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) +set( LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) + +############################################################################################################################################## +# Functions +############################################################################################################################################## +function(GIT_EXTERNAL DIR REPO_URL TAG) + # Find the name of the repository + get_filename_component(REPO_NAME ${REPO_URL} NAME_WE) + + # Compute output directory + set(REPO_DIR "${DIR}/${REPO_NAME}") + + if (EXISTS "${REPO_DIR}") + message(STATUS "Repository ${REPO_NAME} already exists in directory ${DIR}. Skipping git clone command.") + return() + endif() + + # Clone the repository + message(STATUS "git clone ${REPO_URL} ${REPO_DIR}") + execute_process( + COMMAND "${GIT_EXECUTABLE}" clone ${REPO_URL} ${REPO_DIR} + RESULT_VARIABLE returncode ERROR_VARIABLE error + WORKING_DIRECTORY "${DIR}") + if(returncode) + message(FATAL_ERROR "Clone failed: ${error}\n") + endif() + message(STATUS "git clone completed") + + # Checking out the required tag + message(STATUS "git checkout ${TAG}") + execute_process( + COMMAND "${GIT_EXECUTABLE}" checkout ${TAG} + RESULT_VARIABLE returncode ERROR_VARIABLE error + WORKING_DIRECTORY "${REPO_DIR}") + if(returncode) + message(FATAL_ERROR "Checkout failed: ${error}\n") + endif() + message(STATUS "git checkout completed") + + # Delete the .git folder to simulate an export of the repository + message(STATUS "Deleting ${REPO_DIR}/.git") + file(REMOVE_RECURSE "${REPO_DIR}/.git") + +endfunction() + +function(add_example name) + # Create custom example.cpp file which includes the ino sketch file. + SET(SOURCE_INO_FILE "${PROJECT_SOURCE_DIR}/examples/${name}/${name}.ino") + CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/examples.cpp.in" "${PROJECT_BINARY_DIR}/${name}/examples.cpp") + + add_executable(${name} + ${ARDUINO_LIBRARY_SOURCE_FILES} + ${SOURCE_INO_FILE} + "${PROJECT_BINARY_DIR}/${name}/examples.cpp" + ) + + target_include_directories(${name} PRIVATE ${PROJECT_SOURCE_DIR}/src ${BITREADER_SOURCE_DIR} win32arduino ) + target_link_libraries(${name} PRIVATE win32arduino rapidassist) + + set_property(GLOBAL PROPERTY USE_FOLDERS ON) + set_target_properties(${name} PROPERTIES FOLDER "examples") + + if(WIN32) + # 1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\include\ostream(743,1): warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc + # 1>D:\dev\AnyRtttl\master\third_parties\win32Arduino\install\include\win32arduino-2.4.0\SerialPrinter.h(202): message : see reference to function template instantiation 'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,const char *)' being compiled + set_target_properties(${name} PROPERTIES COMPILE_FLAGS "/wd4530") + endif() + +endfunction() + +############################################################################################################################################## +# Dependencies +############################################################################################################################################## +find_package(GTest REQUIRED) +find_package(rapidassist 0.5.0 REQUIRED) +find_package(win32arduino 2.3.1 REQUIRED) +find_package(Git REQUIRED) + +# Arduino BitReader library dependency +file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/external") +GIT_EXTERNAL("${CMAKE_CURRENT_SOURCE_DIR}/external" "http://github.com/end2endzone/BitReader.git" "1.3.0") +set(BITREADER_SOURCE_DIR "${PROJECT_SOURCE_DIR}/external/BitReader/src") + +############################################################################################################################################## +# Project settings +############################################################################################################################################## + +# Build options +option(ANYRTTTL_BUILD_EXAMPLES "Build all example projects" OFF) + +# Prevents annoying warnings on MSVC +if (WIN32) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) +endif() + +# Find all library source and unit test files +file( GLOB ARDUINO_LIBRARY_SOURCE_FILES ${PROJECT_SOURCE_DIR}/src/*.cpp ${PROJECT_SOURCE_DIR}/src/*.h ${BITREADER_SOURCE_DIR}/*.cpp ${BITREADER_SOURCE_DIR}/*.h) +file( GLOB ARDUINO_LIBRARY_TEST_FILES ${PROJECT_SOURCE_DIR}/test/*.cpp ${PROJECT_SOURCE_DIR}/test/*.h ) + +# Create unit test executable +add_executable(anyrtttl_unittest + ${ARDUINO_LIBRARY_SOURCE_FILES} + ${ARDUINO_LIBRARY_TEST_FILES} +) + +#include directories +target_include_directories(anyrtttl_unittest + PRIVATE ${PROJECT_SOURCE_DIR}/src # Arduino Library folder + ${GTEST_INCLUDE_DIR} + ${BITREADER_SOURCE_DIR} + win32arduino +) + +# Unit test projects requires to link with pthread if also linking with gtest +if(NOT WIN32) + set(PTHREAD_LIBRARIES -pthread) +endif() + +#link libraries +target_link_libraries(anyrtttl_unittest PRIVATE win32arduino rapidassist ${PTHREAD_LIBRARIES} ${GTEST_LIBRARIES} ) + +if(WIN32) + # 1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\include\ostream(743,1): warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc + # 1>D:\dev\AnyRtttl\master\third_parties\win32Arduino\install\include\win32arduino-2.4.0\SerialPrinter.h(202): message : see reference to function template instantiation 'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,const char *)' being compiled + set_target_properties(anyrtttl_unittest PROPERTIES COMPILE_FLAGS "/wd4530") +endif() + +# Copy `expected_call_stack.log` to expected locations +configure_file(${PROJECT_SOURCE_DIR}/test/expected_call_stack.log ${PROJECT_BINARY_DIR}/expected_call_stack.log COPYONLY) +set(TEST_FILES_BINARY_DIR ${EXECUTABLE_OUTPUT_PATH}) +if(WIN32) + set(TEST_FILES_BINARY_DIR ${TEST_FILES_BINARY_DIR}/${CMAKE_CFG_INTDIR}) +endif() +add_custom_command( + TARGET anyrtttl_unittest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${PROJECT_SOURCE_DIR}/test/expected_call_stack.log + ${TEST_FILES_BINARY_DIR}/expected_call_stack.log) + +############################################################################################################################################## +# Add all samples to the project unless the user has specified otherwise. +############################################################################################################################################## +if(ANYRTTTL_BUILD_EXAMPLES) + add_example("Basic") + add_example("BlockingProgramMemoryRtttl") + add_example("BlockingRtttl") + add_example("BlockingWithNonBlocking") + add_example("NonBlockingProgramMemoryRtttl") + add_example("NonBlockingRtttl") + add_example("NonBlockingStopBeforeEnd") + add_example("Play10Bits") + add_example("Play16Bits") + add_example("Rtttl2Code") +endif() + \ No newline at end of file diff --git a/lib/AnyRtttl/INSTALL.md b/lib/AnyRtttl/INSTALL.md new file mode 100644 index 0000000000..017401126e --- /dev/null +++ b/lib/AnyRtttl/INSTALL.md @@ -0,0 +1,105 @@ +# Install # + +The library can be found, installed, or updated from the Arduino IDE using the official Arduino Library Manager (available from IDE version 1.6.2). + + +The library can be installed on the system by following the same steps as with other Arduino library. + +Refer to [Installing Additional Arduino Libraries](https://www.arduino.cc/en/Guide/Libraries) tutorial for details on how to install a third party library. + + + + +# Dependencies # + +The AnyRtttl library have no dependencies on other Arduino library. However, some examples of the library requires external dependencies. + +The following Arduino Library must be installed on the system to use the library examples: + +* [BitReader v1.3.0](https://github.com/end2endzone/BitReader/tree/1.3.0) + + + + +# Build # + +The library unit tests can be build on Windows/Linux platform to maintain the product stability and level of quality. + +This section explains how to compile and build the software and how to get a test environment ready. + + + +## Prerequisites ## + + + +### Software Requirements ### + +The following software must be installed on the system before compiling unit test source code: + +* [Google C++ Testing Framework v1.8.0](https://github.com/google/googletest/tree/release-1.8.0) +* [RapidAssist v0.5.0](https://github.com/end2endzone/RapidAssist/tree/0.5.0) +* [win32Arduino v2.3.1](https://github.com/end2endzone/win32Arduino/tree/2.3.1) +* [CMake](http://www.cmake.org/) v3.4.3 (or newer) + + + +### Linux Requirements ### + +These are the base requirements to build source code: + + * GNU-compatible Make or gmake + * POSIX-standard shell + * A C++98-standard-compliant compiler + + + +### Windows Requirements ### + +* Microsoft Visual C++ 2010 or newer + + + +## Build steps ## + +The AnyRtttl unit test uses the CMake build system to generate a platform-specific build environment. CMake reads the CMakeLists.txt files, checks for installed dependencies and then generates files for the selected build system. + +The following steps show how to build the library: + +1) Download the source code from an existing [tags](https://github.com/end2endzone/AnyRtttl/tags) and extract the content to a local directory (for example `c:\projects\AnyRtttl` or `~/dev/AnyRtttl`). + +2) Open a Command Prompt (Windows) or Terminal (Linux) and browse to the project directory. + +3) Enter the following commands to generate the project files for your build system: +``` +mkdir build +cd build +cmake .. +``` + +4) Build the source code. + +**Windows** +``` +cmake --build . --config Release +``` + +**Linux** +``` +make +``` + + + + +# Testing # + +AnyRtttl comes with unit tests which help maintaining the product stability and level of quality. + +Test are build using the Google Test v1.8.0 framework. For more information on how googletest is working, see the [google test documentation primer](https://github.com/google/googletest/blob/release-1.8.0/googletest/docs/V1_6_Primer.md). + +To run tests, open a shell prompt and browse to the `build/bin` folder and run `anyrtttl_unittest` executable. For Windows users, the executable is located in `build\bin\Release`. + +Test results are saved in junit format in file `anyrtttl_unittest.release.xml`. + +The latest test results are available at the beginning of the [README.md](README.md) file. diff --git a/lib/AnyRtttl/LICENSE b/lib/AnyRtttl/LICENSE new file mode 100644 index 0000000000..dd4add21ca --- /dev/null +++ b/lib/AnyRtttl/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Antoine Beauchamp + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/AnyRtttl/README.md b/lib/AnyRtttl/README.md new file mode 100644 index 0000000000..d26d7aa41b --- /dev/null +++ b/lib/AnyRtttl/README.md @@ -0,0 +1,523 @@ +![AnyRtttl logo](https://github.com/end2endzone/AnyRtttl/raw/master/docs/AnyRtttl-splashscreen.png) + + +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Github Releases](https://img.shields.io/github/release/end2endzone/AnyRtttl.svg)](https://github.com/end2endzone/AnyRtttl/releases) + + + +# AnyRtttl # + +AnyRtttl is a feature rich arduino library for playing [RTTTL](http://www.end2endzone.com/anyrtttl-a-feature-rich-arduino-library-for-playing-rtttl-melodies/#Quick_recall_of_the_RTTTL_format) melodies. The library offers much more interesting features than relying on the widely available `void play_rtttl(char *p)` function. The library supports all best RTTTL features. + +Library features: + +* Really small increase in memory & code footprint compared to the usual blocking algorithm. +* Blocking & Non-Blocking modes available. +* Support custom `tone()`, `noTone()`, `delay()` and `millis()` functions. +* Compatible with external Tone libraries. +* Supports highly compressed RTTTL binary format. +* Supports RTTTL melodies stored in Program Memory (`PROGMEM`). +* Play two monolithic melodies on two different pins using 2 piezo buzzer with the help of an external Tone library. + + + +## Status ## + +Build: + +| Service | Build | Tests | +|----|-------|-------| +| AppVeyor | [![Build status](https://img.shields.io/appveyor/ci/end2endzone/AnyRtttl/master.svg?logo=appveyor)](https://ci.appveyor.com/project/end2endzone/AnyRtttl) | [![Tests status](https://img.shields.io/appveyor/tests/end2endzone/AnyRtttl/master.svg?logo=appveyor)](https://ci.appveyor.com/project/end2endzone/AnyRtttl/branch/master/tests) | +| Windows Server 2019 | [![Build on Windows](https://github.com/end2endzone/AnyRtttl/actions/workflows/build_windows.yml/badge.svg)](https://github.com/end2endzone/AnyRtttl/actions/workflows/build_windows.yml) | [![Tests on Windows](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/end2endzone/58cf6c72c08e706335337d5ef9ca48e8/raw/AnyRtttl.master.Windows.json)](https://github.com/end2endzone/AnyRtttl/actions/workflows/build_windows.yml) | +| Ubuntu 22.04 | [![Build on Linux](https://github.com/end2endzone/AnyRtttl/actions/workflows/build_linux.yml/badge.svg)](https://github.com/end2endzone/AnyRtttl/actions/workflows/build_linux.yml) | [![Tests on Linux](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/end2endzone/58cf6c72c08e706335337d5ef9ca48e8/raw/AnyRtttl.master.Linux.json)](https://github.com/end2endzone/AnyRtttl/actions/workflows/build_linux.yml) | + +Statistics: + +| AppVeyor | GitHub | +|--------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------| +| [![Statistics](https://buildstats.info/appveyor/chart/end2endzone/AnyRtttl)](https://ci.appveyor.com/project/end2endzone/AnyRtttl/branch/master) | [![Statistics](https://buildstats.info/github/chart/end2endzone/AnyRtttl)](https://github.com/end2endzone/AnyRtttl/actions) | + + + + +# Purpose # + +After publishing [NonBlockingRtttl](https://github.com/end2endzone/NonBlockingRTTTL) arduino library, I started using the library in more complex projects which was requiring other libraries. I quickly ran into the hell of library dependencies and library conflicts. I realized that I needed more features that could help me prototype faster. + +Other libraries available which allows you to "play" a melody in [RTTTL](http://www.end2endzone.com/anyrtttl-a-feature-rich-arduino-library-for-playing-rtttl-melodies/#Quick_recall_of_the_RTTTL_format) format suffer the same issue: they are based on blocking APIs or the RTTTL data is not optimized for space. + +AnyRtttl is different since it packs multiple RTTTL related features in a single library. It supports [blocking](https://en.wikipedia.org/wiki/Blocking_(computing)) & [non-blocking](http://en.wikipedia.org/wiki/Non-blocking_algorithm) API which makes it suitable to be used by more advanced algorithm. For instance, when using the non-blocking API, the melody can be stopped when a button is pressed. The library is also compatible with external Tone libraries and it supports highly compressed RTTTL binary formats. + + + +## Non-Blocking ## + +Most of the code that can "play" a melody on internet are build the same way: sequential calls to `tone()` and `delay()` functions using hardcoded values. This type of implementation might be good for robots but not for realtime application or projects that needs to monitor pins while the song is playing. + +With AnyRtttl non-blocking mode, your program can read/write IOs pins while playing and react on changes. Implementing a "stop" or "next song" push button is easy! + + + +## External Tone or Timer #0 libraries ## + +The AnyRtttl library is also flexible by allowing you to use the build-in arduino `tone()` and `noTone()` functions or an implementation from any external library which makes it compatible with any Tone library in the market. + +The library also supports custom `delay()` and `millis()` functions. If a project requires modification to the microcontroller's build-in Timer #0, the `millis()` function may be impacted and behave incorrectly. To maximize compatibility, one can supply a custom function which behaves like the original to prevent altering playback. + + + +## Binary RTTTL ## + +The AnyRtttl library also supports playing RTTTL data which is stored as binary data instead of text. This is actually a custom implementation of the RTTTL format. Using this format, one can achieve storing an highly compressed RTTTL melody which saves memory. + +See below for details on RTTTL binary format. + + + + +# Usage # + +The following instructions show how to use the library. + +Define `ANY_RTTTL_INFO` to enable the debugging of the library state on the serial port. + +Use `ANY_RTTTL_VERSION` to get the current version of the library. + +Note, the specified macros must be defined before including `anyrtttl.h` in your sketches. + + + +## Non-blocking mode ## + + anyrtttl::nonblocking::begin(BUZZER_PIN, mario); +Call `anyrtttl::nonblocking::begin()` to setup AnyRtttl library in non-blocking mode. + +Then call `anyrtttl::nonblocking::play()` to update the library's state and play notes as required. + +Use `anyrtttl::done()` or `anyrtttl::nonblocking::isPlaying()` to know if the library is done playing the given song. + +Anytime, one can call `anyrtttl::nonblocking::stop()` to stop playing the current song. + +The following code shows how to use the library in non-blocking mode: + +```cpp +#include +#include +#include + +//project's constants +#define BUZZER_PIN 8 +const char * tetris = "tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a"; +const char * arkanoid = "Arkanoid:d=4,o=5,b=140:8g6,16p,16g.6,2a#6,32p,8a6,8g6,8f6,8a6,2g6"; +const char * mario = "mario:d=4,o=5,b=100:16e6,16e6,32p,8e6,16c6,8e6,8g6,8p,8g,8p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,16p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16c7,16p,16c7,16c7,p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16d#6,8p,16d6,8p,16c6"; +byte songIndex = 0; //which song to play when the previous one finishes + +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + Serial.begin(115200); + Serial.println(); +} + +void loop() { + // If we are not playing something + if ( !anyrtttl::nonblocking::isPlaying() ) + { + // Play a song based on songIndex. + if (songIndex == 0) + anyrtttl::nonblocking::begin(BUZZER_PIN, tetris); + else if (songIndex == 1) + anyrtttl::nonblocking::begin(BUZZER_PIN, arkanoid); + else if (songIndex == 2) + anyrtttl::nonblocking::begin(BUZZER_PIN, mario); + + //Set songIndex ready for next song + songIndex++; + } + else + { + anyrtttl::nonblocking::play(); + } +} +``` + + + +## Playing RTTTL data stored in flash (program) memory ## + +AnyRtttl also supports RTTTL melodies stored in flash or Program Memory (PROGMEM). + +The `anyrtttl::nonblocking::begin()` function supports _Program Memory_ macros such as `FPSTR()` or `F()`. + +The following code shows how to use the library with RTTTL data stored in flash (program) memory instead of SRAM: + +```cpp +#include +#include +#include + +//project's constants +#define BUZZER_PIN 8 +const char tetris[] PROGMEM = "tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a"; +const char arkanoid[] PROGMEM = "Arkanoid:d=4,o=5,b=140:8g6,16p,16g.6,2a#6,32p,8a6,8g6,8f6,8a6,2g6"; +const char mario[] PROGMEM = "mario:d=4,o=5,b=100:16e6,16e6,32p,8e6,16c6,8e6,8g6,8p,8g,8p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,16p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16c7,16p,16c7,16c7,p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16d#6,8p,16d6,8p,16c6"; +// James Bond theme defined in inline code below (also stored in flash memory) +byte songIndex = 0; //which song to play when the previous one finishes + +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + Serial.begin(115200); + Serial.println(); +} + +void loop() { + // If we are not playing something + if ( !anyrtttl::nonblocking::isPlaying() ) + { + // Play a song based on songIndex. + if (songIndex == 0) + anyrtttl::nonblocking::beginProgMem(BUZZER_PIN, tetris); + else if (songIndex == 1) + anyrtttl::nonblocking::begin_P(BUZZER_PIN, arkanoid); + else if (songIndex == 2) + anyrtttl::nonblocking::begin(BUZZER_PIN, FPSTR(mario)); + else if (songIndex == 3) + anyrtttl::nonblocking::begin(BUZZER_PIN, F("Bond:d=4,o=5,b=80:32p,16c#6,32d#6,32d#6,16d#6,8d#6,16c#6,16c#6,16c#6,16c#6,32e6,32e6,16e6,8e6,16d#6,16d#6,16d#6,16c#6,32d#6,32d#6,16d#6,8d#6,16c#6,16c#6,16c#6,16c#6,32e6,32e6,16e6,8e6,16d#6,16d6,16c#6,16c#7,c.7,16g#6,16f#6,g#.6")); + + //Set songIndex ready for next song + songIndex++; + } + else + { + anyrtttl::nonblocking::play(); + } +} +``` + + + +# Advanced Usage # + + + +## Custom Tone function (a.k.a. RTTTL 2 code) ## + +This example shows how custom functions can be used by the AnyRtttl library to convert an RTTTL melody to arduino code. + +First define replacement functions like the following: + +```cpp +void serialTone(byte pin, uint16_t frequency, uint32_t duration) { + Serial.print("tone("); + Serial.print(pin); + Serial.print(","); + Serial.print(frequency); + Serial.print(","); + Serial.print(duration); + Serial.println(");"); +} + +void serialNoTone(byte pin) { + Serial.print("noTone("); + Serial.print(pin); + Serial.println(");"); +} + +void serialDelay(uint32_t duration) { + Serial.print("delay("); + Serial.print(duration); + Serial.println(");"); +} +``` + +Each new functions prints the function call & arguments to the serial port. + +In the `setup()` function, setup the AnyRtttl library to use the new functions: + +```cpp +//Use custom functions +anyrtttl::setToneFunction(&serialTone); +anyrtttl::setNoToneFunction(&serialNoTone); +anyrtttl::setDelayFunction(&serialDelay); +``` + +Use the `anyrtttl::blocking::play()` API for "playing" an RTTTL melody and monitor the output of the serial port to see the actual arduino code generated by the library. + +The following code shows how to use the library with custom functions: + +```cpp +#include +#include +#include + +//project's constants +#define BUZZER_PIN 8 +const char * tetris = "tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a"; + +//******************************************************************************************************************* +// The following replacement functions prints the function call & parameters to the serial port. +//******************************************************************************************************************* +void serialTone(byte pin, uint16_t frequency, uint32_t duration) { + Serial.print("tone("); + Serial.print(pin); + Serial.print(","); + Serial.print(frequency); + Serial.print(","); + Serial.print(duration); + Serial.println(");"); +} + +void serialNoTone(byte pin) { + Serial.print("noTone("); + Serial.print(pin); + Serial.println(");"); +} + +void serialDelay(uint32_t duration) { + Serial.print("delay("); + Serial.print(duration); + Serial.println(");"); +} + +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + Serial.begin(115200); + Serial.println(); + + //Use custom functions + anyrtttl::setToneFunction(&serialTone); + anyrtttl::setNoToneFunction(&serialNoTone); + anyrtttl::setDelayFunction(&serialDelay); +} + +void loop() { + anyrtttl::blocking::play(BUZZER_PIN, tetris); + + while(true) + { + } +} +``` + + +## Play 16 bits per note RTTTL ## + +Note that this mode requires that an RTTTL melody be already converted to 16-bits per note binary format. + +Use the `anyrtttl::blocking::play16Bits()` API for playing an RTTTL melody encoded as 16 bits per note. + +The following code shows how to use the library with 16-bits per note binary RTTTL: + +```cpp +#include +#include +#include + +//project's constants +#define BUZZER_PIN 8 + +//RTTTL 16 bits binary format for the following: tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a +const unsigned char tetris16[] = {0x0A, 0x14, 0x12, 0x02, 0x33, 0x01, 0x03, 0x02, 0x0B, 0x02, 0x14, 0x02, 0x0C, 0x02, 0x03, 0x02, 0x33, 0x01, 0x2A, 0x01, 0x2B, 0x01, 0x03, 0x02, 0x12, 0x02, 0x0B, 0x02, 0x03, 0x02, 0x32, 0x01, 0x33, 0x01, 0x03, 0x02, 0x0A, 0x02, 0x12, 0x02, 0x02, 0x02, 0x2A, 0x01, 0x29, 0x01, 0x3B, 0x01, 0x0A, 0x02, 0x1B, 0x02, 0x2A, 0x02, 0x23, 0x02, 0x1B, 0x02, 0x12, 0x02, 0x13, 0x02, 0x03, 0x02, 0x12, 0x02, 0x0B, 0x02, 0x03, 0x02, 0x32, 0x01, 0x33, 0x01, 0x03, 0x02, 0x0A, 0x02, 0x12, 0x02, 0x02, 0x02, 0x2A, 0x01, 0x2A, 0x01}; +const int tetris16_length = 42; + +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + Serial.begin(115200); + Serial.println(); +} + +void loop() { + anyrtttl::blocking::play16Bits(BUZZER_PIN, tetris16, tetris16_length); + + while(true) + { + } +} +``` + + + +## Play 10 bits per note RTTTL ## + +Note that this mode requires that an RTTTL melody be already converted to 10-bits per note binary format. + +Create a function that will be used by AnyRtttl library to read bits as required. The signature of the library must look like this: `uint16_t function_name(uint8_t numBits)`. + +Note that this code requires the [BitReader](https://github.com/end2endzone/BitReader) library to extract bits from the RTTTL binary buffer. The implementation of `readNextBits()` function delegates the job to the BitReader's `read()` method. + +In the `setup()` function, setup the external library that is used for reading bits: `bitreader.setBuffer(tetris10);`. + +Use the `anyrtttl::blocking::play10Bits()` API for playing an RTTTL melody encoded as 10 bits per note. The 3rd argument of the function requires a pointer to the function extracting bits: `&function_name`. + +The following code shows how to use the library with 10-bits per note binary RTTTL: + +```cpp +#include +#include +#include + +//The BitReader library is required for extracting 10 bit blocks from the RTTTL buffer. +//It can be installed from Arduino Library Manager or from https://github.com/end2endzone/BitReader/releases +#include + +//project's constants +#define BUZZER_PIN 8 + +//RTTTL 10 bits binary format for the following: tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a +const unsigned char tetris10[] = {0x0A, 0x14, 0x12, 0xCE, 0x34, 0xE0, 0x82, 0x14, 0x32, 0x38, 0xE0, 0x4C, 0x2A, 0xAD, 0x34, 0xA0, 0x84, 0x0B, 0x0E, 0x28, 0xD3, 0x4C, 0x03, 0x2A, 0x28, 0xA1, 0x80, 0x2A, 0xA5, 0xB4, 0x93, 0x82, 0x1B, 0xAA, 0x38, 0xE2, 0x86, 0x12, 0x4E, 0x38, 0xA0, 0x84, 0x0B, 0x0E, 0x28, 0xD3, 0x4C, 0x03, 0x2A, 0x28, 0xA1, 0x80, 0x2A, 0xA9, 0x04}; +const int tetris10_length = 42; + +//bit reader support +#ifndef USE_BITADDRESS_READ_WRITE +BitReader bitreader; +#else +BitAddress bitreader; +#endif +uint16_t readNextBits(uint8_t numBits) +{ + uint16_t bits = 0; + bitreader.read(numBits, &bits); + return bits; +} + +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + bitreader.setBuffer(tetris10); + + Serial.begin(115200); + Serial.println(); +} + +void loop() { + anyrtttl::blocking::play10Bits(BUZZER_PIN, tetris10_length, &readNextBits); + + while(true) + { + } +} +``` + + + +## Other ## + +More AnyRtttl examples are also available: + +* [Basic](examples/Basic/Basic.ino) +* [BlockingProgramMemoryRtttl](examples/BlockingProgramMemoryRtttl/BlockingProgramMemoryRtttl.ino) +* [BlockingRtttl](examples/BlockingRtttl/BlockingRtttl.ino) +* [BlockingWithNonBlocking](examples/BlockingWithNonBlocking/BlockingWithNonBlocking.ino) +* [NonBlockingProgramMemoryRtttl](examples/NonBlockingProgramMemoryRtttl/NonBlockingProgramMemoryRtttl.ino) +* [NonBlockingRtttl](examples/NonBlockingRtttl/NonBlockingRtttl.ino) +* [NonBlockingStopBeforeEnd](examples/NonBlockingStopBeforeEnd/NonBlockingStopBeforeEnd.ino) +* [Play10Bits](examples/Play10Bits/Play10Bits.ino) +* [Play16Bits](examples/Play16Bits/Play16Bits.ino) +* [Rtttl2Code](examples/Rtttl2Code/Rtttl2Code.ino) + + + + +# Binary RTTTL format definition # + +The following section defines the field order and size (in bits) required for encoding / decoding of each melody as binary RTTTL. + +This is actually a custom implementation of the RTTTL format. Using this format, one can achieve storing an highly compressed RTTTL melody which saves memory. + +Note that all fields definition are defined in LSB to MSB order. + +## Header ## + +The first 16 bits stores the RTTTL default section (a.k.a header) which is defined as the following: + +| Field name | Size (bits) | Range | Description | +|-------------------------|:-----------:|----------|-------------------------------------------------------------| +| Default duration index | 3 | [0, 7] | Matches the index used for `getNoteDurationFromIndex()` API | +| Default octave index | 2 | [0, 3] | Matches the index used for `getNoteOctaveFromIndex()` API. | +| Beats per minutes (BPM) | 10 | [1, 900] | | +| Padding | 1 | | | + +## Notes ## + +Next is each note's of the melody. Each note is encoded as 10 bits (or 16 bits) per note. Notes are defined as the following: + +| Field name | Size (bits) | Range | Description | +|--------------------|:-----------:|---------|--------------------------------------------------------------| +| Duration index | 3 | [0, 7] | Matches the index used for `getNoteDurationFromIndex()` API. | +| Note letter index | 3 | [0, 7] | Matches the index used for `getNoteLetterFromIndex()` API. | +| Pound | 1 | boolean | Defines if the note is pounded or not. | +| Dotted | 1 | boolean | Defines if the note is dotted or not. | +| Octave index | 2 | [0, 3] | Matches the index used for `getNoteOctaveFromIndex()` API. | +| Padding (optional) | 6 | | See description below. | + +The last field of a note (defined as `Padding`) is an optional 6 bits field. The AnyRtttl library supports both 10 bits per note and 16 bits per note definitions. Use the appropriate API for playing both format. + + + +## 10 bits per note (no padding) ## + +Each RTTTL note is encoded into 10 bits which is the minimum size of a note. This storage method is the best compression method for storing RTTTL melodies and reduces the usage of the dynamic memory to the minimum. + +However, since all notes are not aligned on multiple of 8 bits, addressing each note by an offset is impossible which makes the playback harder. Each notes must be deserialized one after the other from a buffer using blocks of 10 bits which increases the program storage space footprint. + +An external arduino library (or custom code) is required to allow AnyRtttl library to consume bits as needed. The arduino [BitReader](https://github.com/end2endzone/BitReader) library may be used for handling bit deserialization but any library that can extract a given number of bits from a buffer would work. + + + +## 16 bits per note (with padding) ## + +Each RTTTL note is encoded into 16 bits which is much better than the average 3.28 bytes per note text format. This storage method is optimum for storing RTTTL melodies and reduces the usage of the dynamic memory without increasing to much program storage space. + +All notes are aligned on 16 bits. Addressing each note by an offset allows an easy playback. Only the first 10 bits of each 16 bits block is used. The value of the padding field is undefined. + + + +## Playback ## + +The following AnyRtttl blocking APIs are available for playing both binary formats: +* 10 bits per note: `anyrtttl::blocking::play10Bits()`. +* 16 bits per note: `anyrtttl::blocking::play16Bits()`. + + + + +# Building # + +Please refer to file [INSTALL.md](INSTALL.md) for details on how installing/building the application. + + + + +# Platforms # + +AnyRtttl has been tested with the following platform: + + * Linux x86/x64 + * Windows x86/x64 + + + + +# Versioning # + +We use [Semantic Versioning 2.0.0](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/end2endzone/AnyRtttl/tags). + + + + +# Authors # + +* **Antoine Beauchamp** - *Initial work* - [end2endzone](https://github.com/end2endzone) + +See also the list of [contributors](https://github.com/end2endzone/AnyRtttl/blob/master/AUTHORS) who participated in this project. + + + + +# License # + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details diff --git a/lib/AnyRtttl/appveyor.yml b/lib/AnyRtttl/appveyor.yml new file mode 100644 index 0000000000..88c6ab9481 --- /dev/null +++ b/lib/AnyRtttl/appveyor.yml @@ -0,0 +1,81 @@ +#---------------------------------# +# general configuration # +#---------------------------------# + +# version format +version: "{branch} (#{build})" + +# branches to build +branches: + only: + - master + - /feature-issue.*/ + +#---------------------------------# +# environment configuration # +#---------------------------------# + +# Build worker image (VM template) +image: Visual Studio 2019 + +# scripts that are called at very beginning, before repo cloning +init: + - cmd: git config --global core.autocrlf true + - ps: $env:GIT_HASH=$env:APPVEYOR_REPO_COMMIT.Substring(0, 10) + +# clone directory +clone_folder: c:\projects\AnyRtttl + +# scripts that run after cloning repository +install: +- cmd: call %APPVEYOR_BUILD_FOLDER%\ci\appveyor\install_arduinocli.bat +- cmd: call %APPVEYOR_BUILD_FOLDER%\ci\appveyor\arduino_install_libraries.bat +- cmd: call %APPVEYOR_BUILD_FOLDER%\ci\appveyor\install_this.bat +- cmd: call %APPVEYOR_BUILD_FOLDER%\ci\appveyor\install_googletest.bat +- cmd: call %APPVEYOR_BUILD_FOLDER%\ci\appveyor\install_rapidassist.bat +- cmd: call %APPVEYOR_BUILD_FOLDER%\ci\appveyor\install_win32arduino.bat + +#---------------------------------# +# build configuration # +#---------------------------------# + +environment: + PlatformToolset: v142 + +# build platform, i.e. x86, x64, Any CPU. This setting is optional. +platform: Win32 + +# build Configuration, i.e. Debug, Release, etc. +configuration: Release + +build_script: +- cmd: call %APPVEYOR_BUILD_FOLDER%\ci\appveyor\build_library.bat +- cmd: call %APPVEYOR_BUILD_FOLDER%\external\BitReader\ci\appveyor\arduino_library_install.bat +- cmd: call %APPVEYOR_BUILD_FOLDER%\ci\appveyor\arduino_build_sketch.bat Basic +- cmd: call %APPVEYOR_BUILD_FOLDER%\ci\appveyor\arduino_build_sketch.bat BlockingProgramMemoryRtttl +- cmd: call %APPVEYOR_BUILD_FOLDER%\ci\appveyor\arduino_build_sketch.bat BlockingRtttl +- cmd: call %APPVEYOR_BUILD_FOLDER%\ci\appveyor\arduino_build_sketch.bat BlockingWithNonBlocking +- cmd: call %APPVEYOR_BUILD_FOLDER%\ci\appveyor\arduino_build_sketch.bat NonBlockingProgramMemoryRtttl +- cmd: call %APPVEYOR_BUILD_FOLDER%\ci\appveyor\arduino_build_sketch.bat NonBlockingRtttl +- cmd: call %APPVEYOR_BUILD_FOLDER%\ci\appveyor\arduino_build_sketch.bat NonBlockingStopBeforeEnd +- cmd: call %APPVEYOR_BUILD_FOLDER%\ci\appveyor\arduino_build_sketch.bat Play10Bits +- cmd: call %APPVEYOR_BUILD_FOLDER%\ci\appveyor\arduino_build_sketch.bat Play16Bits +- cmd: call %APPVEYOR_BUILD_FOLDER%\ci\appveyor\arduino_build_sketch.bat Rtttl2Code + + +#---------------------------------# +# tests configuration # +#---------------------------------# + +# to run your custom scripts instead of automatic tests +test_script: +- cmd: call %APPVEYOR_BUILD_FOLDER%\ci\appveyor\test_script.bat +- ps: . "$env:APPVEYOR_BUILD_FOLDER\ci\appveyor\UploadJUnitFiles.ps1" -Path "$env:APPVEYOR_BUILD_FOLDER\build\bin" + +#---------------------------------# +# artifacts configuration # +#---------------------------------# + +artifacts: +- path: build\bin\Release\anyrtttl_unittest.release.xml + name: anyrtttl_unittest.release.xml diff --git a/lib/AnyRtttl/docs/AnyRtttl-splashscreen.png b/lib/AnyRtttl/docs/AnyRtttl-splashscreen.png new file mode 100644 index 0000000000..55c5d76751 Binary files /dev/null and b/lib/AnyRtttl/docs/AnyRtttl-splashscreen.png differ diff --git a/lib/AnyRtttl/docs/AnyRtttl.xcf b/lib/AnyRtttl/docs/AnyRtttl.xcf new file mode 100644 index 0000000000..8f8a2b0905 Binary files /dev/null and b/lib/AnyRtttl/docs/AnyRtttl.xcf differ diff --git a/lib/AnyRtttl/examples.cpp.in b/lib/AnyRtttl/examples.cpp.in new file mode 100644 index 0000000000..adbafa79be --- /dev/null +++ b/lib/AnyRtttl/examples.cpp.in @@ -0,0 +1,14 @@ +#include +#include "Arduino.h" +#include "@SOURCE_INO_FILE@" + +using namespace testarduino; + +int main(int argc, char* argv[]) +{ + printf("Calling setup()...\n"); + setup(); + + printf("Calling loop()...\n"); + loop(); +} diff --git a/lib/AnyRtttl/examples/Basic/Basic.ino b/lib/AnyRtttl/examples/Basic/Basic.ino new file mode 100644 index 0000000000..4c7ba902d6 --- /dev/null +++ b/lib/AnyRtttl/examples/Basic/Basic.ino @@ -0,0 +1,22 @@ +#include +#include +#include + +//project's constants +#define BUZZER_PIN 8 +const char * tetris = "tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a"; + +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + Serial.begin(115200); + Serial.println(); +} + +void loop() { + anyrtttl::blocking::play(BUZZER_PIN, tetris); + + while(true) + { + } +} diff --git a/lib/AnyRtttl/examples/BlockingProgramMemoryRtttl/BlockingProgramMemoryRtttl.ino b/lib/AnyRtttl/examples/BlockingProgramMemoryRtttl/BlockingProgramMemoryRtttl.ino new file mode 100644 index 0000000000..cf28266d55 --- /dev/null +++ b/lib/AnyRtttl/examples/BlockingProgramMemoryRtttl/BlockingProgramMemoryRtttl.ino @@ -0,0 +1,37 @@ +#include +#include +#include + +//project's constants +#define BUZZER_PIN 8 +const char tetris[] PROGMEM = "tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a"; +const char arkanoid[] PROGMEM = "Arkanoid:d=4,o=5,b=140:8g6,16p,16g.6,2a#6,32p,8a6,8g6,8f6,8a6,2g6"; +const char mario[] PROGMEM = "mario:d=4,o=5,b=140:16e6,16e6,32p,8e6,16c6,8e6,8g6,8p,8g,8p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,16p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16c7,16p,16c7,16c7,p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16d#6,8p,16d6,8p,16c6"; +// James Bond theme defined in inline code below (also stored in flash memory) + +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + Serial.begin(115200); + Serial.println("ready"); +} + +void loop() { + anyrtttl::blocking::playProgMem(BUZZER_PIN, tetris); + delay(1000); + + anyrtttl::blocking::play_P(BUZZER_PIN, arkanoid); + delay(1000); + +#if defined(ESP8266) + anyrtttl::blocking::play(BUZZER_PIN, FPSTR(mario)); + delay(1000); +#endif + + anyrtttl::blocking::play(BUZZER_PIN, F("Bond:d=4,o=5,b=80:32p,16c#6,32d#6,32d#6,16d#6,8d#6,16c#6,16c#6,16c#6,16c#6,32e6,32e6,16e6,8e6,16d#6,16d#6,16d#6,16c#6,32d#6,32d#6,16d#6,8d#6,16c#6,16c#6,16c#6,16c#6,32e6,32e6,16e6,8e6,16d#6,16d6,16c#6,16c#7,c.7,16g#6,16f#6,g#.6")); + delay(1000); + + while(true) + { + } +} diff --git a/lib/AnyRtttl/examples/BlockingRtttl/BlockingRtttl.ino b/lib/AnyRtttl/examples/BlockingRtttl/BlockingRtttl.ino new file mode 100644 index 0000000000..6156ecdd1f --- /dev/null +++ b/lib/AnyRtttl/examples/BlockingRtttl/BlockingRtttl.ino @@ -0,0 +1,31 @@ +#include +#include +#include + +//project's constants +#define BUZZER_PIN 8 +const char * tetris = "tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a"; +const char * arkanoid = "Arkanoid:d=4,o=5,b=140:8g6,16p,16g.6,2a#6,32p,8a6,8g6,8f6,8a6,2g6"; +const char * mario = "mario:d=4,o=5,b=140:16e6,16e6,32p,8e6,16c6,8e6,8g6,8p,8g,8p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,16p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16c7,16p,16c7,16c7,p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16d#6,8p,16d6,8p,16c6"; + +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + Serial.begin(115200); + Serial.println("ready"); +} + +void loop() { + anyrtttl::blocking::play(BUZZER_PIN, tetris); + delay(1000); + + anyrtttl::blocking::play(BUZZER_PIN, arkanoid); + delay(1000); + + anyrtttl::blocking::play(BUZZER_PIN, mario); + delay(1000); + + while(true) + { + } +} diff --git a/lib/AnyRtttl/examples/BlockingWithNonBlocking/BlockingWithNonBlocking.ino b/lib/AnyRtttl/examples/BlockingWithNonBlocking/BlockingWithNonBlocking.ino new file mode 100644 index 0000000000..9f6e4d0643 --- /dev/null +++ b/lib/AnyRtttl/examples/BlockingWithNonBlocking/BlockingWithNonBlocking.ino @@ -0,0 +1,44 @@ +#include +#include +#include + +//project's constants +#define BUZZER_PIN 8 +const char * tetris = "tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a"; +const char * arkanoid = "Arkanoid:d=4,o=5,b=140:8g6,16p,16g.6,2a#6,32p,8a6,8g6,8f6,8a6,2g6"; +const char * mario = "mario:d=4,o=5,b=100:16e6,16e6,32p,8e6,16c6,8e6,8g6,8p,8g,8p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,16p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16c7,16p,16c7,16c7,p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16d#6,8p,16d6,8p,16c6"; + +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + Serial.begin(115200); + Serial.println(); +} + +void loop() { + + // Play tetris and wait until done playing before jumping to the next song. + anyrtttl::nonblocking::begin(BUZZER_PIN, tetris); + while( !anyrtttl::nonblocking::done() ) + { + anyrtttl::nonblocking::play(); + } + + // Play arkanoid and loop until done playing before jumping to the next song. + anyrtttl::nonblocking::begin(BUZZER_PIN, arkanoid); + while( !anyrtttl::nonblocking::done() ) + { + anyrtttl::nonblocking::play(); + } + + // Play mario and loop until done playing before looping again. + anyrtttl::nonblocking::begin(BUZZER_PIN, mario); + while( !anyrtttl::nonblocking::done() ) + { + anyrtttl::nonblocking::play(); + } + + while(true) + { + } +} diff --git a/lib/AnyRtttl/examples/ESP32Rtttl/ESP32Rtttl.ino b/lib/AnyRtttl/examples/ESP32Rtttl/ESP32Rtttl.ino new file mode 100644 index 0000000000..e5039c79fb --- /dev/null +++ b/lib/AnyRtttl/examples/ESP32Rtttl/ESP32Rtttl.ino @@ -0,0 +1,62 @@ +#include +#include +#include + +//project's constants +#define BUZZER_PIN 8 +const char tetris[] PROGMEM = "tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a"; +const char arkanoid[] PROGMEM = "Arkanoid:d=4,o=5,b=140:8g6,16p,16g.6,2a#6,32p,8a6,8g6,8f6,8a6,2g6"; +const char mario[] PROGMEM = "mario:d=4,o=5,b=140:16e6,16e6,32p,8e6,16c6,8e6,8g6,8p,8g,8p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,16p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16c7,16p,16c7,16c7,p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16d#6,8p,16d6,8p,16c6"; +// James Bond theme defined in inline code below (also stored in flash memory) + +// tone() and noTone() are not implemented for Arduino core for the ESP32 +// See https://github.com/espressif/arduino-esp32/issues/980 +// and https://github.com/espressif/arduino-esp32/issues/1720 +void esp32NoTone(uint8_t pin) { + // don't care about the given pin + ledcWrite(0, 0); // channel, volume +} + +void esp32Tone(uint8_t pin, unsigned int frq, unsigned long duration) { + // don't care about the given pin or the duration + ledcWriteTone(0, frq); // channel, freq + ledcWrite(0, 255); // channel, volume +} + +void esp32ToneSetup(uint8_t pin) { + ledcSetup(0, 1000, 10); // resolution always seems to be 10bit, no matter what is given + ledcAttachPin(pin, 0); +} + +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + // setup AnyRtttl for ESP32 + esp32ToneSetup(BUZZER_PIN); + anyrtttl::setToneFunction(&esp32Tone); + anyrtttl::setNoToneFunction(&esp32NoTone); + + Serial.begin(115200); + + Serial.println("ready"); +} + +void loop() { + anyrtttl::blocking::playProgMem(BUZZER_PIN, tetris); + delay(1000); + + anyrtttl::blocking::play_P(BUZZER_PIN, arkanoid); + delay(1000); + +#if defined(ESP8266) || defined(ESP32) + anyrtttl::blocking::play(BUZZER_PIN, FPSTR(mario)); + delay(1000); +#endif + + anyrtttl::blocking::play(BUZZER_PIN, F("Bond:d=4,o=5,b=80:32p,16c#6,32d#6,32d#6,16d#6,8d#6,16c#6,16c#6,16c#6,16c#6,32e6,32e6,16e6,8e6,16d#6,16d#6,16d#6,16c#6,32d#6,32d#6,16d#6,8d#6,16c#6,16c#6,16c#6,16c#6,32e6,32e6,16e6,8e6,16d#6,16d6,16c#6,16c#7,c.7,16g#6,16f#6,g#.6")); + delay(1000); + + while(true) + { + } +} diff --git a/lib/AnyRtttl/examples/NonBlockingProgramMemoryRtttl/NonBlockingProgramMemoryRtttl.ino b/lib/AnyRtttl/examples/NonBlockingProgramMemoryRtttl/NonBlockingProgramMemoryRtttl.ino new file mode 100644 index 0000000000..fb92e1f466 --- /dev/null +++ b/lib/AnyRtttl/examples/NonBlockingProgramMemoryRtttl/NonBlockingProgramMemoryRtttl.ino @@ -0,0 +1,43 @@ +#include +#include +#include + +//project's constants +#define BUZZER_PIN 8 +const char tetris[] PROGMEM = "tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a"; +const char arkanoid[] PROGMEM = "Arkanoid:d=4,o=5,b=140:8g6,16p,16g.6,2a#6,32p,8a6,8g6,8f6,8a6,2g6"; +const char mario[] PROGMEM = "mario:d=4,o=5,b=100:16e6,16e6,32p,8e6,16c6,8e6,8g6,8p,8g,8p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,16p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16c7,16p,16c7,16c7,p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16d#6,8p,16d6,8p,16c6"; +// James Bond theme defined in inline code below (also stored in flash memory) +byte songIndex = 0; //which song to play when the previous one finishes + +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + Serial.begin(115200); + Serial.println(); +} + +void loop() { + // If we are not playing something + if ( !anyrtttl::nonblocking::isPlaying() ) + { + // Play a song based on songIndex. + if (songIndex == 0) + anyrtttl::nonblocking::beginProgMem(BUZZER_PIN, tetris); + else if (songIndex == 1) + anyrtttl::nonblocking::begin_P(BUZZER_PIN, arkanoid); +#if defined(ESP8266) + else if (songIndex == 2) + anyrtttl::nonblocking::begin(BUZZER_PIN, FPSTR(mario)); +#endif + else if (songIndex == 3) + anyrtttl::nonblocking::begin(BUZZER_PIN, F("Bond:d=4,o=5,b=80:32p,16c#6,32d#6,32d#6,16d#6,8d#6,16c#6,16c#6,16c#6,16c#6,32e6,32e6,16e6,8e6,16d#6,16d#6,16d#6,16c#6,32d#6,32d#6,16d#6,8d#6,16c#6,16c#6,16c#6,16c#6,32e6,32e6,16e6,8e6,16d#6,16d6,16c#6,16c#7,c.7,16g#6,16f#6,g#.6")); + + //Set songIndex ready for next song + songIndex++; + } + else + { + anyrtttl::nonblocking::play(); + } +} diff --git a/lib/AnyRtttl/examples/NonBlockingRtttl/NonBlockingRtttl.ino b/lib/AnyRtttl/examples/NonBlockingRtttl/NonBlockingRtttl.ino new file mode 100644 index 0000000000..fac679bd89 --- /dev/null +++ b/lib/AnyRtttl/examples/NonBlockingRtttl/NonBlockingRtttl.ino @@ -0,0 +1,38 @@ +#include +#include +#include + +//project's constants +#define BUZZER_PIN 8 +const char * tetris = "tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a"; +const char * arkanoid = "Arkanoid:d=4,o=5,b=140:8g6,16p,16g.6,2a#6,32p,8a6,8g6,8f6,8a6,2g6"; +const char * mario = "mario:d=4,o=5,b=100:16e6,16e6,32p,8e6,16c6,8e6,8g6,8p,8g,8p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,16p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16c7,16p,16c7,16c7,p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16d#6,8p,16d6,8p,16c6"; +byte songIndex = 0; //which song to play when the previous one finishes + +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + Serial.begin(115200); + Serial.println(); +} + +void loop() { + // If we are not playing something + if ( !anyrtttl::nonblocking::isPlaying() ) + { + // Play a song based on songIndex. + if (songIndex == 0) + anyrtttl::nonblocking::begin(BUZZER_PIN, tetris); + else if (songIndex == 1) + anyrtttl::nonblocking::begin(BUZZER_PIN, arkanoid); + else if (songIndex == 2) + anyrtttl::nonblocking::begin(BUZZER_PIN, mario); + + //Set songIndex ready for next song + songIndex++; + } + else + { + anyrtttl::nonblocking::play(); + } +} diff --git a/lib/AnyRtttl/examples/NonBlockingStopBeforeEnd/NonBlockingStopBeforeEnd.ino b/lib/AnyRtttl/examples/NonBlockingStopBeforeEnd/NonBlockingStopBeforeEnd.ino new file mode 100644 index 0000000000..7cffd2c93f --- /dev/null +++ b/lib/AnyRtttl/examples/NonBlockingStopBeforeEnd/NonBlockingStopBeforeEnd.ino @@ -0,0 +1,42 @@ +#include +#include +#include + +//project's constants +#define BUZZER_PIN 8 +const char * tetris = "tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a"; + +unsigned long playStart = 0; +bool firstPass = true; + +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + Serial.begin(115200); + Serial.println(); +} + +void loop() { + if (firstPass) { + anyrtttl::nonblocking::begin(BUZZER_PIN, tetris); + + //remember when we started playing the song + playStart = millis(); + + firstPass = false; + } + + //if we are playing something + if ( anyrtttl::nonblocking::isPlaying() ) { + + //does the melody been playing for more than 5 seconds ? + if ( millis() - playStart > 5000 ) + { + anyrtttl::nonblocking::stop(); + } + } + + //if anything available for playing, play it + anyrtttl::nonblocking::play(); + +} diff --git a/lib/AnyRtttl/examples/Play10Bits/Play10Bits.ino b/lib/AnyRtttl/examples/Play10Bits/Play10Bits.ino new file mode 100644 index 0000000000..841ee7cc75 --- /dev/null +++ b/lib/AnyRtttl/examples/Play10Bits/Play10Bits.ino @@ -0,0 +1,44 @@ +#include +#include +#include + +//The BitReader library is required for extracting 10 bit blocks from the RTTTL buffer. +//It can be installed from Arduino Library Manager or from https://github.com/end2endzone/BitReader/releases +#include + +//project's constants +#define BUZZER_PIN 8 + +//RTTTL 10 bits binary format for the following: tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a +const unsigned char tetris10[] = {0x0A, 0x14, 0x12, 0xCE, 0x34, 0xE0, 0x82, 0x14, 0x32, 0x38, 0xE0, 0x4C, 0x2A, 0xAD, 0x34, 0xA0, 0x84, 0x0B, 0x0E, 0x28, 0xD3, 0x4C, 0x03, 0x2A, 0x28, 0xA1, 0x80, 0x2A, 0xA5, 0xB4, 0x93, 0x82, 0x1B, 0xAA, 0x38, 0xE2, 0x86, 0x12, 0x4E, 0x38, 0xA0, 0x84, 0x0B, 0x0E, 0x28, 0xD3, 0x4C, 0x03, 0x2A, 0x28, 0xA1, 0x80, 0x2A, 0xA9, 0x04}; +const int tetris10_length = 42; + +//bit reader support +#ifndef USE_BITADDRESS_READ_WRITE +BitReader bitreader; +#else +BitAddress bitreader; +#endif +uint16_t readNextBits(uint8_t numBits) +{ + uint16_t bits = 0; + bitreader.read(numBits, &bits); + return bits; +} + +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + bitreader.setBuffer(tetris10); + + Serial.begin(115200); + Serial.println(); +} + +void loop() { + anyrtttl::blocking::play10Bits(BUZZER_PIN, tetris10_length, &readNextBits); + + while(true) + { + } +} diff --git a/lib/AnyRtttl/examples/Play16Bits/Play16Bits.ino b/lib/AnyRtttl/examples/Play16Bits/Play16Bits.ino new file mode 100644 index 0000000000..ad18800e40 --- /dev/null +++ b/lib/AnyRtttl/examples/Play16Bits/Play16Bits.ino @@ -0,0 +1,25 @@ +#include +#include +#include + +//project's constants +#define BUZZER_PIN 8 + +//RTTTL 16 bits binary format for the following: tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a +const unsigned char tetris16[] = {0x0A, 0x14, 0x12, 0x02, 0x33, 0x01, 0x03, 0x02, 0x0B, 0x02, 0x14, 0x02, 0x0C, 0x02, 0x03, 0x02, 0x33, 0x01, 0x2A, 0x01, 0x2B, 0x01, 0x03, 0x02, 0x12, 0x02, 0x0B, 0x02, 0x03, 0x02, 0x32, 0x01, 0x33, 0x01, 0x03, 0x02, 0x0A, 0x02, 0x12, 0x02, 0x02, 0x02, 0x2A, 0x01, 0x29, 0x01, 0x3B, 0x01, 0x0A, 0x02, 0x1B, 0x02, 0x2A, 0x02, 0x23, 0x02, 0x1B, 0x02, 0x12, 0x02, 0x13, 0x02, 0x03, 0x02, 0x12, 0x02, 0x0B, 0x02, 0x03, 0x02, 0x32, 0x01, 0x33, 0x01, 0x03, 0x02, 0x0A, 0x02, 0x12, 0x02, 0x02, 0x02, 0x2A, 0x01, 0x2A, 0x01}; +const int tetris16_length = 42; + +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + Serial.begin(115200); + Serial.println(); +} + +void loop() { + anyrtttl::blocking::play16Bits(BUZZER_PIN, tetris16, tetris16_length); + + while(true) + { + } +} diff --git a/lib/AnyRtttl/examples/Rtttl2Code/Rtttl2Code.ino b/lib/AnyRtttl/examples/Rtttl2Code/Rtttl2Code.ino new file mode 100644 index 0000000000..afb3c30510 --- /dev/null +++ b/lib/AnyRtttl/examples/Rtttl2Code/Rtttl2Code.ino @@ -0,0 +1,52 @@ +#include +#include +#include + +//project's constants +#define BUZZER_PIN 8 +const char * tetris = "tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a"; + +//******************************************************************************************************************* +// The following replacement functions prints the function call & parameters to the serial port. +//******************************************************************************************************************* +void serialTone(uint8_t pin, unsigned int frequency, unsigned long duration) { + Serial.print("tone("); + Serial.print(pin); + Serial.print(","); + Serial.print(frequency); + Serial.print(","); + Serial.print(duration); + Serial.println(");"); +} + +void serialNoTone(uint8_t pin) { + Serial.print("noTone("); + Serial.print(pin); + Serial.println(");"); +} + +void serialDelay(unsigned long duration) { + Serial.print("delay("); + Serial.print(duration); + Serial.println(");"); +} + +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + Serial.begin(115200); + Serial.println(); + + //Use custom functions + anyrtttl::setToneFunction(&serialTone); + anyrtttl::setNoToneFunction(&serialNoTone); + anyrtttl::setDelayFunction(&serialDelay); +} + +void loop() { + anyrtttl::blocking::play(BUZZER_PIN, tetris); + + while(true) + { + } +} diff --git a/lib/AnyRtttl/keywords.txt b/lib/AnyRtttl/keywords.txt new file mode 100644 index 0000000000..d327e0eda1 --- /dev/null +++ b/lib/AnyRtttl/keywords.txt @@ -0,0 +1,16 @@ +anyrtttl KEYWORD1 +play16Bits KEYWORD2 +play10Bits KEYWORD2 +begin KEYWORD2 +beginProgMem KEYWORD2 +begin_P KEYWORD2 +play KEYWORD2 +playProgMem KEYWORD2 +play_P KEYWORD2 +stop KEYWORD2 +isPlaying KEYWORD2 +done KEYWORD2 +setToneFunction KEYWORD2 +setNoToneFunction KEYWORD2 +setDelayFunction KEYWORD2 +setMillisFunction KEYWORD2 diff --git a/lib/AnyRtttl/library.properties b/lib/AnyRtttl/library.properties new file mode 100644 index 0000000000..f8c77a30a6 --- /dev/null +++ b/lib/AnyRtttl/library.properties @@ -0,0 +1,9 @@ +name=AnyRtttl +version=2.3 +author=Antoine Beauchamp +maintainer=Antoine Beauchamp +sentence=A feature rich arduino library for playing rtttl melodies. +paragraph=The AnyRtttl is a feature rich library which supports all best RTTTL features: Blocking & Non-Blocking modes, custom tone(), delay() and millis() functions, PROGMEM support, and much more. +category=Other +url=https://github.com/end2endzone/AnyRtttl +architectures=* diff --git a/lib/AnyRtttl/src/anyrtttl.cpp b/lib/AnyRtttl/src/anyrtttl.cpp new file mode 100644 index 0000000000..584e968795 --- /dev/null +++ b/lib/AnyRtttl/src/anyrtttl.cpp @@ -0,0 +1,672 @@ +// --------------------------------------------------------------------------- +// AUTHOR/LICENSE: +// The following code was written by Antoine Beauchamp. For other authors, see AUTHORS file. +// The code & updates for the library can be found at https://github.com/end2endzone/AnyRtttl +// MIT License: http://www.opensource.org/licenses/mit-license.php +// --------------------------------------------------------------------------- +#include "Arduino.h" +#include "anyrtttl.h" +#include "binrtttl.h" + +/********************************************************* + * RTTTL Library data + *********************************************************/ + +namespace anyrtttl +{ + +const uint16_t notes[] = { NOTE_SILENT, +NOTE_C4, NOTE_CS4, NOTE_D4, NOTE_DS4, NOTE_E4, NOTE_F4, NOTE_FS4, NOTE_G4, NOTE_GS4, NOTE_A4, NOTE_AS4, NOTE_B4, +NOTE_C5, NOTE_CS5, NOTE_D5, NOTE_DS5, NOTE_E5, NOTE_F5, NOTE_FS5, NOTE_G5, NOTE_GS5, NOTE_A5, NOTE_AS5, NOTE_B5, +NOTE_C6, NOTE_CS6, NOTE_D6, NOTE_DS6, NOTE_E6, NOTE_F6, NOTE_FS6, NOTE_G6, NOTE_GS6, NOTE_A6, NOTE_AS6, NOTE_B6, +NOTE_C7, NOTE_CS7, NOTE_D7, NOTE_DS7, NOTE_E7, NOTE_F7, NOTE_FS7, NOTE_G7, NOTE_GS7, NOTE_A7, NOTE_AS7, NOTE_B7 +}; + +#define isdigit(n) (n >= '0' && n <= '9') +typedef uint16_t TONE_DURATION; +static const byte NOTES_PER_OCTAVE = 12; + +const char * buffer = ""; +ReadCharFuncPtr readCharFunc = &readChar; +int bufferIndex = -32760; +byte default_dur = 4; +byte default_oct = 5; +RTTTL_BPM bpm = 63; +RTTTL_DURATION wholenote; +byte pin = -1; +unsigned long delayToNextNote = 0; //milliseconds before playing the next note +bool playing = false; +TONE_DURATION duration; +byte noteOffset; +RTTTL_OCTAVE_VALUE scale; +int tmpNumber; + +const char * readNumber(const char * iBuffer, int & oValue, ReadCharFuncPtr iReadCharFunc) +{ + oValue = 0; + while(isdigit(iReadCharFunc(iBuffer))) + { + oValue = (oValue * 10) + (readCharFunc(iBuffer) - '0'); + iBuffer++; + } + return iBuffer; +} + +void serialPrint(const char * iBuffer, ReadCharFuncPtr iReadCharFunc) +{ + char c = readCharFunc(iBuffer); + while(c) { + Serial.print(c); + iBuffer++; + c = readCharFunc(iBuffer); + } +} + +/**************************************************************************** + * Custom functions + ****************************************************************************/ + +ToneFuncPtr _tone = &tone; +NoToneFuncPtr _noTone = &noTone; +DelayFuncPtr _delay = &delay; +MillisFuncPtr _millis = &millis; + +void setToneFunction(ToneFuncPtr iFunc) { + _tone = iFunc; +} + +void setNoToneFunction(NoToneFuncPtr iFunc) { + _noTone = iFunc; +} + +void setDelayFunction(DelayFuncPtr iFunc) { + _delay = iFunc; +} + +void setMillisFunction(MillisFuncPtr iFunc) { + _millis = iFunc; +} + +char readChar(const char * iBuffer) { + return *iBuffer; +} + +char readChar_P(const char * iBuffer) { + return pgm_read_byte_near(iBuffer); +} + + + +/**************************************************************************** + * Blocking API + ****************************************************************************/ +namespace blocking +{ + +void play(byte iPin, const char * iBuffer, ReadCharFuncPtr iReadCharFunc) { + // Absolutely no error checking in here + + default_dur = 4; + default_oct = 6; + bpm = 63; + buffer = iBuffer; + readCharFunc = iReadCharFunc; + + #ifdef ANY_RTTTL_DEBUG + Serial.print("playing: "); + serialPrint(buffer, readCharFunc); + Serial.println(); + #endif + + // format: d=N,o=N,b=NNN: + // find the start (skip name, etc) + + while(readCharFunc(buffer) != ':') buffer++; // ignore name + buffer++; // skip ':' + + // get default duration + if(readCharFunc(buffer) == 'd') + { + buffer++; buffer++; // skip "d=" + buffer = readNumber(buffer, tmpNumber, readCharFunc); + if(tmpNumber > 0) + default_dur = tmpNumber; + buffer++; // skip comma + } + + #ifdef ANY_RTTTL_INFO + Serial.print("ddur: "); Serial.println(default_dur, 10); + #endif + + // get default octave + if(readCharFunc(buffer) == 'o') + { + buffer++; buffer++; // skip "o=" + buffer = readNumber(buffer, tmpNumber, readCharFunc); + if(tmpNumber >= 3 && tmpNumber <= 7) + default_oct = tmpNumber; + buffer++; // skip comma + } + + #ifdef ANY_RTTTL_INFO + Serial.print("doct: "); Serial.println(default_oct, 10); + #endif + + // get BPM + if(readCharFunc(buffer) == 'b') + { + buffer++; buffer++; // skip "b=" + buffer = readNumber(buffer, tmpNumber, readCharFunc); + bpm = tmpNumber; + buffer++; // skip colon + } + + #ifdef ANY_RTTTL_INFO + Serial.print("bpm: "); Serial.println(bpm, 10); + #endif + + // BPM usually expresses the number of quarter notes per minute + wholenote = (60 * 1000L / bpm) * 4; // this is the time for whole noteOffset (in milliseconds) + + #ifdef ANY_RTTTL_INFO + Serial.print("wn: "); Serial.println(wholenote, 10); + #endif + + // now begin note loop + while(readCharFunc(buffer)) + { + // first, get note duration, if available + buffer = readNumber(buffer, tmpNumber, readCharFunc); + + if(tmpNumber) + duration = wholenote / tmpNumber; + else + duration = wholenote / default_dur; // we will need to check if we are a dotted noteOffset after + + // now get the note + noteOffset = getNoteOffsetFromLetter(readCharFunc(buffer)); + buffer++; + + // now, get optional '#' sharp + if(readCharFunc(buffer) == '#') + { + noteOffset++; + buffer++; + } + + // now, get optional '.' dotted note + if(readCharFunc(buffer) == '.') + { + duration += duration/2; + buffer++; + } + + // now, get scale + if(isdigit(readCharFunc(buffer))) + { + scale = readCharFunc(buffer) - '0'; + buffer++; + } + else + { + scale = default_oct; + } + + if(readCharFunc(buffer) == ',') + buffer++; // skip comma for next note (or we may be at the end) + + // now play the note + if(noteOffset) + { + uint16_t frequency = notes[(scale - 4) * NOTES_PER_OCTAVE + noteOffset]; + + #ifdef ANY_RTTTL_INFO + Serial.print("Playing: "); + Serial.print(scale, 10); Serial.print(' '); + Serial.print(noteOffset, 10); Serial.print(" ("); + Serial.print(frequency, 10); + Serial.print(") "); + Serial.println(duration, 10); + #endif + + _tone(iPin, frequency, duration); + _delay(duration+1); + _noTone(iPin); + } + else + { + #ifdef ANY_RTTTL_INFO + Serial.print("Pausing: "); + Serial.println(duration, 10); + #endif + _delay(duration); + } + } +} + +void play(byte iPin, const char * iBuffer) { play(iPin, iBuffer, &readChar); } + +void play(byte iPin, const __FlashStringHelper* str) { play(iPin, (const char *)str, &readChar_P); } +void playProgMem(byte iPin, const char * iBuffer) { play(iPin, iBuffer, &readChar_P); } +void play_P(byte iPin, const char * iBuffer) { play(iPin, iBuffer, &readChar_P); } +void play_P(byte iPin, const __FlashStringHelper* str) { play(iPin, (const char *)str, &readChar_P); } + +void play16Bits(int iPin, const unsigned char * iBuffer, int iNumNotes) { + // Absolutely no error checking in here + + RTTTL_DEFAULT_VALUE_SECTION * defaultSection = (RTTTL_DEFAULT_VALUE_SECTION *)iBuffer; + RTTTL_NOTE * notesBuffer = (RTTTL_NOTE *)iBuffer; + + bpm = defaultSection->bpm; + + #ifdef ANY_RTTTL_DEBUG + Serial.print("numNotes="); + Serial.println(iNumNotes); + // format: d=N,o=N,b=NNN: + Serial.print("d="); + Serial.print(getNoteDurationFromIndex(defaultSection->durationIdx)); + Serial.print(",o="); + Serial.print(getNoteOctaveFromIndex(defaultSection->octaveIdx)); + Serial.print(",b="); + Serial.println(bpm); + #endif + + // BPM usually expresses the number of quarter notes per minute + wholenote = (60 * 1000L / bpm) * 4; // this is the time for whole noteOffset (in milliseconds) + + // now begin note loop + for(int i=0; i= 0 && iIndex < gNoteLettersCount) + return gNoteLetters[iIndex]; + return -1; +} + +uint16_t getNoteLettersCount() +{ + return gNoteLettersCount; +} + +NOTE_LETTER_INDEX findNoteLetterIndex(RTTTL_NOTE_LETTER n) +{ + for(NOTE_LETTER_INDEX i=0; i= 0 && iIndex < gNoteLettersCount) + return gNoteOffsets[iIndex]; + return 0; +} + +int getNoteOffsetFromLetter(RTTTL_NOTE_LETTER n) +{ + NOTE_LETTER_INDEX index = findNoteLetterIndex(n); + return getNoteOffsetFromLetterIndex(index); +} + +RTTTL_DURATION getNoteDurationFromIndex(DURATION_INDEX iIndex) +{ + if (iIndex >= 0 && iIndex < gNoteDurationsCount) + return gNoteDurations[iIndex]; + return -1; +} + +uint16_t getNoteDurationsCount() +{ + return gNoteDurationsCount; +} + +DURATION_INDEX findNoteDurationIndex(RTTTL_DURATION n) +{ + for(DURATION_INDEX i=0; i= 0 && iIndex < gNoteOctavesCount) + return gNoteOctaves[iIndex]; + return -1; +} + +uint16_t getNoteOctavesCount() +{ + return gNoteOctavesCount; +} + +OCTAVE_INDEX findNoteOctaveIndex(RTTTL_OCTAVE_VALUE n) +{ + for(OCTAVE_INDEX i=0; i= 0 && iIndex < gNoteBpmsCount) + return gNoteBpms[iIndex]; + return -1; +} + +uint16_t getBpmsCount() +{ + return gNoteBpmsCount; +} + +BPM_INDEX findBpmIndex(RTTTL_BPM n) +{ + for(BPM_INDEX i=0; i +#include + +#include "TestAnyRtttl.h" +#include "Arduino.h" +#include "anyrtttl.h" +#include "bitreader.h" + +#include "rapidassist/strings.h" +#include "rapidassist/testing.h" +#include "rapidassist/filesystem.h" + +#include "IncrementalClockStrategy.h" + +using namespace testarduino; + +//rtttl native format +const char * tetris = "tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a"; + +//RTTTL 10 bits binary format for the following: tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a +//Compatible with AnyRtttl library v2.0 +//The code & updates for the AnyRtttl library can be found on http://end2endzone.com +const unsigned char tetris10[] = {0x0A, 0x14, 0x12, 0xCE, 0x34, 0xE0, 0x82, 0x14, 0x32, 0x38, 0xE0, 0x4C, 0x2A, 0xAD, 0x34, 0xA0, 0x84, 0x0B, 0x0E, 0x28, 0xD3, 0x4C, 0x03, 0x2A, 0x28, 0xA1, 0x80, 0x2A, 0xA5, 0xB4, 0x93, 0x82, 0x1B, 0xAA, 0x38, 0xE2, 0x86, 0x12, 0x4E, 0x38, 0xA0, 0x84, 0x0B, 0x0E, 0x28, 0xD3, 0x4C, 0x03, 0x2A, 0x28, 0xA1, 0x80, 0x2A, 0xA9, 0x04}; +const int tetris10_length = 42; + +//RTTTL 16 bits binary format for the following: tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a +//Compatible with AnyRtttl library v2.0 +//The code & updates for the AnyRtttl library can be found on http://end2endzone.com +const unsigned char tetris16[] = {0x0A, 0x14, 0x12, 0x02, 0x33, 0x01, 0x03, 0x02, 0x0B, 0x02, 0x14, 0x02, 0x0C, 0x02, 0x03, 0x02, 0x33, 0x01, 0x2A, 0x01, 0x2B, 0x01, 0x03, 0x02, 0x12, 0x02, 0x0B, 0x02, 0x03, 0x02, 0x32, 0x01, 0x33, 0x01, 0x03, 0x02, 0x0A, 0x02, 0x12, 0x02, 0x02, 0x02, 0x2A, 0x01, 0x29, 0x01, 0x3B, 0x01, 0x0A, 0x02, 0x1B, 0x02, 0x2A, 0x02, 0x23, 0x02, 0x1B, 0x02, 0x12, 0x02, 0x13, 0x02, 0x03, 0x02, 0x12, 0x02, 0x0B, 0x02, 0x03, 0x02, 0x32, 0x01, 0x33, 0x01, 0x03, 0x02, 0x0A, 0x02, 0x12, 0x02, 0x02, 0x02, 0x2A, 0x01, 0x2A, 0x01}; +const int tetris16_length = 42; + + +#define PIEZO_PIN 99 + +namespace arduino { namespace test +{ + + //bit reader support + #ifndef USE_BITADDRESS_READ_WRITE + BitReader bitreader; + #else + BitAddress bitreader; + #endif + uint16_t readNextBits(uint8_t numBits) + { + uint16_t bits = 0; + bitreader.read(numBits, &bits); + return bits; + } + + void filterToneFunctions(ra::strings::StringVector & calls) + { + ra::strings::StringVector copy = calls; + calls.clear(); + + for(size_t i=0; icurrent_test_info()->name()) + ".log"; + testarduino::setLogFile(logFile.c_str()); + + //play the actual content + anyrtttl::nonblocking::begin(PIEZO_PIN, tetris); + while( !anyrtttl::nonblocking::done() ) + { + anyrtttl::nonblocking::play(); + } + + //assert + + //load both files into memory + ra::strings::StringVector expectedCalls; + ASSERT_TRUE (ra::testing::GetTextFileContent("expected_call_stack.log", expectedCalls)); + ra::strings::StringVector actualCalls; + ASSERT_TRUE (ra::testing::GetTextFileContent(logFile.c_str(), actualCalls)); + + //assert that file ends with a noTone(); + std::string lastCall = actualCalls[actualCalls.size()-1]; + ASSERT_NE(lastCall.find("noTone("), std::string::npos); + + //compare tone() function calls with the "blocking" template + //file since its the only common thing betwwen blocking and + //non-blocking api. + + //filter out anything that is not tone(...) + filterToneFunctions(expectedCalls); + filterToneFunctions(actualCalls); + + ASSERT_EQ( expectedCalls.size(), actualCalls.size() ); + for(size_t i=0; icurrent_test_info()->name()) + ".log"; + testarduino::setLogFile(logFile.c_str()); + + anyrtttl::nonblocking::begin(PIEZO_PIN, tetris); + + //play for 5 sec then stop. + //note: this is a blocking code section + //use to demonstrate the use of stop() + unsigned long start = millis(); + while( millis() - start < 5000 ) + { + anyrtttl::nonblocking::play(); + } + anyrtttl::nonblocking::stop(); + + //assert + + //load output file into memory + ra::strings::StringVector actualCalls; + ASSERT_TRUE (ra::testing::GetTextFileContent(logFile.c_str(), actualCalls)); + + //assert that file ends with a noTone(); + std::string lastCall = actualCalls[actualCalls.size()-1]; + ASSERT_NE(lastCall.find("noTone("), std::string::npos); + } + //-------------------------------------------------------------------------------------------------- + TEST_F(TestAnyRtttl, testBlockingPlay) + { + //setup log file + std::string logFile = std::string(::testing::UnitTest::GetInstance()->current_test_info()->name()) + ".log"; + testarduino::setLogFile(logFile.c_str()); + + //play the actual content + anyrtttl::blocking::play(PIEZO_PIN, tetris); + + //assert + ASSERT_TRUE( fileContentReplace(logFile.c_str(), "millis();\n", "") ); + ASSERT_TRUE( fileContentReplace(logFile.c_str(), "millis();\r\n", "") ); //windows + std::string diffReason; + bool fileAreIdentical = ra::testing::IsFileEquals("expected_call_stack.log", logFile.c_str(), diffReason, 1); + ASSERT_TRUE( fileAreIdentical ) << diffReason.c_str(); + } + //-------------------------------------------------------------------------------------------------- + TEST_F(TestAnyRtttl, testBlockingPlay10) + { + //setup log file + std::string logFile = std::string(::testing::UnitTest::GetInstance()->current_test_info()->name()) + ".log"; + testarduino::setLogFile(logFile.c_str()); + + //play the actual content + bitreader.setBuffer(tetris10); + anyrtttl::blocking::play10Bits(PIEZO_PIN, tetris10_length, &readNextBits); + + //assert + ASSERT_TRUE( fileContentReplace(logFile.c_str(), "millis();\n", "") ); + ASSERT_TRUE( fileContentReplace(logFile.c_str(), "millis();\r\n", "") ); //windows + std::string diffReason; + bool fileAreIdentical = ra::testing::IsFileEquals("expected_call_stack.log", logFile.c_str(), diffReason, 1); + ASSERT_TRUE( fileAreIdentical ) << diffReason.c_str(); + } + //-------------------------------------------------------------------------------------------------- + TEST_F(TestAnyRtttl, testBlockingPlay16) + { + //setup log file + std::string logFile = std::string(::testing::UnitTest::GetInstance()->current_test_info()->name()) + ".log"; + testarduino::setLogFile(logFile.c_str()); + + //play the actual content + anyrtttl::blocking::play16Bits(PIEZO_PIN, tetris16, tetris16_length); + + //assert + ASSERT_TRUE( fileContentReplace(logFile.c_str(), "millis();\n", "") ); + ASSERT_TRUE( fileContentReplace(logFile.c_str(), "millis();\r\n", "") ); //windows + std::string diffReason; + bool fileAreIdentical = ra::testing::IsFileEquals("expected_call_stack.log", logFile.c_str(), diffReason, 1); + ASSERT_TRUE( fileAreIdentical ) << diffReason.c_str(); + } + //-------------------------------------------------------------------------------------------------- + static int toneCounts = 0; + static int noToneCounts = 0; + static int delayCounts = 0; + void myToneFunc(uint8_t pin, unsigned int frequency, unsigned long duration) + { + toneCounts++; + } + void myNoToneFunc(uint8_t pin) + { + noToneCounts++; + } + void myDelayFunc(unsigned long duration) + { + delayCounts++; + } + + TEST_F(TestAnyRtttl, testCustomFunctionsPlay) + { + //setup log file + std::string logFile = std::string(::testing::UnitTest::GetInstance()->current_test_info()->name()) + ".log"; + testarduino::setLogFile(logFile.c_str()); + + toneCounts = 0; + noToneCounts = 0; + delayCounts = 0; + + //use test's custom functions + anyrtttl::setToneFunction(&myToneFunc); + anyrtttl::setNoToneFunction(&myNoToneFunc); + anyrtttl::setDelayFunction(&myDelayFunc); + + //play the actual content + anyrtttl::blocking::play(PIEZO_PIN, tetris); + + //back to native arduino functions + anyrtttl::setToneFunction(&tone); + anyrtttl::setNoToneFunction(&noTone); + anyrtttl::setDelayFunction(&delay); + + //assert + ASSERT_GT( toneCounts , 0 ); + ASSERT_GT( noToneCounts, 0 ); + ASSERT_GT( delayCounts , 0 ); + } + //-------------------------------------------------------------------------------------------------- +} // End namespace test +} // End namespace arduino diff --git a/lib/AnyRtttl/test/TestAnyRtttl.h b/lib/AnyRtttl/test/TestAnyRtttl.h new file mode 100644 index 0000000000..8fdd41183a --- /dev/null +++ b/lib/AnyRtttl/test/TestAnyRtttl.h @@ -0,0 +1,25 @@ +// --------------------------------------------------------------------------- +// AUTHOR/LICENSE: +// The following code was written by Antoine Beauchamp. For other authors, see AUTHORS file. +// The code & updates for the library can be found at https://github.com/end2endzone/AnyRtttl +// MIT License: http://www.opensource.org/licenses/mit-license.php +// --------------------------------------------------------------------------- + +#ifndef TEST_RTTTL_H +#define TEST_RTTTL_H + +#include + +namespace arduino { namespace test +{ + class TestAnyRtttl : public ::testing::Test + { + public: + virtual void SetUp(); + virtual void TearDown(); + }; + +} // End namespace test +} // End namespace arduino + +#endif // TEST_RTTTL_H diff --git a/lib/AnyRtttl/test/expected_call_stack.log b/lib/AnyRtttl/test/expected_call_stack.log new file mode 100644 index 0000000000..5fc4e6e92a --- /dev/null +++ b/lib/AnyRtttl/test/expected_call_stack.log @@ -0,0 +1,124 @@ +tone(99,1319,375); +delay(376); +noTone(99); +tone(99,988,187); +delay(188); +noTone(99); +tone(99,1047,187); +delay(188); +noTone(99); +tone(99,1175,187); +delay(188); +noTone(99); +tone(99,1319,93); +delay(94); +noTone(99); +tone(99,1175,93); +delay(94); +noTone(99); +tone(99,1047,187); +delay(188); +noTone(99); +tone(99,988,187); +delay(188); +noTone(99); +tone(99,880,375); +delay(376); +noTone(99); +tone(99,880,187); +delay(188); +noTone(99); +tone(99,1047,187); +delay(188); +noTone(99); +tone(99,1319,375); +delay(376); +noTone(99); +tone(99,1175,187); +delay(188); +noTone(99); +tone(99,1047,187); +delay(188); +noTone(99); +tone(99,988,375); +delay(376); +noTone(99); +tone(99,988,187); +delay(188); +noTone(99); +tone(99,1047,187); +delay(188); +noTone(99); +tone(99,1175,375); +delay(376); +noTone(99); +tone(99,1319,375); +delay(376); +noTone(99); +tone(99,1047,375); +delay(376); +noTone(99); +tone(99,880,375); +delay(376); +noTone(99); +tone(99,880,750); +delay(751); +noTone(99); +delay(187); +tone(99,1175,375); +delay(376); +noTone(99); +tone(99,1397,187); +delay(188); +noTone(99); +tone(99,1760,375); +delay(376); +noTone(99); +tone(99,1568,187); +delay(188); +noTone(99); +tone(99,1397,187); +delay(188); +noTone(99); +tone(99,1319,375); +delay(376); +noTone(99); +tone(99,1319,187); +delay(188); +noTone(99); +tone(99,1047,187); +delay(188); +noTone(99); +tone(99,1319,375); +delay(376); +noTone(99); +tone(99,1175,187); +delay(188); +noTone(99); +tone(99,1047,187); +delay(188); +noTone(99); +tone(99,988,375); +delay(376); +noTone(99); +tone(99,988,187); +delay(188); +noTone(99); +tone(99,1047,187); +delay(188); +noTone(99); +tone(99,1175,375); +delay(376); +noTone(99); +tone(99,1319,375); +delay(376); +noTone(99); +tone(99,1047,375); +delay(376); +noTone(99); +tone(99,880,375); +delay(376); +noTone(99); +tone(99,880,375); +delay(376); +noTone(99); diff --git a/lib/AnyRtttl/test/main.cpp b/lib/AnyRtttl/test/main.cpp new file mode 100644 index 0000000000..c3b44b2c8d --- /dev/null +++ b/lib/AnyRtttl/test/main.cpp @@ -0,0 +1,26 @@ +// --------------------------------------------------------------------------- +// AUTHOR/LICENSE: +// The following code was written by Antoine Beauchamp. For other authors, see AUTHORS file. +// The code & updates for the library can be found at https://github.com/end2endzone/AnyRtttl +// MIT License: http://www.opensource.org/licenses/mit-license.php +// --------------------------------------------------------------------------- + +#include +#include +#include "rapidassist/environment.h" + +int main(int argc, char **argv) +{ + //define default values for xml output report + if (ra::environment::IsConfigurationDebug()) + ::testing::GTEST_FLAG(output) = "xml:anyrtttl_unittest.debug.xml"; + else + ::testing::GTEST_FLAG(output) = "xml:anyrtttl_unittest.release.xml"; + + ::testing::GTEST_FLAG(filter) = "*"; + ::testing::InitGoogleTest(&argc, argv); + + int wResult = RUN_ALL_TESTS(); //Find and run all tests + + return wResult; // returns 0 if all the tests are successful, or 1 otherwise +} diff --git a/src/Custom-sample.h b/src/Custom-sample.h index 00d228573b..8d7b8766d9 100644 --- a/src/Custom-sample.h +++ b/src/Custom-sample.h @@ -227,7 +227,9 @@ // #define FEATURE_I2C_DEVICE_CHECK 0 // Disable the I2C Device check feature // #define FEATURE_I2C_GET_ADDRESS 0 // Disable fetching the I2C address from I2C plugins. Will be enabled when FEATURE_I2C_DEVICE_CHECK is enabled // #define FEATURE_RTTTL 1 // Enable rtttl command - +// #define FEATURE_ANYRTTTL_LIB 1 // Use AnyRttl library for RTTTL handling +// #define FEATURE_ANYRTTTL_ASYNC 1 // When AnyRttl enabled, use Async (nonblocking) mode instead of the default Blocking mode +// #define FEATURE_RTTTL_EVENTS 1 // Enable RTTTL events for Async use, for blocking it doesn't make sense #if FEATURE_CUSTOM_PROVISIONING // For device models, see src/src/DataTypes/DeviceModel.h diff --git a/src/src/Commands/GPIO.cpp b/src/src/Commands/GPIO.cpp index ef87046003..0254c79609 100644 --- a/src/src/Commands/GPIO.cpp +++ b/src/src/Commands/GPIO.cpp @@ -97,8 +97,8 @@ bool gpio_monitor_helper(int port, struct EventStruct *event, const char *Line) if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, concat( - logPrefix, - strformat(F(" port #%d: added to monitor list."), port))); + logPrefix, + strformat(F(" port #%d: added to monitor list."), port))); } String dummy; SendStatusOnlyIfNeeded(event, SEARCH_PIN_STATE, key, dummy, 0); @@ -143,7 +143,7 @@ bool gpio_unmonitor_helper(int port, struct EventStruct *event, const char *Line removeMonitorFromPort(key); if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, concat( - logPrefix, + logPrefix, strformat(F(" port #%d: removed from monitor list."), port))); } @@ -385,12 +385,21 @@ const __FlashStringHelper * Command_GPIO_RTTTL(struct EventStruct *event, const // play a tune via a RTTTL string, look at https://www.letscontrolit.com/forum/viewtopic.php?f=4&t=343&hilit=speaker&start=10 for // more info. + // First assume 'new' syntax: rtttl,, + // Difference between 'old' and 'new': + // Comma between the GPIO argument and the melody String melody = parseStringToEndKeepCase(Line, 2); + melody = melody.substring(melody.indexOf(':'), melody.length()); + melody.replace('-', '#'); + melody.replace('_', '#'); if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat(F("RTTTL: pin: %d melody: %s"), event->Par1, melody.c_str())); } + #if FEATURE_ANYRTTTL_LIB && FEATURE_ANYRTTTL_ASYNC + set_rtttl_melody(melody); + #endif // if FEATURE_ANYRTTTL_LIB && FEATURE_ANYRTTTL_ASYNC if (play_rtttl(event->Par1, melody.c_str())) { return return_command_success_flashstr(); @@ -488,7 +497,7 @@ const __FlashStringHelper * Command_GPIO_Toggle(struct EventStruct *event, const String log = logPrefix; log += concat( - F(" toggle"), + F(" toggle"), strformat(F(": port#%d: set to %d"), event->Par1, static_cast(!state))); addLog(LOG_LEVEL_ERROR, log); SendStatusOnlyIfNeeded(event, SEARCH_PIN_STATE, key, log, 0); @@ -1145,7 +1154,6 @@ bool gpio_mode_range_helper(uint8_t pin, uint8_t pinMode, struct EventStruct *ev createAndSetPortStatus_Mode_State(key, mode, currentState); - String log = logPrefix; log += strformat(F(" : port#%d: MODE set to "), pin); log += logPostfix; @@ -1302,7 +1310,7 @@ bool getGPIOPinStateValues(String& str) { str = String(tempValue); break; } - #endif +#endif default: addLog(LOG_LEVEL_ERROR, F("PLUGIN PINSTATE. Plugin not included in build")); return false; diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 98897d01d9..8e5333fec7 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -2920,6 +2920,18 @@ To create/register a plugin, you have to : #undef FEATURE_RTTTL #define FEATURE_RTTTL 1 #endif +#if FEATURE_RTTTL && !defined(FEATURE_ANYRTTTL_LIB) + #define FEATURE_ANYRTTTL_LIB 1 // Enable AnyRtttl library +#endif +#ifndef FEATURE_ANYRTTTL_LIB + #define FEATURE_ANYRTTTL_LIB 0 +#endif +#ifndef FEATURE_ANYRTTTL_ASYNC + #define FEATURE_ANYRTTTL_ASYNC 1 // Use Async by default for better (non-blocking) behavior +#endif +#if FEATURE_ANYRTTTL_ASYNC && !defined(FEATURE_RTTTL_EVENTS) + #define FEATURE_RTTTL_EVENTS 1 // Enable RTTTL events for Async use, for blocking it doesn't make sense +#endif #ifndef FEATURE_SD #define FEATURE_SD 0 diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index b625906eee..8c56888ca7 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -259,7 +259,7 @@ class SettingsStruct_tmpl // - PinBootStates_ESP32 (index_high) // Returns whether it is a valid index bool getPinBootStateIndex( - uint8_t gpio_pin, + int8_t gpio_pin, int8_t& index_low #ifdef ESP32 , int8_t& index_high @@ -268,8 +268,8 @@ class SettingsStruct_tmpl public: - PinBootState getPinBootState(uint8_t gpio_pin) const; - void setPinBootState(uint8_t gpio_pin, PinBootState state); + PinBootState getPinBootState(int8_t gpio_pin) const; + void setPinBootState(int8_t gpio_pin, PinBootState state); bool getSPI_pins(int8_t spi_gpios[3]) const; diff --git a/src/src/DataStructs_templ/SettingsStruct.cpp b/src/src/DataStructs_templ/SettingsStruct.cpp index 9e14c78f8d..46cde6a3f9 100644 --- a/src/src/DataStructs_templ/SettingsStruct.cpp +++ b/src/src/DataStructs_templ/SettingsStruct.cpp @@ -694,7 +694,7 @@ String SettingsStruct_tmpl::getName() const { template bool SettingsStruct_tmpl::getPinBootStateIndex( - uint8_t gpio_pin, + int8_t gpio_pin, int8_t& index_low # ifdef ESP32 , int8_t& index_high @@ -703,17 +703,16 @@ bool SettingsStruct_tmpl::getPinBootStateIndex( index_low = -1; # ifdef ESP32 index_high = -1; - - if (!GPIO_IS_VALID_GPIO(gpio_pin)) { return false; } + if ((gpio_pin < 0) || !(GPIO_IS_VALID_GPIO(gpio_pin))) { return false; } # endif // ifdef ESP32 - constexpr uint8_t maxStates = NR_ELEMENTS(PinBootStates); + constexpr int maxStates = NR_ELEMENTS(PinBootStates); if (gpio_pin < maxStates) { index_low = gpio_pin; return true; } # ifdef ESP32 - constexpr uint8_t maxStatesesp32 = NR_ELEMENTS(PinBootStates_ESP32); + constexpr int maxStatesesp32 = NR_ELEMENTS(PinBootStates_ESP32); index_high = gpio_pin - maxStates; @@ -768,7 +767,8 @@ bool SettingsStruct_tmpl::getPinBootStateIndex( } template -PinBootState SettingsStruct_tmpl::getPinBootState(uint8_t gpio_pin) const { +PinBootState SettingsStruct_tmpl::getPinBootState(int8_t gpio_pin) const { + if (gpio_pin < 0) return PinBootState::Default_state; # ifdef ESP8266 int8_t index_low{}; @@ -795,7 +795,8 @@ PinBootState SettingsStruct_tmpl::getPinBootState(uint8_t gpio_pin) con } template -void SettingsStruct_tmpl::setPinBootState(uint8_t gpio_pin, PinBootState state) { +void SettingsStruct_tmpl::setPinBootState(int8_t gpio_pin, PinBootState state) { + if (gpio_pin < 0) return; # ifdef ESP8266 int8_t index_low{}; diff --git a/src/src/ESPEasyCore/ESPEasy_backgroundtasks.cpp b/src/src/ESPEasyCore/ESPEasy_backgroundtasks.cpp index 80d6c64c2b..1ad7a88cea 100644 --- a/src/src/ESPEasyCore/ESPEasy_backgroundtasks.cpp +++ b/src/src/ESPEasyCore/ESPEasy_backgroundtasks.cpp @@ -9,6 +9,9 @@ #include "../Globals/NetworkState.h" #include "../Globals/Services.h" #include "../Globals/Settings.h" +#if FEATURE_RTTTL && FEATURE_ANYRTTTL_LIB && FEATURE_ANYRTTTL_ASYNC +#include "../Helpers/Audio.h" +#endif // if FEATURE_RTTTL && FEATURE_ANYRTTTL_LIB && FEATURE_ANYRTTTL_ASYNC #include "../Helpers/ESPEasy_time_calc.h" #include "../Helpers/Network.h" #include "../Helpers/Networking.h" @@ -121,6 +124,10 @@ void backgroundtasks() delay(0); + #if FEATURE_RTTTL && FEATURE_ANYRTTTL_LIB && FEATURE_ANYRTTTL_ASYNC + update_rtttl(); + #endif // if FEATURE_RTTTL && FEATURE_ANYRTTTL_LIB && FEATURE_ANYRTTTL_ASYNC + statusLED(false); runningBackgroundTasks = false; diff --git a/src/src/Helpers/Audio.cpp b/src/src/Helpers/Audio.cpp index a7b3c6c831..0e2ffef0b2 100644 --- a/src/src/Helpers/Audio.cpp +++ b/src/src/Helpers/Audio.cpp @@ -1,5 +1,6 @@ #include "../Helpers/Audio.h" +#include "../ESPEasyCore/ESPEasyGPIO.h" #include "../Globals/RamTracker.h" #include "../Helpers/Hardware.h" @@ -8,58 +9,151 @@ Generate a tone of specified frequency on pin \*********************************************************************************************/ bool tone_espEasy(int8_t _pin, unsigned int frequency, unsigned long duration) { - if (_pin<0) return false; + if (!validGpio(_pin)) { return false; } // Duty cycle can be used as some kind of volume. - if (!set_Gpio_PWM_pct(_pin, 50, frequency)) return false; + if (!set_Gpio_PWM_pct(_pin, 50, frequency)) { return false; } + if (duration > 0) { delay(duration); return set_Gpio_PWM(_pin, 0, frequency); } return true; - } /********************************************************************************************\ Play RTTTL string on specified pin \*********************************************************************************************/ #if FEATURE_RTTTL +# if FEATURE_ANYRTTTL_LIB +# include +# include +# if FEATURE_RTTTL_EVENTS +# include "../Globals/EventQueue.h" +# include "../Globals/Settings.h" +static bool rtttlPlaying = false; +# endif // if FEATURE_RTTTL_EVENTS +# if FEATURE_ANYRTTTL_ASYNC +static String rtttlMelody; + +void clear_rtttl_melody() { + // The non-blocking play will read from a char pointer. + // So we must stop the playing before changing the string as it could otherwise lead to a crash. + if (anyrtttl::nonblocking::isPlaying()) { // If currently playing, cancel that + addLog(LOG_LEVEL_INFO, F("RTTTL: Cancelling running song...")); + anyrtttl::nonblocking::stop(); + # if FEATURE_RTTTL_EVENTS + + if (Settings.UseRules) { + eventQueue.add(F("RTTTL#Cancelled")); + } + rtttlPlaying = false; + # endif // if FEATURE_RTTTL_EVENTS + } + + rtttlMelody = String(); +} + +void set_rtttl_melody(String& melody) { + clear_rtttl_melody(); + rtttlMelody = melody; +} + +# endif // if FEATURE_ANYRTTTL_ASYNC + + +bool play_rtttl(int8_t _pin, const char *p) { + if (!validGpio(_pin)) { return false; } + + // addLog(LOG_LEVEL_INFO, F("RTTTL: Using AnyRtttl")); + + # ifndef BUILD_NO_RAM_TRACKER + checkRAM(F("play_rtttl")); + # endif // ifndef BUILD_NO_RAM_TRACKER + + anyrtttl::setNoToneFunction(&setInternalGPIOPullupMode); + # if FEATURE_ANYRTTTL_ASYNC + + if (!rtttlMelody.isEmpty()) { + anyrtttl::nonblocking::begin(_pin, rtttlMelody.c_str()); + } else { + anyrtttl::nonblocking::begin(_pin, p); + } + anyrtttl::nonblocking::play(); + # if FEATURE_RTTTL_EVENTS + + if (Settings.UseRules) { + eventQueue.add(F("RTTTL#Started")); + } + rtttlPlaying = true; + # endif // if FEATURE_RTTTL_EVENTS + # else // if FEATURE_ANYRTTTL_ASYNC + anyrtttl::blocking::play(_pin, p); + # endif // if FEATURE_ANYRTTTL_ASYNC + # ifndef BUILD_NO_RAM_TRACKER + checkRAM(F("play_rtttl2")); + # endif // ifndef BUILD_NO_RAM_TRACKER + return true; +} + +# if FEATURE_ANYRTTTL_ASYNC +void update_rtttl() { + if (anyrtttl::nonblocking::isPlaying()) { + anyrtttl::nonblocking::play(); + } else { + # if FEATURE_RTTTL_EVENTS + + if (rtttlPlaying) { + if (Settings.UseRules) { + eventQueue.add(F("RTTTL#Finished")); + } + rtttlPlaying = false; + } + # endif // if FEATURE_RTTTL_EVENTS + clear_rtttl_melody(); // Release memory + } +} + +# endif // if FEATURE_ANYRTTTL_ASYNC + +# else // if FEATURE_ANYRTTTL_LIB bool play_rtttl(int8_t _pin, const char *p) { - if (_pin<0) return false; - - #ifndef BUILD_NO_RAM_TRACKER + if (!validGpio(_pin)) { return false; } + + # ifndef BUILD_NO_RAM_TRACKER checkRAM(F("play_rtttl")); - #endif - #define OCTAVE_OFFSET 0 + # endif // ifndef BUILD_NO_RAM_TRACKER + # define OCTAVE_OFFSET 0 // FIXME: Absolutely no error checking in here const int notes[] = { 0, - 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, - 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, - 1047,1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, - 2093,2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951 + 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, + 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, + 1047,1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, + 2093,2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951 }; uint8_t default_dur = 4; uint8_t default_oct = 6; - int bpm = 63; - int num; - long wholenote; - long duration; + int bpm = 63; + int num; + long wholenote; + long duration; uint8_t note; uint8_t scale; // format: d=N,o=N,b=NNN: // find the start (skip name, etc) - while (*p != ':') { + while (*p != ':') { p++; // ignore name - if (*p == 0) return false; + + if (*p == 0) { return false; } } - p++; // skip ':' + p++; // skip ':' // get default duration if (*p == 'd') @@ -191,9 +285,12 @@ bool play_rtttl(int8_t _pin, const char *p) delay(duration / 10); } } - #ifndef BUILD_NO_RAM_TRACKER + setInternalGPIOPullupMode(_pin); // Turn off sound, Arduino _noTone() doesn't do that reliably + # ifndef BUILD_NO_RAM_TRACKER checkRAM(F("play_rtttl2")); - #endif + # endif // ifndef BUILD_NO_RAM_TRACKER return true; } + +# endif // if FEATURE_ANYRTTTL_LIB #endif // if FEATURE_RTTTL diff --git a/src/src/Helpers/Audio.h b/src/src/Helpers/Audio.h index 297179d3d6..bff26501a7 100644 --- a/src/src/Helpers/Audio.h +++ b/src/src/Helpers/Audio.h @@ -7,7 +7,7 @@ /********************************************************************************************\ Generate a tone of specified frequency on pin \*********************************************************************************************/ -bool tone_espEasy(int8_t _pin, +bool tone_espEasy(int8_t _pin, unsigned int frequency, unsigned long duration); @@ -15,9 +15,14 @@ bool tone_espEasy(int8_t _pin, Play RTTTL string on specified pin \*********************************************************************************************/ #if FEATURE_RTTTL -bool play_rtttl(int8_t _pin, +bool play_rtttl(int8_t _pin, const char *p); +# if FEATURE_ANYRTTTL_LIB && FEATURE_ANYRTTTL_ASYNC +void update_rtttl(); +void clear_rtttl_melody(); +void set_rtttl_melody(String& melody); +# endif // if FEATURE_ANYRTTTL_LIB && FEATURE_ANYRTTTL_ASYNC #endif // if FEATURE_RTTTL -#endif \ No newline at end of file +#endif // ifndef HELPERS_AUDIO_H diff --git a/src/src/Helpers/Hardware.cpp b/src/src/Helpers/Hardware.cpp index 7321c9c062..df3d2be76f 100644 --- a/src/src/Helpers/Hardware.cpp +++ b/src/src/Helpers/Hardware.cpp @@ -617,16 +617,27 @@ extern "C" { uint8_t temprature_sens_read(); } #elif defined(ESP32C3) || defined(ESP32S2) || defined(ESP32S3) -#include "driver/temp_sensor.h" + #include + #if ESP_IDF_VERSION_MAJOR < 5 + // Work-around for bug in ESP-IDF < 5.0 + #if defined(ESP32S3) || defined(ESP32C3) + #include + #elif defined(ESP32S2) + #include + #endif + + #endif #endif // ESP32_CLASSIC #endif // ESP32 float getInternalTemperature() { - static float temperature = -273.15f; // Improbable value - int8_t retries = 2; + static float temperature_filtered = NAN; // Improbable value + float celsius{}; + esp_err_t result = ESP_FAIL; #ifdef ESP32 #if defined(ESP32_CLASSIC) uint8_t raw = 128u; + int8_t retries = 2; while ((128u == raw) && (0 != retries)) { delay(0); raw = temprature_sens_read(); // Each reading takes about 112 microseconds @@ -636,21 +647,81 @@ float getInternalTemperature() { addLog(LOG_LEVEL_DEBUG, concat(F("ESP32: Raw temperature value: "), raw)); #endif if (raw != 128) { - temperature = (raw - 32) / 1.8f; + result = ESP_OK; + // Raw value is in Fahrenheit + celsius = (raw - 32) / 1.8f; } #elif defined(ESP32C3) || defined(ESP32S2) || defined(ESP32S3) + temp_sensor_config_t tsens = TSENS_CONFIG_DEFAULT(); temp_sensor_set_config(tsens); - float tmpTemp = 0.0f; temp_sensor_start(); - esp_err_t result = temp_sensor_read_celsius(&tmpTemp); + + #if ESP_IDF_VERSION_MAJOR < 5 + // Work-around for bug in ESP-IDF < 5.0 + // Seems to be fixed in ESP_IDF5.1 + // temp_sensor_get_config always returns ESP_OK + // Thus dac_offset can be just about anything + // dac_offset is used as index in an array without bounds checking + { + #if defined(ESP32S3) || defined(ESP32C3) + static float s_deltaT = (esp_efuse_rtc_calib_get_ver() == 1) ? + (esp_efuse_rtc_calib_get_cal_temp(1) / 10.0f) : + 0.0f; + #elif defined(ESP32S2) + static uint32_t version = esp_efuse_rtc_table_read_calib_version(); + static float s_deltaT = (version == 1 || version == 2) ? + (esp_efuse_rtc_table_get_parsed_efuse_value(RTCCALIB_IDX_TMPSENSOR, false) / 10.0f) : + 0.0f; + #endif + + +/* + if (isnan(s_deltaT)) { //suggests that the value is not initialized + uint32_t version = esp_efuse_rtc_calib_get_ver(); + if (version == 1) { + // fetch calibration value for temp sensor from eFuse + s_deltaT = esp_efuse_rtc_calib_get_cal_temp(version); + } else { + // no value to fetch, use 0. + s_deltaT = 0; + } + } +*/ + #ifndef TSENS_ADC_FACTOR + #define TSENS_ADC_FACTOR (0.4386) + #endif + #ifndef TSENS_DAC_FACTOR + #define TSENS_DAC_FACTOR (27.88) + #endif + #ifndef TSENS_SYS_OFFSET + #define TSENS_SYS_OFFSET (20.52) + #endif + uint32_t tsens_raw{}; + temp_sensor_read_raw(&tsens_raw); + celsius = (TSENS_ADC_FACTOR * tsens_raw) - s_deltaT - TSENS_SYS_OFFSET; + result = ESP_OK; + } + + #else + + result = temp_sensor_read_celsius(&celsius); + + #endif + temp_sensor_stop(); + #endif // ESP32_CLASSIC if (result == ESP_OK) { - temperature = tmpTemp; + if (isnanf(temperature_filtered)) { + temperature_filtered = celsius; + } else { + constexpr float IIR_FACTOR = 5.0f; + constexpr float IIR_DIVIDER = IIR_FACTOR + 1.0f; + temperature_filtered = ((IIR_FACTOR * temperature_filtered) + celsius) / IIR_DIVIDER; + } } - #endif // ESP32_CLASSIC #endif // USE_ESP32 - return temperature; + return temperature_filtered; } #endif // if FEATURE_INTERNAL_TEMPERATURE @@ -1660,7 +1731,7 @@ bool getGpioInfo(int gpio, int& pinnr, bool& input, bool& output, bool& warning) output = GPIO_IS_VALID_OUTPUT_GPIO(gpio); warning = false; - if (!GPIO_IS_VALID_GPIO(gpio)) return false; + if ((gpio < 0) || !(GPIO_IS_VALID_GPIO(gpio))) { return false; } # ifdef ESP32S2 @@ -2041,11 +2112,12 @@ bool getGpioPullResistor(int gpio, bool& hasPullUp, bool& hasPullDown) { #endif // ifdef ESP8266 bool validGpio(int gpio) { + if (gpio < 0) { return false; } #ifdef ESP32 if (!GPIO_IS_VALID_GPIO(gpio)) { return false; } #endif #ifdef ESP8266 - if ((gpio < 0) || (gpio > MAX_GPIO)) { return false; } + if (gpio > MAX_GPIO) { return false; } #endif int pinnr; bool input; diff --git a/src/src/PluginStructs/P037_data_struct.h b/src/src/PluginStructs/P037_data_struct.h index 072b3725d4..5d6b8bc3d5 100644 --- a/src/src/PluginStructs/P037_data_struct.h +++ b/src/src/PluginStructs/P037_data_struct.h @@ -15,8 +15,8 @@ // # define PLUGIN_037_DEBUG // Additional debugging information -# if defined(PLUGIN_BUILD_CUSTOM) || defined(PLUGIN_BUILD_MAX_ESP32) \ - || (defined(PLUGIN_SET_STABLE) && !(defined(PLUGIN_SET_COLLECTION) || defined(PLUGIN_ENERGY_COLLECTION))) +# if (defined(PLUGIN_BUILD_CUSTOM) || defined(PLUGIN_BUILD_MAX_ESP32) || defined(ESP32) \ + || (defined(PLUGIN_SET_STABLE)) && !((defined(PLUGIN_SET_COLLECTION) || defined(PLUGIN_ENERGY_COLLECTION)) && defined(ESP8266))) # ifndef P037_MAPPING_SUPPORT # define P037_MAPPING_SUPPORT 1 // Enable Value mapping support # endif // ifndef P037_MAPPING_SUPPORT @@ -52,7 +52,7 @@ # if (defined(FEATURE_ADC_VCC) || defined(P037_LIMIT_BUILD_SIZE)) && P037_FILTER_SUPPORT # undef P037_FILTER_SUPPORT # define P037_FILTER_SUPPORT 0 -# endif +# endif // if (defined(FEATURE_ADC_VCC) || defined(P037_LIMIT_BUILD_SIZE)) && P037_FILTER_SUPPORT // #if P037_JSON_SUPPORT // #undef P037_JSON_SUPPORT @@ -167,11 +167,11 @@ struct P037_data_struct : public PluginTaskData_base // The settings structures // The stuff we want to save between settings - String mqttTopics[VARS_PER_TASK] = {}; + String mqttTopics[VARS_PER_TASK] = {}; String jsonAttributes[VARS_PER_TASK] = {}; - String globalTopicPrefix = {}; - String valueArray[P037_ARRAY_SIZE] = {}; // Layout: P037_START_MAPPINGS..P037_END_MAPPINGS = mappings, - // P037_START_FILTERS..P037_END_FILTERS = filters + String globalTopicPrefix = {}; + String valueArray[P037_ARRAY_SIZE] = {}; // Layout: P037_START_MAPPINGS..P037_END_MAPPINGS = mappings, + // P037_START_FILTERS..P037_END_FILTERS = filters String getFullMQTTTopic(uint8_t taskValueIndex) const;