diff --git a/.gitignore b/.gitignore index 293347b3..6d487764 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ .vscode/ipch build_output src/GUI-Generic.ino.cpp +.project diff --git a/fr.h b/fr.h new file mode 100644 index 00000000..d127405a --- /dev/null +++ b/fr.h @@ -0,0 +1,105 @@ +#ifndef _LANGUAGE_FR_S_H_ +#define _LANGUAGE_FR_S_H_ + +// translated by Fryga +#define S_SETTING_WIFI_SSID "Mettre à jour du WIFI" +#define S_WIFI_SSID "Nom du WIFI" +#define S_WIFI_PASS "Mot de passe" +#define S_HOST_NAME "Nom du module" +#define S_SETTING_SUPLA "Paramètres SUPLA" +#define S_SUPLA_SERVER "Adresse du serveur" +#define S_SUPLA_EMAIL "E-mail" +#define S_SETTING_ADMIN "Paramètres administrateur" +#define S_LOGIN "Connexion" +#define S_LOGIN_PASS "Mot de passe" +#define S_ROLLERSHUTTERS "Volet roulant" +#define S_SAVE "Enregistrer" +#define S_DEVICE_SETTINGS "Paramètres du module" +#define S_UPDATE "Mettre à jour" +#define S_RESTART "Réinitialisation" +#define S_RETURN "Retour" +#define S_TEMPLATE_BOARD "Modèle de la planches" +#define S_TYPE "Genre" +#define S_RELAYS "LES RELAIS" +#define S_BUTTONS "LES BOUTONS" +#define S_SENSORS_1WIRE "LES SENSORS du 1Wire" +#define S_SENSORS_I2C "LES SENSORS du i2c" +#define S_SENSORS_SPI "LES SENSORS du SPI" +#define S_LED_BUTTON_CFG "LED, BOUTON metre à jour" +#define S_QUANTITY "Quantité" +#define S_GPIO_SETTINGS_FOR_RELAYS "Paramètres du GPIO pour les relais" +#define S_RELAY "LE RELAIS" +#define S_RELAY_NR_SETTINGS "Paramètres de la relais No." +#define S_NO_RELAY_NR "Manquant numéro de relais" +#define S_STATE_CONTROL "Contrôle d'état" +#define S_REACTION_AFTER_RESET "Réaction après réinitialisation" +#define S_GPIO_SETTINGS_FOR_BUTTONS "Paramètres GPIO pour les boutons" +#define S_BUTTON "BOUTON" +#define S_BUTTON_NR_SETTINGS "Bouton mettre à jour" +#define S_NO_BUTTON_NR "Manquant numéro de bouton" +#define S_REACTION_TO "Réaction à" +#define S_GPIO_SETTINGS_FOR_LIMIT_SWITCH "Paramètres GPIO pour capteur de limite" +#define S_LIMIT_SWITCH "Capteur de limite" +#define S_GPIO_SETTINGS_FOR "GPIO mettre à jour pour" +#define S_FOUND "Trouvé" +#define S_NO_SENSORS_CONNECTED "Aucun capteur connecté" +#define S_SAVE_FOUND "Enregistrer trouvé" +#define S_TEMPERATURE "Température" +#define S_NAME "Nom" +#define S_ALTITUDE_ABOVE_SEA_LEVEL "Altitude" +#define S_GPIO_SETTINGS_FOR_CONFIG "Paramètres GPIO pour la à jour" +#define S_SOFTWARE_UPDATE "Mise à jour du logiciel" +#define S_DATA_SAVED "Données enregistrées" +#define S_RESTART_MODULE "Redémarrer le module" +#define S_DATA_ERASED_RESTART_DEVICE "Données effacées - redémarrer le module" +#define S_WRITE_ERROR_UNABLE_TO_READ_FILE_FS_PARTITION_MISSING "Erreur d'écriture - impossible de lire à partir du fichier FS. Partition manquante" +#define S_DATA_SAVED_RESTART_MODULE "Données enregistrées - redémarrer le module" +#define S_WRITE_ERROR_BAD_DATA "Erreur d'écriture - données incorrectes" + +//#### SuplaConfigESP.cpp #### +#define S_ALEREADY_INITIATED "Déjà lancé" +#define S_NOT_ASSIGNED_CB "CB non attribué" +#define S_INVALID_GUID_OR_DEVICE_REGISTRATION_INACTIVE "GUID non valide ou enregistrement de l'appareil INACTIF" +#define S_UNKNOWN_SEVER_ADDRESS "Adresse de serveur inconnue" +#define S_UNKNOWN_ID "ID inconnu" +#define S_INITIATED "Initié" +#define S_CHANNEL_LIMIT_EXCEEDED "Limite de canal dépassée" +#define S_DISCONNECTED "Déconnecté" +#define S_REGISRATION_IS_PENDING "L'enregistrement est en attente" +#define S_VARIABLE_ERROR "Erreur de variable" +#define S_PROTOCOL_VERSION_ERROR "Erreur de version de protocole" +#define S_BAD_CREDENTIALS "Informations d'identification incorrectes" +#define S_TEMPORARILY_UNAVAILABLE "Temporairement indisponible" +#define S_LOCATION_CONFLICT "Conflit d'emplacement" +#define S_CHANNEL_CONFLICT "Conflit de canal" +#define S_REGISTERED_AND_READY "Enregistré et prêt" +#define S_DEVICE_IS_DISCONNECTED "L'appareil est déconnecté" +#define S_LOCATION_IS_DISABLED "L'emplacement est désactivé" +#define S_DEVICE_LIMIT_EXCEEDED "Limite d'appareils dépassée" + +// #### SuplaCommonPROGMEM.h #### +#define S_OFF "ÉTEINDRE" +#define S_ON "ALLUMER" +#define S_LOW "BASSE" +#define S_HIGH "HAUT" +#define S_POSITION_MEMORY "MEMOIRE DE POSITION" +#define S_REACTION_ON_PRESS "ON PRESSE" +#define S_REACTION_ON_RELEASE "EN LIBÉRATION" +#define S_REACTION_ON_CHANGE "SUR LE CHANGEMENT" +#define S_CFG_10_PRESSES "10 FOIS SUR PRESSE" +#define S_5SEK_HOLD "5 SEC TENIR" + +// #### SuplaTemplateBoard.h #### +#define S_ABSENT "ABSENT" + +// #### SuplaWebPageSensor.cpp #### +#define S_IMPULSE_COUNTER "Compteur d'impulsions" +#define S_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT "Délai anti-rebond" +#define S_IMPULSE_COUNTER_RAISING_EDGE "Bord montant" +#define S_IMPULSE_COUNTER_PULL_UP "Remonter" +#define S_IMPULSE_COUNTER_CHANGE_VALUE "Modifier la valeur" +#define S_IMPULSE_COUNTER_SETTINGS_NR "Paramètres Compteur d'impulsions No." +#define S_NO_IMPULSE_COUNTER_NR "Aucun Compteur d'impulsions No." + + +#endif // _LANGUAGE_FR_S_H_ \ No newline at end of file diff --git a/lib/SuplaDevice/.drone.yml b/lib/SuplaDevice/.drone.yml new file mode 100644 index 00000000..688641c7 --- /dev/null +++ b/lib/SuplaDevice/.drone.yml @@ -0,0 +1,17 @@ +kind: pipeline +name: default + +steps: +- name: compilation + image: rikorose/gcc-cmake + commands: + - mkdir extras/test/build + - cd extras/test/build + - cmake .. + - make + +- name: test + image: rikorose/gcc-cmake + commands: + - cd extras/test/build + - ./supladevicetests diff --git a/lib/SuplaDevice/.gitignore b/lib/SuplaDevice/.gitignore new file mode 100644 index 00000000..dcd241ea --- /dev/null +++ b/lib/SuplaDevice/.gitignore @@ -0,0 +1 @@ +extras/test/build/ diff --git a/lib/SuplaDevice/.travis.yml b/lib/SuplaDevice/.travis.yml new file mode 100644 index 00000000..bd33cea0 --- /dev/null +++ b/lib/SuplaDevice/.travis.yml @@ -0,0 +1,18 @@ +os: linux +dist: xenial +sudo: false +language: cpp + +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-6 + +script: + - mkdir extras/test/build + - cd extras/test/build + - CXX=/usr/bin/g++-6 CC=/usr/bin/gcc-6 cmake .. + - make + - ./supladevicetests diff --git a/lib/SuplaDevice/LICENSE b/lib/SuplaDevice/LICENSE new file mode 100644 index 00000000..23cb7903 --- /dev/null +++ b/lib/SuplaDevice/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/lib/SuplaDevice/_clang-format b/lib/SuplaDevice/_clang-format new file mode 100644 index 00000000..5915a3e9 --- /dev/null +++ b/lib/SuplaDevice/_clang-format @@ -0,0 +1,91 @@ +--- +Language: Cpp +BasedOnStyle: Google +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignConsecutiveMacros: true +AlignEscapedNewlinesLeft: true +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: true +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: true +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +IncludeCategories: + - Regex: '^<.*\.h>' + Priority: 1 + - Regex: '^<.*' + Priority: 2 + - Regex: '.*' + Priority: 3 +IndentCaseLabels: true +IndentWidth: 2 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: false +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +TabWidth: 8 +UseTab: Never +... + diff --git a/lib/SuplaDevice/_config.yml b/lib/SuplaDevice/_config.yml new file mode 100644 index 00000000..c4192631 --- /dev/null +++ b/lib/SuplaDevice/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ No newline at end of file diff --git a/lib/SuplaDevice/examples/Afore/Afore.ino b/lib/SuplaDevice/examples/Afore/Afore.ino index eaace6d9..859636c9 100644 --- a/lib/SuplaDevice/examples/Afore/Afore.ino +++ b/lib/SuplaDevice/examples/Afore/Afore.ino @@ -14,28 +14,25 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include #include #include // Choose proper network interface for your card: -// Arduino Mega with EthernetShield W5100: -#include -// Ethernet MAC address -uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; -Supla::EthernetShield ethernet(mac); -// -// Arduino Mega with ENC28J60: -// #include -// Supla::ENC28J60 ethernet(mac); -// -// ESP8266 based board: -// #include -// Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); -// -// ESP32 based board: -// #include -// Supla::ESP32Wifi wifi("your_wifi_ssid", "your_wifi_password"); +#ifdef ARDUINO_ARCH_AVR + // Arduino Mega with EthernetShield W5100: + #include + // Ethernet MAC address + uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + Supla::EthernetShield ethernet(mac); + + // Arduino Mega with ENC28J60: + // #include + // Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESP8266 and ESP32 based board: + #include + Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif void setup() { diff --git a/lib/SuplaDevice/examples/DHT/DHT.ino b/lib/SuplaDevice/examples/DHT/DHT.ino index b91c0fea..e0c85e2a 100644 --- a/lib/SuplaDevice/examples/DHT/DHT.ino +++ b/lib/SuplaDevice/examples/DHT/DHT.ino @@ -14,28 +14,25 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include #include #include // Choose proper network interface for your card: -// Arduino Mega with EthernetShield W5100: -#include -// Ethernet MAC address -uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; -Supla::EthernetShield ethernet(mac); -// -// Arduino Mega with ENC28J60: -// #include -// Supla::ENC28J60 ethernet(mac); -// -// ESP8266 based board: -// #include -// Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); -// -// ESP32 based board: -// #include -// Supla::ESP32Wifi wifi("your_wifi_ssid", "your_wifi_password"); +#ifdef ARDUINO_ARCH_AVR + // Arduino Mega with EthernetShield W5100: + #include + // Ethernet MAC address + uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + Supla::EthernetShield ethernet(mac); + + // Arduino Mega with ENC28J60: + // #include + // Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESP8266 and ESP32 based board: + #include + Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif /* * This example requires DHT sensor library installed. @@ -49,7 +46,7 @@ Supla::EthernetShield ethernet(mac); void setup() { - Serial.begin(9600); + Serial.begin(115200); // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; diff --git a/lib/SuplaDevice/examples/DallasTemperature/DallasTemperature.ino b/lib/SuplaDevice/examples/DallasTemperature/DallasTemperature.ino index f10308df..81eb8fbc 100644 --- a/lib/SuplaDevice/examples/DallasTemperature/DallasTemperature.ino +++ b/lib/SuplaDevice/examples/DallasTemperature/DallasTemperature.ino @@ -14,7 +14,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include #include /* @@ -26,27 +25,25 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // Choose proper network interface for your card: -// Arduino Mega with EthernetShield W5100: -#include -// Ethernet MAC address -uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; -Supla::EthernetShield ethernet(mac); -// -// Arduino Mega with ENC28J60: -// #include -// Supla::ENC28J60 ethernet(mac); -// -// ESP8266 based board: -// #include -// Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); -// -// ESP32 based board: -// #include -// Supla::ESP32Wifi wifi("your_wifi_ssid", "your_wifi_password"); +#ifdef ARDUINO_ARCH_AVR + // Arduino Mega with EthernetShield W5100: + #include + // Ethernet MAC address + uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + Supla::EthernetShield ethernet(mac); + + // Arduino Mega with ENC28J60: + // #include + // Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESP8266 and ESP32 based board: + #include + Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif void setup() { - Serial.begin(9600); + Serial.begin(115200); // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; diff --git a/lib/SuplaDevice/examples/Fronius/Fronius.ino b/lib/SuplaDevice/examples/Fronius/Fronius.ino index 98b3b7ad..27cc0e75 100644 --- a/lib/SuplaDevice/examples/Fronius/Fronius.ino +++ b/lib/SuplaDevice/examples/Fronius/Fronius.ino @@ -14,28 +14,25 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include #include #include // Choose proper network interface for your card: -// Arduino Mega with EthernetShield W5100: -#include -// Ethernet MAC address -uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; -Supla::EthernetShield ethernet(mac); -// -// Arduino Mega with ENC28J60: -// #include -// Supla::ENC28J60 ethernet(mac); -// -// ESP8266 based board: -// #include -// Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); -// -// ESP32 based board: -// #include -// Supla::ESP32Wifi wifi("your_wifi_ssid", "your_wifi_password"); +#ifdef ARDUINO_ARCH_AVR + // Arduino Mega with EthernetShield W5100: + #include + // Ethernet MAC address + uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + Supla::EthernetShield ethernet(mac); + + // Arduino Mega with ENC28J60: + // #include + // Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESP8266 and ESP32 based board: + #include + Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif void setup() { diff --git a/lib/SuplaDevice/examples/GarageParkingAssistLight/GarageParkingAssistLight.ino b/lib/SuplaDevice/examples/GarageParkingAssistLight/GarageParkingAssistLight.ino new file mode 100644 index 00000000..76c4e2d8 --- /dev/null +++ b/lib/SuplaDevice/examples/GarageParkingAssistLight/GarageParkingAssistLight.ino @@ -0,0 +1,94 @@ +/* +Copyright (C) AC SOFTWARE SP. Z O.O. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +// Add include to HC_SR04 sensor +#include +#include +#include + +// Choose proper network interface for your card: +#ifdef ARDUINO_ARCH_AVR + // Arduino Mega with EthernetShield W5100: + #include + // Ethernet MAC address + uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + Supla::EthernetShield ethernet(mac); + + // Arduino Mega with ENC28J60: + // #include + // Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESP8266 and ESP32 based board: + #include + Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif + +void setup() { + + Serial.begin(115200); + + // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid + char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + // Replace the following AUTHKEY with value that you can retrieve from: https://www.supla.org/arduino/get-authkey + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + /* + * Having your device already registered at cloud.supla.org, + * you want to change CHANNEL sequence or remove any of them, + * then you must also remove the device itself from cloud.supla.org. + * Otherwise you will get "Channel conflict!" error. + */ + + + auto distance = new Supla::Sensor::HC_SR04(12,13);//(trigPin, echoPin) + + auto redLight = new Supla::Control::Relay(22); + auto orangeLight = new Supla::Control::Relay(23); + auto greenLight = new Supla::Control::Relay(24); + + distance->addAction(Supla::TURN_ON, redLight, OnLess(0.40)); + distance->addAction(Supla::TURN_ON, orangeLight, OnBetween(0.40, 0.80)); + distance->addAction(Supla::TURN_ON, greenLight, OnBetween(0.80, 1.50)); + + distance->addAction(Supla::TURN_OFF, orangeLight, OnLess(0.35)); + distance->addAction(Supla::TURN_OFF, greenLight, OnLess(0.75)); + + distance->addAction(Supla::TURN_OFF, redLight, OnGreater(0.45)); + distance->addAction(Supla::TURN_OFF, orangeLight, OnGreater(0.85)); + distance->addAction(Supla::TURN_OFF, greenLight, OnGreater(1.55)); + + /* + * SuplaDevice Initialization. + * Server address is available at https://cloud.supla.org + * If you do not have an account, you can create it at https://cloud.supla.org/account/create + * SUPLA and SUPLA CLOUD are free of charge + * + */ + + SuplaDevice.begin(GUID, // Global Unique Identifier + "svr1.supla.org", // SUPLA server address + "email@address", // Email address used to login to Supla Cloud + AUTHKEY); // Authorization key + +} + +void loop() { + SuplaDevice.iterate(); +} + diff --git a/lib/SuplaDevice/examples/HC_SR04_Distance_sensor/HC_SR04_Distance_sensor.ino b/lib/SuplaDevice/examples/HC_SR04_Distance_sensor/HC_SR04_Distance_sensor.ino index 2de1ac58..0bb45c39 100644 --- a/lib/SuplaDevice/examples/HC_SR04_Distance_sensor/HC_SR04_Distance_sensor.ino +++ b/lib/SuplaDevice/examples/HC_SR04_Distance_sensor/HC_SR04_Distance_sensor.ino @@ -14,7 +14,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include #include // Add include to HC_SR04 sensor @@ -22,27 +21,25 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // Choose proper network interface for your card: -// Arduino Mega with EthernetShield W5100: -#include -// Ethernet MAC address -uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; -Supla::EthernetShield ethernet(mac); -// -// Arduino Mega with ENC28J60: -// #include -// Supla::ENC28J60 ethernet(mac); -// -// ESP8266 based board: -// #include -// Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); -// -// ESP32 based board: -// #include -// Supla::ESP32Wifi wifi("your_wifi_ssid", "your_wifi_password"); +#ifdef ARDUINO_ARCH_AVR + // Arduino Mega with EthernetShield W5100: + #include + // Ethernet MAC address + uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + Supla::EthernetShield ethernet(mac); + + // Arduino Mega with ENC28J60: + // #include + // Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESP8266 and ESP32 based board: + #include + Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif void setup() { - Serial.begin(9600); + Serial.begin(115200); // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; @@ -63,7 +60,7 @@ void setup() { /* * SuplaDevice Initialization. - * Server address, LocationID and LocationPassword are available at https://cloud.supla.org + * Server address is available at https://cloud.supla.org * If you do not have an account, you can create it at https://cloud.supla.org/account/create * SUPLA and SUPLA CLOUD are free of charge * diff --git a/lib/SuplaDevice/examples/HC_SR04_Distance_sensor_extended/HC_SR04_Distance_sensor_extended.ino b/lib/SuplaDevice/examples/HC_SR04_Distance_sensor_extended/HC_SR04_Distance_sensor_extended.ino new file mode 100644 index 00000000..eb0d8503 --- /dev/null +++ b/lib/SuplaDevice/examples/HC_SR04_Distance_sensor_extended/HC_SR04_Distance_sensor_extended.ino @@ -0,0 +1,85 @@ +/* +Copyright (C) AC SOFTWARE SP. Z O.O. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +// Add include to HC_SR04 sensor +#include + +// Choose proper network interface for your card: +#ifdef ARDUINO_ARCH_AVR + // Arduino Mega with EthernetShield W5100: + #include + // Ethernet MAC address + uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + Supla::EthernetShield ethernet(mac); + + // Arduino Mega with ENC28J60: + // #include + // Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESP8266 and ESP32 based board: + #include + Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif + +int8_t trigPin = 13; +int8_t echoPin = 12; +int16_t minSensorRead = 5; //minimum sensor reading distance in centimeters +int16_t maxSensorRead = 55; //maximum sensor reading distance in centimeters +int16_t minAppReading = 50; //minimum distance shown by the App in centimeters when sensor read <= min_sensor_read +int16_t maxAppReading = 0; //maximum distance shown by the App in centimeters when sensor read >= max_sensor_read + +void setup() { + + Serial.begin(115200); + + // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid + char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + // Replace the following AUTHKEY with value that you can retrieve from: https://www.supla.org/arduino/get-authkey + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + /* + * Having your device already registered at cloud.supla.org, + * you want to change CHANNEL sequence or remove any of them, + * then you must also remove the device itself from cloud.supla.org. + * Otherwise you will get "Channel conflict!" error. + */ + + new Supla::Sensor::HC_SR04(trigPin,echoPin, minSensorRead, maxSensorRead, minAppReading, maxAppReading); + + // Supla::Sensor::HC_SR04(trigPin, echoPin) // sends sensor reading to cloud unchanged + + + /* + * SuplaDevice Initialization. + * Server address is available at https://cloud.supla.org + * If you do not have an account, you can create it at https://cloud.supla.org/account/create + * SUPLA and SUPLA CLOUD are free of charge + * + */ + + SuplaDevice.begin(GUID, // Global Unique Identifier + "svrX.supla.org", // SUPLA server address + "email@address", // Email address used to login to Supla Cloud + AUTHKEY); // Authorization key + +} + +void loop() { + SuplaDevice.iterate(); +} diff --git a/lib/SuplaDevice/examples/ImpulseCounter/ImpulseCounter.ino b/lib/SuplaDevice/examples/ImpulseCounter/ImpulseCounter.ino index 5035b970..e2949220 100644 --- a/lib/SuplaDevice/examples/ImpulseCounter/ImpulseCounter.ino +++ b/lib/SuplaDevice/examples/ImpulseCounter/ImpulseCounter.ino @@ -14,9 +14,11 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include #include #include +// Remove below line if you don't want to have internal LED blinking on each impulse +#include +#include // Choose where Supla should store counter data in persistant memory // We recommend to use external FRAM memory @@ -26,29 +28,26 @@ Supla::Eeprom eeprom(STORAGE_OFFSET); // #include // Supla::FramSpi fram(STORAGE_OFFSET); - // Choose proper network interface for your card: -// Arduino Mega with EthernetShield W5100: -#include -// Ethernet MAC address -uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; -Supla::EthernetShield ethernet(mac); -// -// Arduino Mega with ENC28J60: -// #include -// Supla::ENC28J60 ethernet(mac); -// -// ESP8266 based board: -// #include -// Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); -// -// ESP32 based board: -// #include -// Supla::ESP32Wifi wifi("your_wifi_ssid", "your_wifi_password"); +#ifdef ARDUINO_ARCH_AVR + // Arduino Mega with EthernetShield W5100: + #include + // Ethernet MAC address + uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + Supla::EthernetShield ethernet(mac); + + // Arduino Mega with ENC28J60: + // #include + // Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESP8266 and ESP32 based board: + #include + Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif void setup() { - Serial.begin(9600); + Serial.begin(115200); // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; @@ -64,11 +63,21 @@ void setup() { */ // CHANNEL0 - Impulse Counter on pin 34, counting raising edge (from LOW to HIGH), no pullup on pin, and 10 ms debounce timeout - new Supla::Sensor::ImpulseCounter(34, true, false, 10); + auto ic1 = new Supla::Sensor::ImpulseCounter(34, true, false, 10); // CHANNEL1 - Impulse Counter on pin 35, counting falling edge (from HIGH to LOW), with pullup on pin, and 50 ms debounce timeout - new Supla::Sensor::ImpulseCounter(35, false, true, 50); + auto ic2 = new Supla::Sensor::ImpulseCounter(35, false, true, 50); + // Configuring internal LED to notify each change of impulse counter + auto led1 = new Supla::Control::InternalPinOutput(24); // LED on pin 24 + auto led2 = new Supla::Control::InternalPinOutput(25); // LED on pin 25 + + // LED1 will blink (100 ms) on each change of ic1 + led1->setDurationMs(100); + ic1->addAction(Supla::TURN_ON, led1, Supla::ON_CHANGE); + + // LED2 will toggle it's state on each change of ic2 + ic2->addAction(Supla::TOGGLE, led2, Supla::ON_CHANGE); /* * Server address is available at https://cloud.supla.org diff --git a/lib/SuplaDevice/examples/Pzem_V_2/Pzem_V_2.ino b/lib/SuplaDevice/examples/Pzem_V_2/Pzem_V_2.ino index e03d4aa9..0b8c4918 100644 --- a/lib/SuplaDevice/examples/Pzem_V_2/Pzem_V_2.ino +++ b/lib/SuplaDevice/examples/Pzem_V_2/Pzem_V_2.ino @@ -13,24 +13,32 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - // this example will work only on esp8266 and esp32 boards. On Arduino mega it will not fly. + //dependence: Arduino communication library for Peacefair PZEM-004T Energy monitor https://github.com/olehs/PZEM004T -#include #include #include -// ESP8266 based board: -#include -Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); -// -// ESP32 based board: -// #include -// Supla::ESP32Wifi wifi("your_wifi_ssid", "your_wifi_password"); +// Choose proper network interface for your card: +#ifdef ARDUINO_ARCH_AVR + // Arduino Mega with EthernetShield W5100: + #include + // Ethernet MAC address + uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + Supla::EthernetShield ethernet(mac); + + // Arduino Mega with ENC28J60: + // #include + // Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESP8266 and ESP32 based board: + #include + Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif void setup() { - Serial.begin(9600); + Serial.begin(115200); // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; diff --git a/lib/SuplaDevice/examples/Pzem_V_3/Pzem_V_3.ino b/lib/SuplaDevice/examples/Pzem_V_3/Pzem_V_3.ino index fd435453..85fe0bb6 100644 --- a/lib/SuplaDevice/examples/Pzem_V_3/Pzem_V_3.ino +++ b/lib/SuplaDevice/examples/Pzem_V_3/Pzem_V_3.ino @@ -13,56 +13,66 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - // this example will work only on esp8266 and esp32 boards. On Arduino mega it will not fly. - //dependence: Arduino library for the Updated PZEM-004T v3.0 Power and Energy meter https://github.com/mandulaj/PZEM-004T-v30 -#include +// dependence: Arduino library for the Updated PZEM-004T v3.0 Power and Energy +// meter https://github.com/mandulaj/PZEM-004T-v30 + #include #include -// ESP8266 based board: +// Choose proper network interface for your card: +#ifdef ARDUINO_ARCH_AVR +// Arduino Mega with EthernetShield W5100: +#include +// Ethernet MAC address +uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; +Supla::EthernetShield ethernet(mac); + +// Arduino Mega with ENC28J60: +// #include +// Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) +// ESP8266 and ESP32 based board: #include Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); -// -// ESP32 based board: -// #include -// Supla::ESP32Wifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif void setup() { + Serial.begin(115200); - Serial.begin(9600); - - // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid - char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - - // Replace the following AUTHKEY with value that you can retrieve from: https://www.supla.org/arduino/get-authkey - char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + // Replace the falowing GUID with value that you can retrieve from + // https://www.supla.org/arduino/get-guid + char GUID[SUPLA_GUID_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - /* - * Having your device already registered at cloud.supla.org, - * you want to change CHANNEL sequence or remove any of them, - * then you must also remove the device itself from cloud.supla.org. - * Otherwise you will get "Channel conflict!" error. - */ - - new Supla::Sensor::PZEMv3(5, 4); // (RX,TX) + // Replace the following AUTHKEY with value that you can retrieve from: + // https://www.supla.org/arduino/get-authkey + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + /* + * Having your device already registered at cloud.supla.org, + * you want to change CHANNEL sequence or remove any of them, + * then you must also remove the device itself from cloud.supla.org. + * Otherwise you will get "Channel conflict!" error. + */ - /* - * SuplaDevice Initialization. - * Server address, is available at https://cloud.supla.org - * If you do not have an account, you can create it at https://cloud.supla.org/account/create - * SUPLA and SUPLA CLOUD are free of charge - * - */ + new Supla::Sensor::PZEMv3(5, 4); // (RX,TX) - SuplaDevice.begin(GUID, // Global Unique Identifier - "svr1.supla.org", // SUPLA server address - "email@address", // Email address used to login to Supla Cloud - AUTHKEY); // Authorization key + /* + * SuplaDevice Initialization. + * Server address, is available at https://cloud.supla.org + * If you do not have an account, you can create it at + * https://cloud.supla.org/account/create SUPLA and SUPLA CLOUD are free of + * charge + * + */ + SuplaDevice.begin( + GUID, // Global Unique Identifier + "svr1.supla.org", // SUPLA server address + "email@address", // Email address used to login to Supla Cloud + AUTHKEY); // Authorization key } void loop() { - SuplaDevice.iterate(); + SuplaDevice.iterate(); } diff --git a/lib/SuplaDevice/examples/RGBW/RGBW.ino b/lib/SuplaDevice/examples/RGBW/RGBW.ino index 994fd701..fb446e08 100644 --- a/lib/SuplaDevice/examples/RGBW/RGBW.ino +++ b/lib/SuplaDevice/examples/RGBW/RGBW.ino @@ -14,28 +14,25 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include #include #include // Choose proper network interface for your card: -// Arduino Mega with EthernetShield W5100: -#include -// Ethernet MAC address -uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; -Supla::EthernetShield ethernet(mac); -// -// Arduino Mega with ENC28J60: -// #include -// Supla::ENC28J60 ethernet(mac); -// -// ESP8266 based board: -// #include -// Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); -// -// ESP32 based board: -// #include -// Supla::ESP32Wifi wifi("your_wifi_ssid", "your_wifi_password"); +#ifdef ARDUINO_ARCH_AVR + // Arduino Mega with EthernetShield W5100: + #include + // Ethernet MAC address + uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + Supla::EthernetShield ethernet(mac); + + // Arduino Mega with ENC28J60: + // #include + // Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESP8266 and ESP32 based board: + #include + Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif /* * Youtube: https://youtu.be/FE9tqzTjmA4 @@ -83,7 +80,7 @@ class RgbwLeds : public Supla::Control::RGBWBase { }; void setup() { - Serial.begin(9600); + Serial.begin(115200); // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; diff --git a/lib/SuplaDevice/examples/RollerShutter/RollerShutter.ino b/lib/SuplaDevice/examples/RollerShutter/RollerShutter.ino index 9747b4e6..777b47bd 100644 --- a/lib/SuplaDevice/examples/RollerShutter/RollerShutter.ino +++ b/lib/SuplaDevice/examples/RollerShutter/RollerShutter.ino @@ -14,10 +14,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include #include #include #include +#include // Choose where Supla should store roller shutter data in persistant memory // We recommend to use external FRAM memory @@ -27,30 +27,26 @@ Supla::Eeprom eeprom(STORAGE_OFFSET); // #include // Supla::FramSpi fram(STORAGE_OFFSET); - // Choose proper network interface for your card: -// Arduino Mega with EthernetShield W5100: -#include -// Ethernet MAC address -uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; -Supla::EthernetShield ethernet(mac); -// -// Arduino Mega with ENC28J60: -// #include -// Supla::ENC28J60 ethernet(mac); -// -// ESP8266 based board: -// #include -// Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); -// -// ESP32 based board: -// #include -// Supla::ESP32Wifi wifi("your_wifi_ssid", "your_wifi_password"); +#ifdef ARDUINO_ARCH_AVR + // Arduino Mega with EthernetShield W5100: + #include + // Ethernet MAC address + uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + Supla::EthernetShield ethernet(mac); + // Arduino Mega with ENC28J60: + // #include + // Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESP8266 and ESP32 based board: + #include + Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif void setup() { - Serial.begin(9600); + Serial.begin(115200); // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; @@ -69,6 +65,12 @@ void setup() { Supla::Control::Button *buttonOpen = new Supla::Control::Button(28, true, true); Supla::Control::Button *buttonClose = new Supla::Control::Button(29, true, true); + // Add two LEDs to inform about roller shutter relay status + // If inverted value is required, please add third parameter with true value + new Supla::Control::PinStatusLed(30, 24); // pin 30 status to be informed on pin 24 + new Supla::Control::PinStatusLed(31, 25); // pin 31 status to be informed on pin 25 + + buttonOpen->addAction(Supla::OPEN_OR_STOP, *rs, Supla::ON_PRESS); buttonClose->addAction(Supla::CLOSE_OR_STOP, *rs, Supla::ON_PRESS); diff --git a/lib/SuplaDevice/examples/SequenceButton/SequenceButton.ino b/lib/SuplaDevice/examples/SequenceButton/SequenceButton.ino new file mode 100644 index 00000000..75f81134 --- /dev/null +++ b/lib/SuplaDevice/examples/SequenceButton/SequenceButton.ino @@ -0,0 +1,89 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include + +// Choose proper network interface for your card: +#ifdef ARDUINO_ARCH_AVR + // Arduino Mega with EthernetShield W5100: + #include + // Ethernet MAC address + uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + Supla::EthernetShield ethernet(mac); + + // Arduino Mega with ENC28J60: + // #include + // Supla::ENC28J60 ethernet(mac); +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESP8266 and ESP32 based board: + #include + Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +#endif + + +void setup() { + + Serial.begin(115200); + + // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid + char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + // Replace the following AUTHKEY with value that you can retrieve from: https://www.supla.org/arduino/get-authkey + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + /* + * Having your device already registered at cloud.supla.org, + * you want to change CHANNEL sequence or remove any of them, + * then you must also remove the device itself from cloud.supla.org. + * Otherwise you will get "Channel conflict!" error. + */ + + auto secretRelay = new Supla::Control::Relay(30, false); // Low level trigger relay on pin 30 + auto alarmRelay = new Supla::Control::Relay(31, false); // Low level trigger relay on pin 31 + auto seqButton = new Supla::Control::SequenceButton(28, true, true); // Button on pin 28 with internal pullUp + // and LOW is considered as "pressed" state + + // Sequence of lenghts [ms] of button being presset, released, pressed, released, etc. + // Aplication will print on Serial recorded sequence, so use it to record your rhythm and put it here + uint16_t sequence[30] = {90, 590, 90, 140, 90, 290, 90, 230, 90, 140, 90}; + + seqButton->setSequence(sequence); + seqButton->setMargin(0.5); // we allow +- 50% deviation of state length compared to matching sequence + + // Button will trigger secretRelay when correct rhythm will be detected or alarmRelay otherwise + seqButton->addAction(Supla::TURN_ON, secretRelay, Supla::ON_SEQUENCE_MATCH); + seqButton->addAction(Supla::TURN_ON, alarmRelay, Supla::ON_SEQUENCE_DOESNT_MATCH); + + /* + * SuplaDevice Initialization. + * Server address is available at https://cloud.supla.org + * If you do not have an account, you can create it at https://cloud.supla.org/account/create + * SUPLA and SUPLA CLOUD are free of charge + * + */ + + SuplaDevice.begin(GUID, // Global Unique Identifier + "svr1.supla.org", // SUPLA server address + "email@address", // Email address used to login to Supla Cloud + AUTHKEY); // Authorization key + +} + +void loop() { + SuplaDevice.iterate(); +} diff --git a/lib/SuplaDevice/extras/test/CMakeLists.txt b/lib/SuplaDevice/extras/test/CMakeLists.txt new file mode 100644 index 00000000..107bf4d8 --- /dev/null +++ b/lib/SuplaDevice/extras/test/CMakeLists.txt @@ -0,0 +1,60 @@ +cmake_minimum_required(VERSION 3.11) + +project(supladevice) + +enable_testing() + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +set(CMAKE_BUILD_TYPE Debug) + +include_directories(../../src) +include_directories(doubles) + +add_subdirectory(../../src/ build) + +mark_as_advanced( +BUILD_GMOCK +BUILD_GTEST +) + +include(FetchContent) + +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.10.0 + ) + +FetchContent_GetProperties(googletest) +if(NOT googletest_POPULATED) + FetchContent_Populate(googletest) + add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR}) +endif() + +file(GLOB TEST_SRC + UptimeTests/*.cpp + ChannelTests/*cpp + IoTests/*.cpp + ElementTests/*.cpp + LocalActionTests/*.cpp + SensorTests/*.cpp + ChannelElementTests/*.cpp + InternalPinOutputTests/*.cpp + PinStatusLedTests/*.cpp + ConditionTests/*.cpp + ) + +file(GLOB DOUBLE_SRC doubles/*.cpp) + +add_executable(supladevicetests ${TEST_SRC} ${DOUBLE_SRC}) + +target_link_libraries(supladevicetests + gmock + gtest + gtest_main + supladevicelib + ) + +add_test(NAME supladevicetests + COMMAND supladevicetests) diff --git a/lib/SuplaDevice/extras/test/ChannelElementTests/channel_element_tests.cpp b/lib/SuplaDevice/extras/test/ChannelElementTests/channel_element_tests.cpp new file mode 100644 index 00000000..19e731df --- /dev/null +++ b/lib/SuplaDevice/extras/test/ChannelElementTests/channel_element_tests.cpp @@ -0,0 +1,81 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include + + +class ActionHandlerMock : public Supla::ActionHandler { + public: + MOCK_METHOD(void, handleAction, (int, int), (override)); +}; + + +TEST(ChannelElementTests, ActionDelegationToChannel) { + ASSERT_EQ(Supla::LocalAction::getClientListPtr(), nullptr); + Supla::ChannelElement element; + + ActionHandlerMock mock1; + ActionHandlerMock mock2; + + int action1 = 11; + + EXPECT_CALL(mock1, handleAction(Supla::ON_TURN_ON, action1)).Times(2); + EXPECT_CALL(mock2, handleAction(Supla::ON_TURN_OFF, action1)).Times(2); + + element.addAction(action1, mock1, Supla::ON_TURN_ON); + element.addAction(action1, &mock2, Supla::ON_TURN_OFF); + + element.getChannel()->setNewValue(false); + element.getChannel()->setNewValue(true); + element.getChannel()->setNewValue(true); + + element.getChannel()->setNewValue(false); + element.getChannel()->setNewValue(true); + element.getChannel()->setNewValue(false); + element.getChannel()->setNewValue(false); +} + +TEST(ChannelElementTests, ActionDelegationToConditions) { + ASSERT_EQ(Supla::LocalAction::getClientListPtr(), nullptr); + + ActionHandlerMock mock1; + ActionHandlerMock mock2; + + int action1 = 11; + + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)).Times(1); + EXPECT_CALL(mock2, handleAction(Supla::ON_CHANGE, action1)).Times(1); + + Supla::ChannelElement element; + auto channel = element.getChannel(); + channel->setType(SUPLA_CHANNELTYPE_DISTANCESENSOR); + + element.addAction(action1, mock1, OnLess(50)); + element.addAction(action1, &mock2, OnLess(30)); + + channel->setNewValue(60); + channel->setNewValue(50); + channel->setNewValue(45); + channel->setNewValue(25); +} + + diff --git a/lib/SuplaDevice/extras/test/ChannelTests/channel_extended_tests.cpp b/lib/SuplaDevice/extras/test/ChannelTests/channel_extended_tests.cpp new file mode 100644 index 00000000..57296822 --- /dev/null +++ b/lib/SuplaDevice/extras/test/ChannelTests/channel_extended_tests.cpp @@ -0,0 +1,128 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include +#include +#include +#include +#include +#include + + +class ActionHandlerMock : public Supla::ActionHandler { + public: + MOCK_METHOD(void, handleAction, (int, int), (override)); +}; + + +using ::testing::_; +using ::testing::ElementsAreArray; +using ::testing::Args; +using ::testing::ElementsAre; + + +TEST(ChannelExtendedTests, ExtendedChannelMethods) { + Supla::ChannelExtended extChannel; + + EXPECT_TRUE(extChannel.isExtended()); + EXPECT_NE(nullptr, extChannel.getExtValue() ); + +} + +TEST(ChannelExtendedTests, SetNewValueOnExtChannel) { + int number = 0; + Supla::ChannelExtended extChannel; + TElectricityMeter_ExtendedValue_V2 emVal = {}; + TElectricityMeter_Value expectedValue = {}; + + emVal.m_count = 1; + emVal.measured_values |= EM_VAR_FORWARD_ACTIVE_ENERGY; + emVal.total_forward_active_energy[0] = 1000; + emVal.total_forward_active_energy[1] = 2000; + emVal.total_forward_active_energy[2] = 4000; + + expectedValue.total_forward_active_energy = (1000 + 2000 + 4000) / 1000; + + extChannel.setNewValue(emVal); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &expectedValue, sizeof(expectedValue))); + EXPECT_TRUE(extChannel.isUpdateReady()); + extChannel.clearUpdateReady(); + + emVal.measured_values |= EM_VAR_VOLTAGE; + emVal.m[0].voltage[0] = 10; + emVal.m[0].voltage[1] = 0; + emVal.m[0].voltage[2] = 0; + + expectedValue.flags = 0; + expectedValue.flags |= EM_VALUE_FLAG_PHASE1_ON; + + extChannel.setNewValue(emVal); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &expectedValue, sizeof(expectedValue))); + EXPECT_TRUE(extChannel.isUpdateReady()); + extChannel.clearUpdateReady(); + + emVal.m[0].voltage[0] = 0; + emVal.m[0].voltage[1] = 20; + emVal.m[0].voltage[2] = 0; + + expectedValue.flags = 0; + expectedValue.flags |= EM_VALUE_FLAG_PHASE2_ON; + + extChannel.setNewValue(emVal); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &expectedValue, sizeof(expectedValue))); + EXPECT_TRUE(extChannel.isUpdateReady()); + extChannel.clearUpdateReady(); + + + emVal.m[0].voltage[0] = 0; + emVal.m[0].voltage[1] = 0; + emVal.m[0].voltage[2] = 300; + + expectedValue.flags = 0; + expectedValue.flags |= EM_VALUE_FLAG_PHASE3_ON; + + extChannel.setNewValue(emVal); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &expectedValue, sizeof(expectedValue))); + EXPECT_TRUE(extChannel.isUpdateReady()); + extChannel.clearUpdateReady(); + + + emVal.m[0].voltage[0] = 10; + emVal.m[0].voltage[1] = 0; + emVal.m[0].voltage[2] = 540; + + expectedValue.flags = 0; + expectedValue.flags |= EM_VALUE_FLAG_PHASE1_ON | EM_VALUE_FLAG_PHASE3_ON; + + extChannel.setNewValue(emVal); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &expectedValue, sizeof(expectedValue))); + EXPECT_TRUE(extChannel.isUpdateReady()); + extChannel.clearUpdateReady(); + + emVal.m[0].voltage[0] = 10; + emVal.m[0].voltage[1] = 230; + emVal.m[0].voltage[2] = 540; + + expectedValue.flags = 0; + expectedValue.flags |= EM_VALUE_FLAG_PHASE1_ON | EM_VALUE_FLAG_PHASE3_ON | EM_VALUE_FLAG_PHASE2_ON; + + extChannel.setNewValue(emVal); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &expectedValue, sizeof(expectedValue))); + EXPECT_TRUE(extChannel.isUpdateReady()); + extChannel.clearUpdateReady(); +} diff --git a/lib/SuplaDevice/extras/test/ChannelTests/channel_tests.cpp b/lib/SuplaDevice/extras/test/ChannelTests/channel_tests.cpp new file mode 100644 index 00000000..09008e7a --- /dev/null +++ b/lib/SuplaDevice/extras/test/ChannelTests/channel_tests.cpp @@ -0,0 +1,483 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include +#include +#include +#include +#include + + +class ActionHandlerMock : public Supla::ActionHandler { + public: + MOCK_METHOD(void, handleAction, (int, int), (override)); +}; + + +using ::testing::_; +using ::testing::ElementsAreArray; +using ::testing::Args; +using ::testing::ElementsAre; + +TEST(ChannelTests, ChannelMethods) { + Supla::Channel first; + Supla::Channel second; + + EXPECT_EQ(first.getChannelNumber(), 0); + EXPECT_EQ(second.getChannelNumber(), 1); + + EXPECT_EQ(first.isExtended(), false); + EXPECT_EQ(first.isUpdateReady(), false); + EXPECT_EQ(first.getChannelType(), 0); + EXPECT_EQ(first.getExtValue(), nullptr); + + int number = first.getChannelNumber(); + char emptyArray[SUPLA_CHANNELVALUE_SIZE] = {}; + EXPECT_EQ(number, Supla::Channel::reg_dev.channels[number].Number); + EXPECT_EQ(Supla::Channel::reg_dev.channels[number].Type, 0); + EXPECT_EQ(Supla::Channel::reg_dev.channels[number].FuncList, 0); + EXPECT_EQ(Supla::Channel::reg_dev.channels[number].Default, 0); + EXPECT_EQ(Supla::Channel::reg_dev.channels[number].Flags, SUPLA_CHANNEL_FLAG_CHANNELSTATE); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, emptyArray, SUPLA_CHANNELVALUE_SIZE)); + + first.setType(10); + EXPECT_EQ(first.getChannelType(), 10); + EXPECT_EQ(Supla::Channel::reg_dev.channels[number].Type, 10); + + first.setDefault(14); + EXPECT_EQ(Supla::Channel::reg_dev.channels[number].Default, 14); + + first.setFlag(2); + EXPECT_EQ(Supla::Channel::reg_dev.channels[number].Flags, SUPLA_CHANNEL_FLAG_CHANNELSTATE | 2); + + first.setFlag(4); + EXPECT_EQ(Supla::Channel::reg_dev.channels[number].Flags, SUPLA_CHANNEL_FLAG_CHANNELSTATE | 2 | 4); + + first.unsetFlag(2); + EXPECT_EQ(Supla::Channel::reg_dev.channels[number].Flags, SUPLA_CHANNEL_FLAG_CHANNELSTATE | 4); + + first.unsetFlag(SUPLA_CHANNEL_FLAG_CHANNELSTATE); + EXPECT_EQ(Supla::Channel::reg_dev.channels[number].Flags, 4); + + first.setFuncList(11); + EXPECT_EQ(Supla::Channel::reg_dev.channels[number].FuncList, 11); + +} + +TEST(ChannelTests, SetNewValue) { + Supla::Channel channel; + int number = channel.getChannelNumber(); + char emptyArray[SUPLA_CHANNELVALUE_SIZE] = {}; + + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, emptyArray, SUPLA_CHANNELVALUE_SIZE)); + EXPECT_FALSE(channel.isUpdateReady()); + + char array[SUPLA_CHANNELVALUE_SIZE] = {0, 1, 2, 3, 4, 5, 6, 7}; + channel.setNewValue(array); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, array, SUPLA_CHANNELVALUE_SIZE)); + EXPECT_TRUE(channel.isUpdateReady()); + + channel.clearUpdateReady(); + EXPECT_FALSE(channel.isUpdateReady()); + + channel.setNewValue(array); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, array, SUPLA_CHANNELVALUE_SIZE)); + EXPECT_FALSE(channel.isUpdateReady()); + + array[4] = 15; + channel.setNewValue(array); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, array, SUPLA_CHANNELVALUE_SIZE)); + EXPECT_TRUE(channel.isUpdateReady()); + + ASSERT_EQ(sizeof(double), 8); + double temp = 3.1415; + channel.setNewValue(temp); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &temp, SUPLA_CHANNELVALUE_SIZE)); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); + + char arrayBool[SUPLA_CHANNELVALUE_SIZE] = {}; + arrayBool[0] = true; + channel.setNewValue(true); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, arrayBool, SUPLA_CHANNELVALUE_SIZE)); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); + + channel.setNewValue(false); + arrayBool[0] = false; + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, arrayBool, SUPLA_CHANNELVALUE_SIZE)); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); + + int value = 1234; + ASSERT_EQ(sizeof(int), 4); + channel.setNewValue(value); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &value, sizeof(int))); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); + + _supla_int64_t value64 = 124346; + ASSERT_EQ(sizeof(value64), 8); + channel.setNewValue(value64); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &value64, sizeof(value64))); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); + + double humi = 95.2234123; + temp = 23.443322; + + int expectedTemp = temp * 1000; + int expectedHumi = humi * 1000; + + channel.setNewValue(temp, humi); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &expectedTemp, sizeof(expectedTemp))); + EXPECT_TRUE(0 == memcmp(&(Supla::Channel::reg_dev.channels[number].value[4]), &expectedHumi, sizeof(expectedHumi))); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); + + // RGBW channel setting + channel.setNewValue(1, 2, 3, 4, 5); + char rgbwArray[SUPLA_CHANNELVALUE_SIZE] = {5, 4, 3, 2, 1, 0, 0, 0}; + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, rgbwArray, SUPLA_CHANNELVALUE_SIZE)); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); + + TElectricityMeter_ExtendedValue_V2 emVal = {}; + TElectricityMeter_Value expectedValue = {}; + + emVal.m_count = 1; + emVal.measured_values |= EM_VAR_FORWARD_ACTIVE_ENERGY; + emVal.total_forward_active_energy[0] = 1000; + emVal.total_forward_active_energy[1] = 2000; + emVal.total_forward_active_energy[2] = 4000; + + expectedValue.total_forward_active_energy = (1000 + 2000 + 4000) / 1000; + + channel.setNewValue(emVal); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &expectedValue, sizeof(expectedValue))); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); + + emVal.measured_values |= EM_VAR_VOLTAGE; + emVal.m[0].voltage[0] = 10; + emVal.m[0].voltage[1] = 0; + emVal.m[0].voltage[2] = 0; + + expectedValue.flags = 0; + expectedValue.flags |= EM_VALUE_FLAG_PHASE1_ON; + + channel.setNewValue(emVal); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &expectedValue, sizeof(expectedValue))); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); + + emVal.m[0].voltage[0] = 0; + emVal.m[0].voltage[1] = 20; + emVal.m[0].voltage[2] = 0; + + expectedValue.flags = 0; + expectedValue.flags |= EM_VALUE_FLAG_PHASE2_ON; + + channel.setNewValue(emVal); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &expectedValue, sizeof(expectedValue))); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); + + + emVal.m[0].voltage[0] = 0; + emVal.m[0].voltage[1] = 0; + emVal.m[0].voltage[2] = 300; + + expectedValue.flags = 0; + expectedValue.flags |= EM_VALUE_FLAG_PHASE3_ON; + + channel.setNewValue(emVal); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &expectedValue, sizeof(expectedValue))); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); + + + emVal.m[0].voltage[0] = 10; + emVal.m[0].voltage[1] = 0; + emVal.m[0].voltage[2] = 540; + + expectedValue.flags = 0; + expectedValue.flags |= EM_VALUE_FLAG_PHASE1_ON | EM_VALUE_FLAG_PHASE3_ON; + + channel.setNewValue(emVal); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &expectedValue, sizeof(expectedValue))); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); + + emVal.m[0].voltage[0] = 10; + emVal.m[0].voltage[1] = 230; + emVal.m[0].voltage[2] = 540; + + expectedValue.flags = 0; + expectedValue.flags |= EM_VALUE_FLAG_PHASE1_ON | EM_VALUE_FLAG_PHASE3_ON | EM_VALUE_FLAG_PHASE2_ON; + + channel.setNewValue(emVal); + EXPECT_TRUE(0 == memcmp(Supla::Channel::reg_dev.channels[number].value, &expectedValue, sizeof(expectedValue))); + EXPECT_TRUE(channel.isUpdateReady()); + channel.clearUpdateReady(); +} + +TEST(ChannelTests, ChannelValueGetters) { + Supla::Channel channel; + + EXPECT_DOUBLE_EQ(channel.getValueDouble(), 0); + + double pi = 3.1415; + channel.setNewValue(pi); + EXPECT_DOUBLE_EQ(channel.getValueDouble(), pi); + + double e = 2.71828; + channel.setNewValue(pi, e); + EXPECT_NEAR(channel.getValueDoubleFirst(), pi, 0.001); + EXPECT_NEAR(channel.getValueDoubleSecond(), e, 0.001); + + int valueInt = 2021; + channel.setNewValue(valueInt); + EXPECT_EQ(channel.getValueInt32(), valueInt); + + _supla_int64_t valueInt64 = 202013012021000; + channel.setNewValue(valueInt64); + EXPECT_EQ(channel.getValueInt64(), valueInt64); + + channel.setNewValue(true); + EXPECT_TRUE(channel.getValueBool()); + + channel.setNewValue(false); + EXPECT_FALSE(channel.getValueBool()); + + uint8_t red = 10, green = 20, blue = 30, colorBright = 50, bright = 90; + channel.setNewValue(red, green, blue, colorBright, bright); + EXPECT_EQ(channel.getValueRed(), red); + EXPECT_EQ(channel.getValueGreen(), green); + EXPECT_EQ(channel.getValueBlue(), blue); + EXPECT_EQ(channel.getValueColorBrightness(), colorBright); + EXPECT_EQ(channel.getValueBrightness(), bright); +} + +TEST(ChannelTests, SendUpdateTest) { + Supla::Channel channel; + ::testing::InSequence seq; + SrpcMock srpc; + + const char emptyArray[SUPLA_CHANNELVALUE_SIZE] = {}; + char array[SUPLA_CHANNELVALUE_SIZE] = {}; + array[0] = 1; + + EXPECT_CALL(srpc, valueChanged(nullptr, 0, ElementsAreArray(emptyArray))); + EXPECT_CALL(srpc, valueChanged(nullptr, 0, ElementsAreArray(array))); + + EXPECT_FALSE(channel.isUpdateReady()); + channel.sendUpdate(nullptr); + channel.setNewValue(true); + EXPECT_TRUE(channel.isUpdateReady()); + channel.sendUpdate(nullptr); + EXPECT_FALSE(channel.isUpdateReady()); +} + +TEST(ChannelTests, BoolChannelWithLocalActions) { + Supla::Channel ch1; + + ::testing::InSequence seq; + ActionHandlerMock mock1; + ActionHandlerMock mock2; + SrpcMock srpc; + + int action1 = 11; + int action2 = 12; + int action3 = 13; + + EXPECT_CALL(mock1, handleAction(Supla::ON_TURN_ON, action1)); + EXPECT_CALL(mock1, handleAction(Supla::ON_TURN_OFF, action2)); + EXPECT_CALL(mock1, handleAction(Supla::ON_TURN_ON, action1)); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action3)); + EXPECT_CALL(mock2, handleAction).Times(0); + + + ch1.addAction(action1, mock1, Supla::ON_TURN_ON); + ch1.addAction(action2, mock1, Supla::ON_TURN_OFF); + + ch1.setNewValue(true); + ch1.setNewValue(false); + ch1.setNewValue(false); // nothing should be called on mocks + ch1.setNewValue(false); // nothing should be called on mocks + + ch1.addAction(action3, mock1, Supla::ON_CHANGE); + ch1.setNewValue(true); + ch1.setNewValue(true); // nothing should be called on mocks + ch1.setNewValue(true); // nothing should be called on mocks + +} + +TEST(ChannelTests, Int32ChannelWithLocalActions) { + Supla::Channel ch1; + + ::testing::InSequence seq; + ActionHandlerMock mock1; + ActionHandlerMock mock2; + SrpcMock srpc; + + int action1 = 11; + int action2 = 12; + int action3 = 13; + + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock2, handleAction).Times(0); + + ch1.addAction(action1, mock1, Supla::ON_CHANGE); + + _supla_int_t value = 15; + + ch1.setNewValue(value); + ch1.setNewValue(value); + + value++; + ch1.setNewValue(value); + + value++; + ch1.setNewValue(value); + ch1.setNewValue(value); + ch1.setNewValue(value); +} + +TEST(ChannelTests, Int64ChannelWithLocalActions) { + Supla::Channel ch1; + + ::testing::InSequence seq; + ActionHandlerMock mock1; + ActionHandlerMock mock2; + SrpcMock srpc; + + int action1 = 11; + int action2 = 12; + int action3 = 13; + + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock2, handleAction).Times(0); + + ch1.addAction(action1, mock1, Supla::ON_CHANGE); + + _supla_int64_t value = 15; + + ch1.setNewValue(value); + ch1.setNewValue(value); + + value++; + ch1.setNewValue(value); + + value++; + ch1.setNewValue(value); + ch1.setNewValue(value); + ch1.setNewValue(value); +} + +TEST(ChannelTests, DoubleChannelWithLocalActions) { + Supla::Channel ch1; + + ::testing::InSequence seq; + ActionHandlerMock mock1; + ActionHandlerMock mock2; + SrpcMock srpc; + + int action1 = 11; + + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock2, handleAction).Times(0); + + ch1.addAction(action1, mock1, Supla::ON_CHANGE); + + double value = 3.1415; + + ch1.setNewValue(value); + ch1.setNewValue(value); + + value += 1.2; + ch1.setNewValue(value); + + value += 1.2; + ch1.setNewValue(value); + ch1.setNewValue(value); + ch1.setNewValue(value); +} + +TEST(ChannelTests, DoubleFloatChannelWithLocalActions) { + Supla::Channel ch1; + + ::testing::InSequence seq; + ActionHandlerMock mock1; + ActionHandlerMock mock2; + SrpcMock srpc; + + int action1 = 11; + + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock2, handleAction).Times(0); + + ch1.addAction(action1, mock1, Supla::ON_CHANGE); + + float value1 = 3.1415; + float value2 = 2.5; + + ch1.setNewValue(value1, value2); + ch1.setNewValue(value1, value2); + + value1 += 1.2; + ch1.setNewValue(value1, value2); + + value2 += 1.2; + ch1.setNewValue(value1, value2); + ch1.setNewValue(value1, value2); + ch1.setNewValue(value1, value2); +} + +TEST(ChannelTests, RgbwChannelWithLocalActions) { + Supla::Channel ch1; + + ::testing::InSequence seq; + ActionHandlerMock mock1; + ActionHandlerMock mock2; + SrpcMock srpc; + + int action1 = 11; + + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock1, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(mock2, handleAction).Times(0); + + ch1.addAction(action1, mock1, Supla::ON_CHANGE); + + ch1.setNewValue(10, 20, 30, 90, 80); + ch1.setNewValue(10, 20, 30, 90, 80); + + ch1.setNewValue(10, 21, 30, 90, 80); + ch1.setNewValue(10, 20, 30, 90, 81); + ch1.setNewValue(10, 20, 30, 90, 81); +} diff --git a/lib/SuplaDevice/extras/test/ConditionTests/on_between_eq_tests.cpp b/lib/SuplaDevice/extras/test/ConditionTests/on_between_eq_tests.cpp new file mode 100644 index 00000000..f3287a2b --- /dev/null +++ b/lib/SuplaDevice/extras/test/ConditionTests/on_between_eq_tests.cpp @@ -0,0 +1,43 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include + +TEST(OnBetweenEqTests, OnBetweenEqConditionTests) { + auto cond = OnBetweenEq(20, 30); + + EXPECT_FALSE(cond->checkConditionFor(5)); + EXPECT_FALSE(cond->checkConditionFor(15)); + + EXPECT_TRUE(cond->checkConditionFor(20)); + EXPECT_FALSE(cond->checkConditionFor(20.001)); + EXPECT_FALSE(cond->checkConditionFor(25)); + + EXPECT_FALSE(cond->checkConditionFor(5)); + EXPECT_FALSE(cond->checkConditionFor(5)); + EXPECT_FALSE(cond->checkConditionFor(5)); + + EXPECT_FALSE(cond->checkConditionFor(50)); + EXPECT_TRUE(cond->checkConditionFor(30)); + EXPECT_FALSE(cond->checkConditionFor(5)); + + EXPECT_TRUE(cond->checkConditionFor(24)); +} + + + diff --git a/lib/SuplaDevice/extras/test/ConditionTests/on_between_tests.cpp b/lib/SuplaDevice/extras/test/ConditionTests/on_between_tests.cpp new file mode 100644 index 00000000..63314db8 --- /dev/null +++ b/lib/SuplaDevice/extras/test/ConditionTests/on_between_tests.cpp @@ -0,0 +1,41 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include + +TEST(OnBetweenTests, OnBetweenConditionTests) { + auto cond = OnBetween(20, 30); + + EXPECT_FALSE(cond->checkConditionFor(5)); + EXPECT_FALSE(cond->checkConditionFor(15)); + + EXPECT_FALSE(cond->checkConditionFor(20)); + EXPECT_TRUE(cond->checkConditionFor(20.001)); + EXPECT_FALSE(cond->checkConditionFor(25)); + + EXPECT_FALSE(cond->checkConditionFor(5)); + EXPECT_FALSE(cond->checkConditionFor(5)); + EXPECT_FALSE(cond->checkConditionFor(5)); + + EXPECT_FALSE(cond->checkConditionFor(50)); + EXPECT_TRUE(cond->checkConditionFor(29)); + EXPECT_FALSE(cond->checkConditionFor(5)); + +} + + diff --git a/lib/SuplaDevice/extras/test/ConditionTests/on_equal_tests.cpp b/lib/SuplaDevice/extras/test/ConditionTests/on_equal_tests.cpp new file mode 100644 index 00000000..c48cc0ed --- /dev/null +++ b/lib/SuplaDevice/extras/test/ConditionTests/on_equal_tests.cpp @@ -0,0 +1,39 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include + +TEST(OnEqualTests, OnEqualConditionTests) { + auto cond = OnEqual(3.1415); + + EXPECT_FALSE(cond->checkConditionFor(5)); + EXPECT_FALSE(cond->checkConditionFor(15)); + + EXPECT_TRUE(cond->checkConditionFor(3.1415)); + EXPECT_FALSE(cond->checkConditionFor(20.001)); + EXPECT_FALSE(cond->checkConditionFor(25)); + + EXPECT_FALSE(cond->checkConditionFor(5)); + EXPECT_FALSE(cond->checkConditionFor(5)); + EXPECT_FALSE(cond->checkConditionFor(5)); + + EXPECT_FALSE(cond->checkConditionFor(50)); + EXPECT_TRUE(cond->checkConditionFor(3.1415)); + EXPECT_FALSE(cond->checkConditionFor(5)); + +} diff --git a/lib/SuplaDevice/extras/test/ConditionTests/on_greater_eq_tests.cpp b/lib/SuplaDevice/extras/test/ConditionTests/on_greater_eq_tests.cpp new file mode 100644 index 00000000..5022c061 --- /dev/null +++ b/lib/SuplaDevice/extras/test/ConditionTests/on_greater_eq_tests.cpp @@ -0,0 +1,39 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include + +TEST(OnGreaterEqTests, OnGreaterEqConditionTests) { + auto cond = OnGreaterEq(20); + + EXPECT_FALSE(cond->checkConditionFor(5)); + EXPECT_FALSE(cond->checkConditionFor(15)); + + EXPECT_TRUE(cond->checkConditionFor(20)); + EXPECT_FALSE(cond->checkConditionFor(20.001)); + EXPECT_FALSE(cond->checkConditionFor(25)); + + EXPECT_FALSE(cond->checkConditionFor(5)); + EXPECT_FALSE(cond->checkConditionFor(5)); + EXPECT_FALSE(cond->checkConditionFor(5)); + + EXPECT_TRUE(cond->checkConditionFor(50)); + EXPECT_FALSE(cond->checkConditionFor(5)); + +} + diff --git a/lib/SuplaDevice/extras/test/ConditionTests/on_greater_tests.cpp b/lib/SuplaDevice/extras/test/ConditionTests/on_greater_tests.cpp new file mode 100644 index 00000000..c51459ab --- /dev/null +++ b/lib/SuplaDevice/extras/test/ConditionTests/on_greater_tests.cpp @@ -0,0 +1,38 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include + +TEST(OnGreaterTests, OnGreaterConditionTests) { + auto cond = OnGreater(20); + + EXPECT_FALSE(cond->checkConditionFor(5)); + EXPECT_FALSE(cond->checkConditionFor(15)); + + EXPECT_FALSE(cond->checkConditionFor(20)); + EXPECT_TRUE(cond->checkConditionFor(20.001)); + EXPECT_FALSE(cond->checkConditionFor(25)); + + EXPECT_FALSE(cond->checkConditionFor(5)); + EXPECT_FALSE(cond->checkConditionFor(5)); + EXPECT_FALSE(cond->checkConditionFor(5)); + + EXPECT_TRUE(cond->checkConditionFor(50)); + EXPECT_FALSE(cond->checkConditionFor(5)); + +} diff --git a/lib/SuplaDevice/extras/test/ConditionTests/on_less_eq_tests.cpp b/lib/SuplaDevice/extras/test/ConditionTests/on_less_eq_tests.cpp new file mode 100644 index 00000000..f9de0634 --- /dev/null +++ b/lib/SuplaDevice/extras/test/ConditionTests/on_less_eq_tests.cpp @@ -0,0 +1,43 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include + +TEST(OnEqualEqTests, OnLessEqConditionTests) { + auto cond = OnLessEq(10); + + EXPECT_TRUE(cond->checkConditionFor(5)); + EXPECT_FALSE(cond->checkConditionFor(15)); + + EXPECT_TRUE(cond->checkConditionFor(10)); + EXPECT_FALSE(cond->checkConditionFor(9.9999)); + + // "On" conditions should fire actions only on transition to meet condition. + EXPECT_FALSE(cond->checkConditionFor(5)); + EXPECT_FALSE(cond->checkConditionFor(5)); + EXPECT_FALSE(cond->checkConditionFor(5)); + + // Going back above threshold value, should reset expectation and it should return + // true on next call with met condition + EXPECT_FALSE(cond->checkConditionFor(50)); + EXPECT_TRUE(cond->checkConditionFor(5)); + +} + + + diff --git a/lib/SuplaDevice/extras/test/ConditionTests/on_less_tests.cpp b/lib/SuplaDevice/extras/test/ConditionTests/on_less_tests.cpp new file mode 100644 index 00000000..aee77b1b --- /dev/null +++ b/lib/SuplaDevice/extras/test/ConditionTests/on_less_tests.cpp @@ -0,0 +1,336 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include + +#include +#include +#include +#include + + +class ActionHandlerMock : public Supla::ActionHandler { + public: + MOCK_METHOD(void, handleAction, (int, int), (override)); +}; + + +using ::testing::_; +using ::testing::ElementsAreArray; +using ::testing::Args; +using ::testing::ElementsAre; + +TEST(ConditionTests, handleActionTestsForDouble) { + ActionHandlerMock ahMock; + const int action1 = 15; + const int action2 = 16; + const int action3 = 17; + + EXPECT_CALL(ahMock, handleAction(Supla::ON_CHANGE, action1)).Times(4); + EXPECT_CALL(ahMock, handleAction(Supla::ON_CHANGE, action3)).Times(4); + + Supla::ChannelElement channelElement; + auto channel = channelElement.getChannel(); + + auto cond = OnLess(15.1); + cond->setSource(channelElement); + cond->setClient(ahMock); + + + // DISTANCE sensor use int type on channel value + channel->setType(SUPLA_CHANNELTYPE_WINDSENSOR); + + channel->setNewValue(0.0); + // channel should be initialized to 0, so condition should be met + cond->handleAction(Supla::ON_CHANGE, action1); + + channel->setNewValue(100.0); + + // 100 is not less than 15.1, so nothing should happen + cond->handleAction(Supla::ON_CHANGE, action2); + + channel->setNewValue(15.0); + // 15 is less than 15.1 + cond->handleAction(Supla::ON_CHANGE, action3); + + // nothing should happen + channel->setNewValue(25.0); + cond->handleAction(Supla::ON_CHANGE, action1); + + // PRESSURE sensor use int type on channel value + channel->setType(SUPLA_CHANNELTYPE_PRESSURESENSOR); + + channel->setNewValue(0.0); + // channel should be initialized to 0, so condition should be met + cond->handleAction(Supla::ON_CHANGE, action1); + + channel->setNewValue(100.0); + + // 100 is not less than 15.1, so nothing should happen + cond->handleAction(Supla::ON_CHANGE, action2); + + channel->setNewValue(15.0); + // 15 is less than 15.1 + cond->handleAction(Supla::ON_CHANGE, action3); + + // nothing should happen + channel->setNewValue(25.0); + cond->handleAction(Supla::ON_CHANGE, action1); + + // RAIN sensor use int type on channel value + channel->setType(SUPLA_CHANNELTYPE_RAINSENSOR); + + channel->setNewValue(0.0); + // channel should be initialized to 0, so condition should be met + cond->handleAction(Supla::ON_CHANGE, action1); + + channel->setNewValue(100.0); + + // 100 is not less than 15.1, so nothing should happen + cond->handleAction(Supla::ON_CHANGE, action2); + + channel->setNewValue(15.0); + // 15 is less than 15.1 + cond->handleAction(Supla::ON_CHANGE, action3); + + // nothing should happen + channel->setNewValue(25.0); + cond->handleAction(Supla::ON_CHANGE, action1); + + + // WEIGHT sensor use int type on channel value + channel->setType(SUPLA_CHANNELTYPE_WEIGHTSENSOR); + + channel->setNewValue(0.0); + // channel should be initialized to 0, so condition should be met + cond->handleAction(Supla::ON_CHANGE, action1); + + channel->setNewValue(100.0); + + // 100 is not less than 15.1, so nothing should happen + cond->handleAction(Supla::ON_CHANGE, action2); + + channel->setNewValue(15.0); + // 15 is less than 15.1 + cond->handleAction(Supla::ON_CHANGE, action3); + +} + +TEST(ConditionTests, handleActionTestsForInt64) { + ActionHandlerMock ahMock; + const int action1 = 15; + const int action2 = 16; + const int action3 = 17; + + EXPECT_CALL(ahMock, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(ahMock, handleAction(Supla::ON_CHANGE, action3)); + + Supla::ChannelElement channelElement; + auto channel = channelElement.getChannel(); + + auto cond = OnLess(15.1); + cond->setSource(channelElement); + cond->setClient(ahMock); + + // DISTANCE sensor use int type on channel value + channel->setType(SUPLA_CHANNELTYPE_IMPULSE_COUNTER); + + // channel should be initialized to 0, so condition should be met + cond->handleAction(Supla::ON_CHANGE, action1); + + _supla_int64_t newValue = 10000344422234; + channel->setNewValue(newValue); + + // newValue is not less than 15.1, so nothing should happen + cond->handleAction(Supla::ON_CHANGE, action2); + + newValue = 2; + channel->setNewValue(newValue); + // newValue is less than 15.1 + cond->handleAction(Supla::ON_CHANGE, action3); +} + +TEST(ConditionTests, handleActionTestsForDouble2) { + ActionHandlerMock ahMock; + const int action1 = 15; + const int action2 = 16; + const int action3 = 17; + + EXPECT_CALL(ahMock, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(ahMock, handleAction(Supla::ON_CHANGE, action3)); + + Supla::ChannelElement channelElement; + auto channel = channelElement.getChannel(); + + auto cond = OnLess(15.1); + cond->setSource(channelElement); + cond->setClient(ahMock); + + channel->setType(SUPLA_CHANNELTYPE_THERMOMETER); + + // channel should be initialized to 0, so condition should be met + cond->handleAction(Supla::ON_CHANGE, action1); + + channel->setNewValue(15.1); + + // 15.1 is not less than 15.1, so nothing should happen + cond->handleAction(Supla::ON_CHANGE, action2); + + channel->setNewValue(15.01); + cond->handleAction(Supla::ON_CHANGE, action3); + + // DISTANCE sensor use double type on channel value + channel->setType(SUPLA_CHANNELTYPE_DISTANCESENSOR); + + channel->setNewValue(100); + + // 100 is not less than 15.1, so nothing should happen + cond->handleAction(Supla::ON_CHANGE, action2); + + channel->setNewValue(15); + // 15 is less than 15.1 + cond->handleAction(Supla::ON_CHANGE, action3); + + // nothing should happen + channel->setNewValue(25); + cond->handleAction(Supla::ON_CHANGE, action1); +} + +TEST(ConditionTests, handleActionTestsForNotSupportedChannel) { + ActionHandlerMock ahMock; + const int action1 = 15; + const int action2 = 16; + const int action3 = 17; + + EXPECT_CALL(ahMock, handleAction).Times(0); + + Supla::ChannelElement channelElement; + auto channel = channelElement.getChannel(); + + auto cond = OnLess(15.1); + cond->setSource(channelElement); + cond->setClient(ahMock); + + // this channel type is not used in library. DS18B20 uses standard THERMOMETER channel + channel->setType(SUPLA_CHANNELTYPE_THERMOMETERDS18B20); + + cond->handleAction(Supla::ON_CHANGE, action1); + + channel->setNewValue(15.1); + + cond->handleAction(Supla::ON_CHANGE, action2); + + channel->setNewValue(15.01); + cond->handleAction(Supla::ON_CHANGE, action3); +} + +TEST(ConditionTests, handleActionTestsForFirstDouble) { + ActionHandlerMock ahMock; + const int action1 = 15; + const int action2 = 16; + const int action3 = 17; + + EXPECT_CALL(ahMock, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(ahMock, handleAction(Supla::ON_CHANGE, action3)); + + Supla::ChannelElement channelElement; + auto channel = channelElement.getChannel(); + + auto cond = OnLess(15.1); + cond->setSource(channelElement); + cond->setClient(ahMock); + + channel->setType(SUPLA_CHANNELTYPE_HUMIDITYANDTEMPSENSOR); + + // channel should be initialized to 0, so condition should be met + cond->handleAction(Supla::ON_CHANGE, action1); + + channel->setNewValue(15.1, 10.5); + + // nothing should happen + cond->handleAction(Supla::ON_CHANGE, action2); + + channel->setNewValue(15.1, 100.5); + + // nothing should happen + cond->handleAction(Supla::ON_CHANGE, action2); + + + // ahMock should be called + channel->setNewValue(15.01, 25.1); + cond->handleAction(Supla::ON_CHANGE, action3); +} + +TEST(ConditionTests, handleActionTestsForSecondDouble) { + ActionHandlerMock ahMock; + const int action1 = 15; + const int action2 = 16; + const int action3 = 17; + + EXPECT_CALL(ahMock, handleAction(Supla::ON_CHANGE, action1)); + EXPECT_CALL(ahMock, handleAction(Supla::ON_CHANGE, action3)); + + Supla::ChannelElement channelElement; + auto channel = channelElement.getChannel(); + + // second parameter indicates that we should check alternative channel value (pressure/second float) + auto cond = OnLess(15.1, true); + cond->setSource(channelElement); + cond->setClient(ahMock); + + channel->setType(SUPLA_CHANNELTYPE_HUMIDITYANDTEMPSENSOR); + + // channel should be initialized to 0, so condition should be met + cond->handleAction(Supla::ON_CHANGE, action1); + + channel->setNewValue(0.1, 15.1); + + // nothing should happen + cond->handleAction(Supla::ON_CHANGE, action2); + + channel->setNewValue(15.1, 100.5); + + // nothing should happen + cond->handleAction(Supla::ON_CHANGE, action2); + + + // ahMock should be called + channel->setNewValue(16.01, 5.1); + cond->handleAction(Supla::ON_CHANGE, action3); +} + +TEST(OnLessTests, OnLessConditionTests) { + auto cond = OnLess(10); + + EXPECT_TRUE(cond->checkConditionFor(5)); + EXPECT_FALSE(cond->checkConditionFor(15)); + EXPECT_FALSE(cond->checkConditionFor(10)); + EXPECT_TRUE(cond->checkConditionFor(9.9999)); + + // "On" conditions should fire actions only on transition to meet condition. + EXPECT_FALSE(cond->checkConditionFor(5)); + EXPECT_FALSE(cond->checkConditionFor(5)); + EXPECT_FALSE(cond->checkConditionFor(5)); + + // Going back above threshold value, should reset expectation and it should return + // true on next call with met condition + EXPECT_FALSE(cond->checkConditionFor(50)); + EXPECT_TRUE(cond->checkConditionFor(5)); + +} + + diff --git a/lib/SuplaDevice/extras/test/ElementTests/element_tests.cpp b/lib/SuplaDevice/extras/test/ElementTests/element_tests.cpp new file mode 100644 index 00000000..8526dc0a --- /dev/null +++ b/lib/SuplaDevice/extras/test/ElementTests/element_tests.cpp @@ -0,0 +1,180 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include +#include +#include +#include + +using ::testing::Return; +using ::testing::ElementsAreArray; + +class ElementWithChannel : public Supla::Element { + public: + Supla::Channel *getChannel() { + return &channel; + } + Supla::Channel channel; +}; + +TEST(ElementTests, ElementEmptyListTests) { + EXPECT_EQ(Supla::Element::begin(), nullptr); + EXPECT_EQ(Supla::Element::last(), nullptr); + EXPECT_EQ(Supla::Element::getElementByChannelNumber(0), nullptr); + EXPECT_EQ(Supla::Element::getElementByChannelNumber(-1), nullptr); + EXPECT_EQ(Supla::Element::getElementByChannelNumber(10), nullptr); +} + +TEST(ElementTests, ElementListAdding) { + auto el1 = new Supla::Element; + + EXPECT_EQ(Supla::Element::begin(), el1); + EXPECT_EQ(Supla::Element::last(), el1); + + EXPECT_EQ(Supla::Element::getElementByChannelNumber(0), nullptr); + // Element without channel number acts as channel with -1 number + EXPECT_EQ(Supla::Element::getElementByChannelNumber(-1), el1); + EXPECT_EQ(Supla::Element::getElementByChannelNumber(10), nullptr); + + auto el2 = new Supla::Element; + EXPECT_EQ(Supla::Element::begin(), el1); + EXPECT_EQ(Supla::Element::last(), el2); + + EXPECT_EQ(Supla::Element::getElementByChannelNumber(0), nullptr); + // Element without channel number acts as channel with -1 number + EXPECT_EQ(Supla::Element::getElementByChannelNumber(-1), el1); + EXPECT_EQ(Supla::Element::getElementByChannelNumber(10), nullptr); + + auto el3 = new Supla::Element; + EXPECT_EQ(Supla::Element::begin(), el1); + EXPECT_EQ(Supla::Element::last(), el3); + + delete el2; + + EXPECT_EQ(Supla::Element::begin(), el1); + EXPECT_EQ(Supla::Element::last(), el3); + + el2 = new Supla::Element; + EXPECT_EQ(Supla::Element::begin(), el1); + EXPECT_EQ(Supla::Element::last(), el2); + + delete el1; + EXPECT_EQ(Supla::Element::begin(), el3); + EXPECT_EQ(Supla::Element::last(), el2); + + el1 = new Supla::Element; + EXPECT_EQ(Supla::Element::begin(), el3); + EXPECT_EQ(Supla::Element::last(), el1); + + delete el1; + EXPECT_EQ(Supla::Element::begin(), el3); + EXPECT_EQ(Supla::Element::last(), el2); + + delete el2; + delete el3; + + EXPECT_EQ(Supla::Element::begin(), nullptr); + EXPECT_EQ(Supla::Element::last(), nullptr); + +} + +TEST(ElementTests, NoChannelElementMethods) { + Supla::Element el1; + + // those methods are empty, so just call to make sure that they do nothing and don't crash + el1.onInit(); + el1.onLoadState(); + el1.onSaveState(); + el1.iterateAlways(); + el1.onTimer(); + el1.onFastTimer(); + + TDSC_ChannelState channelState; + el1.handleGetChannelState(channelState); + + EXPECT_EQ(el1.getChannelNumber(), -1); + EXPECT_EQ(el1.getChannel(), nullptr); + EXPECT_EQ(el1.getSecondaryChannel(), nullptr); + EXPECT_EQ(&(el1.disableChannelState()), &el1); + EXPECT_EQ(el1.next(), nullptr); + + EXPECT_EQ(el1.iterateConnected(nullptr), true); + EXPECT_EQ(el1.iterateConnected(&el1), true); + EXPECT_EQ(el1.handleNewValueFromServer(nullptr), -1); + EXPECT_EQ(el1.handleCalcfgFromServer(nullptr), SUPLA_CALCFG_RESULT_NOT_SUPPORTED); +} + +TEST(ElementTests, ChannelElementMethods) { + ElementWithChannel el1; + TimeInterfaceMock time; + SrpcMock srpc; + + // those methods are empty, so just call to make sure that they do nothing and don't crash + el1.onInit(); + el1.onLoadState(); + el1.onSaveState(); + el1.iterateAlways(); + el1.onTimer(); + el1.onFastTimer(); + + TDSC_ChannelState channelState; + el1.handleGetChannelState(channelState); + + EXPECT_EQ(el1.getChannelNumber(), 0); + EXPECT_EQ(el1.getChannel(), &(el1.channel)); + EXPECT_EQ(el1.getSecondaryChannel(), nullptr); + EXPECT_EQ(&(el1.disableChannelState()), &el1); + EXPECT_EQ(el1.next(), nullptr); + + EXPECT_FALSE(el1.channel.isUpdateReady()); + EXPECT_EQ(el1.iterateConnected(nullptr), true); + EXPECT_EQ(el1.iterateConnected(&el1), true); + EXPECT_EQ(el1.handleNewValueFromServer(nullptr), -1); + EXPECT_EQ(el1.handleCalcfgFromServer(nullptr), SUPLA_CALCFG_RESULT_NOT_SUPPORTED); + + EXPECT_FALSE(el1.channel.isUpdateReady()); + el1.channel.setNewValue(true); + EXPECT_TRUE(el1.channel.isUpdateReady()); + + EXPECT_CALL(time, millis) + .WillOnce(Return(0)) // #1 first call after value changed to true + .WillOnce(Return(200)) // #2 two calls after value changed to true and 100 ms passed + .WillOnce(Return(200)) // #2 + .WillOnce(Return(250)) // #3 value changed, however not enough time passed + .WillOnce(Return(250)) // #4 value changed, however not enough time passed + .WillOnce(Return(400)) // #5 two calls after value changed and another >100 ms passed + .WillOnce(Return(400)); + + char array0[SUPLA_CHANNELVALUE_SIZE] = {}; + char array1[SUPLA_CHANNELVALUE_SIZE] = {}; + array1[0] = 1; + EXPECT_CALL(srpc, valueChanged(nullptr, 0, ElementsAreArray(array1))); // value at #2 + EXPECT_CALL(srpc, valueChanged(nullptr, 0, ElementsAreArray(array0))); // value at #5 + + + EXPECT_EQ(el1.iterateConnected(nullptr), true); // #1 + EXPECT_EQ(el1.iterateConnected(nullptr), false); // #2 + + el1.channel.setNewValue(false); + EXPECT_EQ(el1.iterateConnected(nullptr), true); // #3 + EXPECT_EQ(el1.iterateConnected(nullptr), true); // #4 + EXPECT_EQ(el1.iterateConnected(nullptr), false); // #5 +} + + + diff --git a/lib/SuplaDevice/extras/test/InternalPinOutputTests/internal_pin_output_tests.cpp b/lib/SuplaDevice/extras/test/InternalPinOutputTests/internal_pin_output_tests.cpp new file mode 100644 index 00000000..fe661d4d --- /dev/null +++ b/lib/SuplaDevice/extras/test/InternalPinOutputTests/internal_pin_output_tests.cpp @@ -0,0 +1,353 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include + +#include +#include +#include +#include + +using ::testing::Return; + +class ActionHandlerMock : public Supla::ActionHandler { + public: + MOCK_METHOD(void, handleAction, (int, int), (override)); +}; + +TEST(InternalPinOutputTests, BasicMethodsTests) { + const int pin = 5; + const int pin2 = 6; + DigitalInterfaceMock ioMock; + TimeInterfaceMock timeMock; + + EXPECT_CALL(timeMock, millis).WillRepeatedly(Return(0)); + + ::testing::InSequence seq; + + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); + EXPECT_CALL(ioMock, pinMode(pin, OUTPUT)).Times(1); + + EXPECT_CALL(ioMock, digitalWrite(pin2, HIGH)).Times(1); + EXPECT_CALL(ioMock, pinMode(pin2, OUTPUT)).Times(1); + + EXPECT_CALL(ioMock, digitalRead(pin)).WillOnce(Return(LOW)); + EXPECT_CALL(ioMock, digitalRead(pin2)).WillOnce(Return(HIGH)); + + EXPECT_CALL(ioMock, digitalWrite(pin, HIGH)).Times(1); + + EXPECT_CALL(ioMock, digitalWrite(pin2, LOW)).Times(1); + + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); + + EXPECT_CALL(ioMock, digitalRead(pin)).WillOnce(Return(LOW)); + EXPECT_CALL(ioMock, digitalWrite(pin, HIGH)).Times(1); + + Supla::Control::InternalPinOutput ipo(pin); + Supla::Control::InternalPinOutput ipo2(pin2, false); + + + EXPECT_EQ(ipo.pinOnValue(), HIGH); + EXPECT_EQ(ipo.pinOffValue(), LOW); + + EXPECT_EQ(ipo2.pinOnValue(), LOW); + EXPECT_EQ(ipo2.pinOffValue(), HIGH); + + ipo.onInit(); + ipo2.onInit(); + + EXPECT_EQ(ipo.isOn(), false); + EXPECT_EQ(ipo2.isOn(), false); + + ipo.turnOn(); + ipo2.turnOn(); + + ipo.turnOff(); + + ipo.toggle(); +} + +TEST(InternalPinOutputTests, DefaultInitialState) { + const int pin = 5; + const int pin2 = 6; + DigitalInterfaceMock ioMock; + TimeInterfaceMock timeMock; + + EXPECT_CALL(timeMock, millis).WillRepeatedly(Return(0)); + + ::testing::InSequence seq; + + EXPECT_CALL(ioMock, digitalWrite(pin, HIGH)).Times(1); + EXPECT_CALL(ioMock, pinMode(pin, OUTPUT)).Times(1); + + EXPECT_CALL(ioMock, digitalWrite(pin2, HIGH)).Times(1); + EXPECT_CALL(ioMock, pinMode(pin2, OUTPUT)).Times(1); + + Supla::Control::InternalPinOutput ipo(pin); + Supla::Control::InternalPinOutput ipo2(pin2, false); + + ipo.setDefaultStateOn(); + ipo2.setDefaultStateOff(); + + ipo.onInit(); + ipo2.onInit(); + +} + +TEST(InternalPinOutputTests, TurnOnWithIterations) { + const int pin = 5; + DigitalInterfaceMock ioMock; + TimeInterfaceMock timeMock; + + + ::testing::InSequence seq; + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); + EXPECT_CALL(ioMock, pinMode(pin, OUTPUT)).Times(1); + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(ioMock, digitalWrite(pin, HIGH)).Times(1); + + + Supla::Control::InternalPinOutput ipo(pin); + + ipo.onInit(); + + ipo.iterateAlways(); + ipo.iterateAlways(); + + ipo.turnOn(); + ipo.iterateAlways(); + ipo.iterateAlways(); +} + +TEST(InternalPinOutputTests, TurnOnWithDuration) { + const int pin = 5; + DigitalInterfaceMock ioMock; + TimeInterfaceMock timeMock; + + + ::testing::InSequence seq; + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); + EXPECT_CALL(ioMock, pinMode(pin, OUTPUT)).Times(1); + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(ioMock, digitalWrite(pin, HIGH)).Times(1); + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(100)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(200)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(201)); + + EXPECT_CALL(ioMock, digitalRead(pin)).WillOnce(Return(HIGH)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(201)); + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); + + + Supla::Control::InternalPinOutput ipo(pin); + + ipo.onInit(); + + ipo.iterateAlways(); + ipo.iterateAlways(); + + ipo.turnOn(200); + ipo.iterateAlways(); // time 0 + ipo.iterateAlways(); // time 100 + ipo.iterateAlways(); // time 200 + ipo.iterateAlways(); // time 201 + + ipo.iterateAlways(); + ipo.iterateAlways(); + ipo.iterateAlways(); +} + +TEST(InternalPinOutputTests, TurnOffWithDuration) { + const int pin = 5; + DigitalInterfaceMock ioMock; + TimeInterfaceMock timeMock; + + + ::testing::InSequence seq; + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); + EXPECT_CALL(ioMock, pinMode(pin, OUTPUT)).Times(1); + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(100)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(200)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(201)); + + EXPECT_CALL(ioMock, digitalRead(pin)).WillOnce(Return(LOW)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(201)); + EXPECT_CALL(ioMock, digitalWrite(pin, HIGH)).Times(1); + + Supla::Control::InternalPinOutput ipo(pin); + + ipo.onInit(); + + ipo.iterateAlways(); + ipo.iterateAlways(); + + ipo.turnOff(200); + ipo.iterateAlways(); // time 0 + ipo.iterateAlways(); // time 100 + ipo.iterateAlways(); // time 200 + ipo.iterateAlways(); // time 201 + + ipo.iterateAlways(); + ipo.iterateAlways(); + ipo.iterateAlways(); +} + + +TEST(InternalPinOutputTests, TurnOnWithStoredDuration) { + const int pin = 5; + DigitalInterfaceMock ioMock; + TimeInterfaceMock timeMock; + + + ::testing::InSequence seq; + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); + EXPECT_CALL(ioMock, pinMode(pin, OUTPUT)).Times(1); + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(ioMock, digitalWrite(pin, HIGH)).Times(1); + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(100)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(200)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(201)); + + EXPECT_CALL(ioMock, digitalRead(pin)).WillOnce(Return(HIGH)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(201)); + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); + + EXPECT_CALL(timeMock, millis).WillOnce(Return(300)); + EXPECT_CALL(ioMock, digitalWrite(pin, HIGH)).Times(1); + EXPECT_CALL(timeMock, millis).WillOnce(Return(400)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(600)); + EXPECT_CALL(ioMock, digitalRead(pin)).WillOnce(Return(HIGH)); + EXPECT_CALL(timeMock, millis).WillOnce(Return(600)); + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); + + Supla::Control::InternalPinOutput ipo(pin); + + ipo.onInit(); + + ipo.iterateAlways(); + ipo.iterateAlways(); + + ipo.setDurationMs(200); + + ipo.turnOn(); + ipo.iterateAlways(); // time 0 + ipo.iterateAlways(); // time 100 + ipo.iterateAlways(); // time 200 + ipo.iterateAlways(); // time 201 + + ipo.iterateAlways(); + ipo.iterateAlways(); + ipo.iterateAlways(); + + ipo.turnOn(); + ipo.iterateAlways(); + ipo.iterateAlways(); + ipo.iterateAlways(); +} + +TEST(InternalPinOutputTests, HandleActionTests) { + const int pin = 5; + DigitalInterfaceMock ioMock; + TimeInterfaceMock timeMock; + + EXPECT_CALL(timeMock, millis).WillRepeatedly(Return(0)); + + ::testing::InSequence seq; + + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); // onInit + EXPECT_CALL(ioMock, pinMode(pin, OUTPUT)).Times(1); + + EXPECT_CALL(ioMock, digitalWrite(pin, HIGH)).Times(1); // Turn on + + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); // Turn off + + EXPECT_CALL(ioMock, digitalRead(pin)).WillOnce(Return(LOW)); // Toggle + EXPECT_CALL(ioMock, digitalWrite(pin, HIGH)).Times(1); + + Supla::Control::InternalPinOutput ipo(pin); + + ipo.onInit(); + + ipo.iterateAlways(); + ipo.iterateAlways(); + + ipo.handleAction(0, Supla::TURN_ON); + + ipo.iterateAlways(); + ipo.handleAction(0, Supla::TURN_OFF); + + ipo.iterateAlways(); + ipo.handleAction(0, Supla::TOGGLE); + ipo.iterateAlways(); +} + +TEST(InternalPinOutputTests, TurnOnWithAction) { + const int pin = 5; + DigitalInterfaceMock ioMock; + TimeInterfaceMock timeMock; + ActionHandlerMock ahMock; + + + ::testing::InSequence seq; + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(ahMock, handleAction(Supla::ON_TURN_OFF, Supla::TURN_OFF)); + EXPECT_CALL(ahMock, handleAction(Supla::ON_CHANGE, 33)); + EXPECT_CALL(ioMock, digitalWrite(pin, LOW)).Times(1); + EXPECT_CALL(ioMock, pinMode(pin, OUTPUT)).Times(1); + + EXPECT_CALL(timeMock, millis).WillOnce(Return(0)); + EXPECT_CALL(ahMock, handleAction(Supla::ON_TURN_ON, Supla::TURN_ON)); + EXPECT_CALL(ahMock, handleAction(Supla::ON_CHANGE, 33)); + EXPECT_CALL(ioMock, digitalWrite(pin, HIGH)).Times(1); + + + Supla::Control::InternalPinOutput ipo(pin); + ipo.addAction(Supla::TURN_ON, ahMock, Supla::ON_TURN_ON); + ipo.addAction(Supla::TURN_OFF, ahMock, Supla::ON_TURN_OFF); + ipo.addAction(33, ahMock, Supla::ON_CHANGE); + + ipo.onInit(); + + ipo.iterateAlways(); + ipo.iterateAlways(); + + ipo.turnOn(); + ipo.iterateAlways(); + ipo.iterateAlways(); +} + diff --git a/lib/SuplaDevice/extras/test/IoTests/io_tests.cpp b/lib/SuplaDevice/extras/test/IoTests/io_tests.cpp new file mode 100644 index 00000000..0c9fc938 --- /dev/null +++ b/lib/SuplaDevice/extras/test/IoTests/io_tests.cpp @@ -0,0 +1,146 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include + +using ::testing::Return; + +class CustomIoMock : public Supla::Io { + public: + MOCK_METHOD( + void, customPinMode, (int channelNumber, uint8_t pin, uint8_t mode)); + MOCK_METHOD(int, customDigitalRead, (int channelNumber, uint8_t pin)); + MOCK_METHOD( + void, customDigitalWrite, (int channelNumber, uint8_t pin, uint8_t val)); +}; + +TEST(IoTests, PinMode) { + DigitalInterfaceMock ioMock; + ::testing::InSequence seq; + + EXPECT_CALL(ioMock, pinMode(3, INPUT)); + EXPECT_CALL(ioMock, pinMode(0, OUTPUT)); + EXPECT_CALL(ioMock, pinMode(3, INPUT_PULLUP)); + + Supla::Io::pinMode(3, INPUT); + Supla::Io::pinMode(0, OUTPUT); + Supla::Io::pinMode(3, INPUT_PULLUP); +} + +TEST(IoTests, PinModeWithChannelNumber) { + DigitalInterfaceMock ioMock; + ::testing::InSequence seq; + + EXPECT_CALL(ioMock, pinMode(3, INPUT)); + EXPECT_CALL(ioMock, pinMode(0, OUTPUT)); + EXPECT_CALL(ioMock, pinMode(3, INPUT_PULLUP)); + + Supla::Io::pinMode(10, 3, INPUT); + Supla::Io::pinMode(11, 0, OUTPUT); + Supla::Io::pinMode(12, 3, INPUT_PULLUP); +} + +TEST(IoTests, DigitalWrite) { + DigitalInterfaceMock ioMock; + ::testing::InSequence seq; + + EXPECT_CALL(ioMock, digitalWrite(3, HIGH)); + EXPECT_CALL(ioMock, digitalWrite(3, LOW)); + EXPECT_CALL(ioMock, digitalWrite(99, LOW)); + + Supla::Io::digitalWrite(3, HIGH); + Supla::Io::digitalWrite(3, LOW); + Supla::Io::digitalWrite(99, LOW); +} + +TEST(IoTests, DigitalWriteWithChannel) { + DigitalInterfaceMock ioMock; + ::testing::InSequence seq; + + EXPECT_CALL(ioMock, digitalWrite(3, HIGH)); + EXPECT_CALL(ioMock, digitalWrite(3, LOW)); + EXPECT_CALL(ioMock, digitalWrite(99, LOW)); + + Supla::Io::digitalWrite(3, HIGH); + Supla::Io::digitalWrite(3, LOW); + Supla::Io::digitalWrite(99, LOW); +} + +TEST(IoTests, DigitalRead) { + DigitalInterfaceMock ioMock; + + EXPECT_CALL(ioMock, digitalRead(3)) + .WillOnce(Return(LOW)) + .WillOnce(Return(HIGH)) + .WillOnce(Return(LOW)); + + EXPECT_EQ(Supla::Io::digitalRead(3), LOW); + EXPECT_EQ(Supla::Io::digitalRead(3), HIGH); + EXPECT_EQ(Supla::Io::digitalRead(3), LOW); +} + +TEST(IoTests, DigitalReadWithChannel) { + DigitalInterfaceMock ioMock; + + EXPECT_CALL(ioMock, digitalRead(3)) + .WillOnce(Return(LOW)) + .WillOnce(Return(HIGH)) + .WillOnce(Return(LOW)); + + EXPECT_EQ(Supla::Io::digitalRead(100, 3), LOW); + EXPECT_EQ(Supla::Io::digitalRead(100, 3), HIGH); + EXPECT_EQ(Supla::Io::digitalRead(-1, 3), LOW); +} + +TEST(IoTest, OperationsWithCustomIoInteface) { + DigitalInterfaceMock hwInterfaceMock; + CustomIoMock ioMock; + + EXPECT_CALL(hwInterfaceMock, pinMode).Times(0); + EXPECT_CALL(hwInterfaceMock, digitalWrite).Times(0); + EXPECT_CALL(hwInterfaceMock, digitalRead).Times(0); + + EXPECT_CALL(ioMock, customPinMode(-1, 12, INPUT)); + EXPECT_CALL(ioMock, customDigitalRead(-1, 11)) + .WillOnce(Return(HIGH)); + EXPECT_CALL(ioMock, customDigitalWrite(-1, 13, HIGH)); + + Supla::Io::pinMode(12, INPUT); + EXPECT_EQ(Supla::Io::digitalRead(11), HIGH); + Supla::Io::digitalWrite(13, HIGH); + +} +TEST(IoTest, OperationsWithCustomIoIntefaceWithChannel) { + DigitalInterfaceMock hwInterfaceMock; + CustomIoMock ioMock; + + // Custom io interface should not call arduino's methods + EXPECT_CALL(hwInterfaceMock, pinMode).Times(0); + EXPECT_CALL(hwInterfaceMock, digitalWrite).Times(0); + EXPECT_CALL(hwInterfaceMock, digitalRead).Times(0); + + EXPECT_CALL(ioMock, customPinMode(6, 12, INPUT)); + EXPECT_CALL(ioMock, customDigitalRead(6, 11)) + .WillOnce(Return(HIGH)); + EXPECT_CALL(ioMock, customDigitalWrite(6, 13, HIGH)); + + Supla::Io::pinMode(6, 12, INPUT); + EXPECT_EQ(Supla::Io::digitalRead(6, 11), HIGH); + Supla::Io::digitalWrite(6, 13, HIGH); + +} diff --git a/lib/SuplaDevice/extras/test/LocalActionTests/local_action_tests.cpp b/lib/SuplaDevice/extras/test/LocalActionTests/local_action_tests.cpp new file mode 100644 index 00000000..bbd03614 --- /dev/null +++ b/lib/SuplaDevice/extras/test/LocalActionTests/local_action_tests.cpp @@ -0,0 +1,177 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include + +class ActionHandlerMock : public Supla::ActionHandler { + public: + MOCK_METHOD(void, handleAction, (int, int), (override)); +}; + +TEST(LocalActionTests, TwoItemsTests) { + auto b1 = new Supla::LocalAction; + auto b2 = new Supla::LocalAction; + ActionHandlerMock mock1; + ActionHandlerMock mock2; + + int action1 = 1; + int action2 = 2; + int action3 = 3; + int action4 = 4; + int action5 = 5; + int event1 = 11; + int event2 = 12; + int event3 = 13; + + EXPECT_CALL(mock1, handleAction(event1, action1)); + EXPECT_CALL(mock1, handleAction(event1, action3)); + EXPECT_CALL(mock2, handleAction(event3, action1)); + EXPECT_CALL(mock1, handleAction(event2, action2)); + EXPECT_CALL(mock1, handleAction(event2, action4)); + EXPECT_CALL(mock1, handleAction(event2, action5)); + EXPECT_CALL(mock2, handleAction(event1, action1)); + EXPECT_CALL(mock2, handleAction(event2, action2)); + + b1->addAction(action1, mock1, event1); + b1->addAction(action2, mock1, event2); + b1->addAction(action3, mock1, event1); + b1->addAction(action4, mock1, event2); + b1->addAction(action5, mock1, event2); + b1->addAction(action1, mock2, event3); + b2->addAction(action1, mock2, event1); + b2->addAction(action2, mock2, event2); + b1->runAction(event1); + b1->runAction(event3); + b1->runAction(event2); + + b2->runAction(event1); + + delete b1; + + b2->runAction(event2); + + delete b2; +} + +TEST(LocalActionTests, FourItemsTestsNoCalls) { + auto b1 = new Supla::LocalAction; + auto b2 = new Supla::LocalAction; + auto b3 = new Supla::LocalAction; + auto b4 = new Supla::LocalAction; + ActionHandlerMock mock1; + ActionHandlerMock mock2; + ActionHandlerMock mock3; + ActionHandlerMock mock4; + + EXPECT_CALL(mock1, handleAction).Times(0); + EXPECT_CALL(mock2, handleAction).Times(0); + EXPECT_CALL(mock3, handleAction).Times(0); + EXPECT_CALL(mock4, handleAction).Times(0); + + int action1 = 1; + int action2 = 2; + int action3 = 3; + int action4 = 4; + int action5 = 5; + int event1 = 11; + int event2 = 12; + int event3 = 13; + int event4 = 14; + + b4->addAction(action4, mock4, event4); + b3->addAction(action3, mock3, event3); + b1->addAction(action1, mock1, event1); + b2->addAction(action2, mock2, event2); + + b1->runAction(event2); + b1->runAction(event3); + b1->runAction(event4); + + b2->runAction(event1); + b2->runAction(event3); + b2->runAction(event4); + + b3->runAction(event1); + b3->runAction(event2); + b3->runAction(event4); + + b4->runAction(event1); + b4->runAction(event2); + b4->runAction(event3); + + delete b1; + delete b2; + delete b3; + delete b4; +} + +TEST(LocalActionTests, FourItemsTestsWithCalls) { + auto b1 = new Supla::LocalAction; + auto b2 = new Supla::LocalAction; + auto b3 = new Supla::LocalAction; + auto b4 = new Supla::LocalAction; + ActionHandlerMock mock1; + ActionHandlerMock mock2; + ActionHandlerMock mock3; + ActionHandlerMock mock4; + + int action1 = 1; + int action2 = 2; + int action3 = 3; + int action4 = 4; + int action5 = 5; + int event1 = 11; + int event2 = 12; + int event3 = 13; + int event4 = 14; + + EXPECT_CALL(mock1, handleAction(event1, action1)); + EXPECT_CALL(mock2, handleAction(event2, action2)); + EXPECT_CALL(mock3, handleAction(event3, action3)); + EXPECT_CALL(mock4, handleAction(event4, action4)); + + b4->addAction(action4, mock4, event4); + b3->addAction(action3, mock3, event3); + b1->addAction(action1, mock1, event1); + b2->addAction(action2, mock2, event2); + + b1->runAction(event1); + b1->runAction(event2); + b1->runAction(event3); + b1->runAction(event4); + + b2->runAction(event1); + b2->runAction(event2); + b2->runAction(event3); + b2->runAction(event4); + + b3->runAction(event3); + b3->runAction(event1); + b3->runAction(event2); + b3->runAction(event4); + + b4->runAction(event1); + b4->runAction(event2); + b4->runAction(event3); + b4->runAction(event4); + + delete b1; + delete b2; + delete b3; + delete b4; +} diff --git a/lib/SuplaDevice/extras/test/PinStatusLedTests/pin_status_led_tests.cpp b/lib/SuplaDevice/extras/test/PinStatusLedTests/pin_status_led_tests.cpp new file mode 100644 index 00000000..c9118731 --- /dev/null +++ b/lib/SuplaDevice/extras/test/PinStatusLedTests/pin_status_led_tests.cpp @@ -0,0 +1,103 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include + +#include +#include + + +using ::testing::Return; + +TEST(PinStatusLedTest, ReplicationTest) { + EXPECT_EQ(DigitalInterface::instance, nullptr); + + DigitalInterfaceMock ioMock; + TimeInterfaceMock timeMock; + + + ::testing::InSequence seq; + + EXPECT_CALL(ioMock, digitalRead(1)).WillOnce(Return(LOW)); + EXPECT_CALL(ioMock, digitalRead(2)).WillOnce(Return(LOW)); +// EXPECT_CALL(ioMock, digitalWrite(2, LOW)).Times(1); + EXPECT_CALL(ioMock, pinMode(2, OUTPUT)).Times(1); + + EXPECT_CALL(ioMock, digitalRead(1)).WillOnce(Return(LOW)); + EXPECT_CALL(ioMock, digitalRead(2)).WillOnce(Return(LOW)); +// EXPECT_CALL(ioMock, digitalWrite(2, LOW)).Times(1); + + EXPECT_CALL(ioMock, digitalRead(1)).WillOnce(Return(HIGH)); + EXPECT_CALL(ioMock, digitalRead(2)).WillOnce(Return(LOW)); + EXPECT_CALL(ioMock, digitalWrite(2, HIGH)).Times(1); + + EXPECT_CALL(ioMock, digitalRead(1)).WillOnce(Return(LOW)); + EXPECT_CALL(ioMock, digitalRead(2)).WillOnce(Return(HIGH)); + EXPECT_CALL(ioMock, digitalWrite(2, LOW)).Times(1); + + Supla::Control::PinStatusLed led(1, 2); + led.onInit(); + led.iterateAlways(); // LOW->LOW + led.iterateAlways(); // HIGH->HIGH + led.iterateAlways(); // LOW->LOW + +} + +TEST(PinStatusLedTest, ReplicationTestWithInvertedLogic) { + EXPECT_EQ(DigitalInterface::instance, nullptr); + + DigitalInterfaceMock ioMock; + TimeInterfaceMock timeMock; + + + ::testing::InSequence seq; + + EXPECT_CALL(ioMock, digitalRead(1)).WillOnce(Return(LOW)); // onInit + EXPECT_CALL(ioMock, digitalRead(2)).WillOnce(Return(LOW)); + EXPECT_CALL(ioMock, digitalWrite(2, HIGH)).Times(1); + EXPECT_CALL(ioMock, pinMode(2, OUTPUT)).Times(1); + + EXPECT_CALL(ioMock, digitalRead(1)).WillOnce(Return(LOW)); // iterateAlways + EXPECT_CALL(ioMock, digitalRead(2)).WillOnce(Return(HIGH)); +// EXPECT_CALL(ioMock, digitalWrite(2, HIGH)).Times(1); + + EXPECT_CALL(ioMock, digitalRead(1)).WillOnce(Return(HIGH)); + EXPECT_CALL(ioMock, digitalRead(2)).WillOnce(Return(HIGH)); + EXPECT_CALL(ioMock, digitalWrite(2, LOW)).Times(1); + + EXPECT_CALL(ioMock, digitalRead(1)).WillOnce(Return(LOW)); + EXPECT_CALL(ioMock, digitalRead(2)).WillOnce(Return(LOW)); + EXPECT_CALL(ioMock, digitalWrite(2, HIGH)).Times(1); + + EXPECT_CALL(ioMock, digitalRead(1)).WillOnce(Return(LOW)); // disable inverted logic + EXPECT_CALL(ioMock, digitalRead(2)).WillOnce(Return(HIGH)); + EXPECT_CALL(ioMock, digitalWrite(2, LOW)).Times(1); + + EXPECT_CALL(ioMock, digitalRead(1)).WillOnce(Return(LOW)); + EXPECT_CALL(ioMock, digitalRead(2)).WillOnce(Return(LOW)); +// EXPECT_CALL(ioMock, digitalWrite(2, HIGH)).Times(1); + + Supla::Control::PinStatusLed led(1, 2, true); + led.onInit(); + led.iterateAlways(); // LOW->HIGH + led.iterateAlways(); // HIGH->LOW + led.iterateAlways(); // LOW->HIGH + + led.setInvertedLogic(false); // LOW->LOW + led.iterateAlways(); // HIGH->HIGH + +} diff --git a/lib/SuplaDevice/extras/test/SensorTests/thermometer_tests.cpp b/lib/SuplaDevice/extras/test/SensorTests/thermometer_tests.cpp new file mode 100644 index 00000000..cea47588 --- /dev/null +++ b/lib/SuplaDevice/extras/test/SensorTests/thermometer_tests.cpp @@ -0,0 +1,24 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include + +TEST(ThermometerTests, BasicTest) { + EXPECT_TRUE(true); +} + diff --git a/lib/SuplaDevice/extras/test/UptimeTests/uptime_tests.cpp b/lib/SuplaDevice/extras/test/UptimeTests/uptime_tests.cpp new file mode 100644 index 00000000..59241f9b --- /dev/null +++ b/lib/SuplaDevice/extras/test/UptimeTests/uptime_tests.cpp @@ -0,0 +1,76 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include + +TEST(UptimeTests, LastResetCauseSetAndGet) { + Supla::Uptime uptime; + EXPECT_EQ(uptime.getUptime(), 0); + EXPECT_EQ(uptime.getConnectionUptime(), 0); + EXPECT_EQ(uptime.getLastResetCause(), SUPLA_LASTCONNECTIONRESETCAUSE_UNKNOWN); + + // setter should not work unless first resetConnectionUptime is called + uptime.setConnectionLostCause(SUPLA_LASTCONNECTIONRESETCAUSE_WIFI_CONNECTION_LOST); + EXPECT_EQ(uptime.getUptime(), 0); + EXPECT_EQ(uptime.getConnectionUptime(), 0); + EXPECT_EQ(uptime.getLastResetCause(), SUPLA_LASTCONNECTIONRESETCAUSE_UNKNOWN); + + uptime.resetConnectionUptime(); + uptime.setConnectionLostCause(SUPLA_LASTCONNECTIONRESETCAUSE_WIFI_CONNECTION_LOST); + EXPECT_EQ(uptime.getUptime(), 0); + EXPECT_EQ(uptime.getConnectionUptime(), 0); + EXPECT_EQ(uptime.getLastResetCause(), SUPLA_LASTCONNECTIONRESETCAUSE_WIFI_CONNECTION_LOST); +} + +TEST(UptimeTests, IterateShouldIncreaseUptimeCounters) { + Supla::Uptime uptime; + unsigned long millis = 0; + + EXPECT_EQ(uptime.getUptime(), 0); + EXPECT_EQ(uptime.getConnectionUptime(), 0); + EXPECT_EQ(uptime.getLastResetCause(), SUPLA_LASTCONNECTIONRESETCAUSE_UNKNOWN); + + millis += 999; + uptime.iterate(millis); + EXPECT_EQ(uptime.getUptime(), 0); + EXPECT_EQ(uptime.getConnectionUptime(), 0); + EXPECT_EQ(uptime.getLastResetCause(), SUPLA_LASTCONNECTIONRESETCAUSE_UNKNOWN); + + millis += 1500; + uptime.iterate(millis); + EXPECT_EQ(uptime.getUptime(), 2); + EXPECT_EQ(uptime.getConnectionUptime(), 2); + EXPECT_EQ(uptime.getLastResetCause(), SUPLA_LASTCONNECTIONRESETCAUSE_UNKNOWN); + + millis += 20000; + uptime.iterate(millis); + EXPECT_EQ(uptime.getUptime(), 22); + EXPECT_EQ(uptime.getConnectionUptime(), 22); + EXPECT_EQ(uptime.getLastResetCause(), SUPLA_LASTCONNECTIONRESETCAUSE_UNKNOWN); + + uptime.resetConnectionUptime(); + EXPECT_EQ(uptime.getUptime(), 22); + EXPECT_EQ(uptime.getConnectionUptime(), 0); + EXPECT_EQ(uptime.getLastResetCause(), SUPLA_LASTCONNECTIONRESETCAUSE_UNKNOWN); + + millis += 2500; + uptime.iterate(millis); + EXPECT_EQ(uptime.getUptime(), 24); + EXPECT_EQ(uptime.getConnectionUptime(), 2); + EXPECT_EQ(uptime.getLastResetCause(), SUPLA_LASTCONNECTIONRESETCAUSE_UNKNOWN); +} + diff --git a/lib/SuplaDevice/extras/test/doubles/Arduino.h b/lib/SuplaDevice/extras/test/doubles/Arduino.h new file mode 100644 index 00000000..518485d2 --- /dev/null +++ b/lib/SuplaDevice/extras/test/doubles/Arduino.h @@ -0,0 +1,115 @@ +#ifndef _test_double_arduino_h +#define _test_double_arduino_h + +#include + +#include + +typedef std::string String; + +#define LSBFIRST 0 +#define INPUT 0 +#define INPUT_PULLUP 2 +#define OUTPUT 1 +#define HIGH 1 +#define LOW 0 + +void digitalWrite(uint8_t pin, uint8_t val); +int digitalRead(uint8_t pin); +void pinMode(uint8_t pin, uint8_t mode); +unsigned long millis(); + + +class SerialStub { + public: + SerialStub() { + } + + virtual ~SerialStub() { + } + + int printf(const char *format, ...) { + return 0; + } + int print(const String &) { + return 0; + } + + int print(const char[]) { + return 0; + } + + int print(char) { + return 0; + } + + int print(unsigned char) { + return 0; + } + + int print(int) { + return 0; + } + + int print(unsigned int) { + return 0; + } + + int print(long) { + return 0; + } + + int print(unsigned long) { + return 0; + } + + int print(double) { + return 0; + } + + + int println(const String &s) { + return 0; + } + + int println(const char[]) { + return 0; + } + + int println(char) { + return 0; + } + + int println(unsigned char) { + return 0; + } + + int println(int) { + return 0; + } + + int println(unsigned int) { + return 0; + } + + int println(long) { + return 0; + } + + int println(unsigned long) { + return 0; + } + + int println(double) { + return 0; + } + + int println(void) { + return 0; + } + +}; + +extern SerialStub Serial; + +#endif diff --git a/lib/SuplaDevice/extras/test/doubles/arduino_mock.cpp b/lib/SuplaDevice/extras/test/doubles/arduino_mock.cpp new file mode 100644 index 00000000..f64576a2 --- /dev/null +++ b/lib/SuplaDevice/extras/test/doubles/arduino_mock.cpp @@ -0,0 +1,60 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + + +#include "Arduino.h" +#include +#include "arduino_mock.h" + +SerialStub Serial; + +DigitalInterface::DigitalInterface() { + instance = this; +} + +DigitalInterface::~DigitalInterface() { + instance = nullptr; +} + +DigitalInterface *DigitalInterface::instance = nullptr; + +TimeInterface::TimeInterface() { + instance = this; +} + +TimeInterface::~TimeInterface() { + instance = nullptr; +} + +TimeInterface *TimeInterface::instance = nullptr; + +void digitalWrite(uint8_t pin, uint8_t val) { + DigitalInterface::instance->digitalWrite(pin, val); +} + +int digitalRead(uint8_t pin) { + return DigitalInterface::instance->digitalRead(pin); +} + +void pinMode(uint8_t pin, uint8_t mode) { + DigitalInterface::instance->pinMode(pin, mode); +} + +unsigned long millis() { + return TimeInterface::instance->millis(); +} + + diff --git a/lib/SuplaDevice/extras/test/doubles/arduino_mock.h b/lib/SuplaDevice/extras/test/doubles/arduino_mock.h new file mode 100644 index 00000000..8daf5a1e --- /dev/null +++ b/lib/SuplaDevice/extras/test/doubles/arduino_mock.h @@ -0,0 +1,57 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _arduino_mock_h +#define _arduino_mock_h + +#include "Arduino.h" +#include + +class DigitalInterface { + public: + DigitalInterface(); + virtual ~DigitalInterface(); + virtual void digitalWrite(uint8_t, uint8_t) = 0; + virtual int digitalRead(uint8_t) = 0; + virtual void pinMode(uint8_t, uint8_t) = 0; + + static DigitalInterface *instance; +}; + +class TimeInterface { + public: + TimeInterface(); + virtual ~TimeInterface(); + virtual unsigned long millis() = 0; + + static TimeInterface *instance; +}; + + +class DigitalInterfaceMock : public DigitalInterface { + public: + MOCK_METHOD(void, digitalWrite, (uint8_t, uint8_t), (override)); + MOCK_METHOD(int, digitalRead, (uint8_t), (override)); + MOCK_METHOD(void, pinMode, (uint8_t, uint8_t), (override)); + +}; + +class TimeInterfaceMock : public TimeInterface { + public: + MOCK_METHOD(unsigned long, millis, (), (override)); +}; + +#endif diff --git a/lib/SuplaDevice/extras/test/doubles/log.cpp b/lib/SuplaDevice/extras/test/doubles/log.cpp new file mode 100644 index 00000000..e117cc36 --- /dev/null +++ b/lib/SuplaDevice/extras/test/doubles/log.cpp @@ -0,0 +1,22 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +void supla_log(int __pri, const char *__fmt, ...) { + return; +} + diff --git a/lib/SuplaDevice/extras/test/doubles/srpc.cpp b/lib/SuplaDevice/extras/test/doubles/srpc.cpp new file mode 100644 index 00000000..5ac9386a --- /dev/null +++ b/lib/SuplaDevice/extras/test/doubles/srpc.cpp @@ -0,0 +1,29 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_channel_extendedvalue_changed( + void *_srpc, unsigned char channel_number, + TSuplaChannelExtendedValue *value) { + return 0; +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_channel_value_changed( + void *_srpc, unsigned char channel_number, char *value) { + return 0; +} + diff --git a/lib/SuplaDevice/extras/test/doubles/srpc_mock.cpp b/lib/SuplaDevice/extras/test/doubles/srpc_mock.cpp new file mode 100644 index 00000000..46d5e16c --- /dev/null +++ b/lib/SuplaDevice/extras/test/doubles/srpc_mock.cpp @@ -0,0 +1,42 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include "srpc_mock.h" + +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_channel_extendedvalue_changed( + void *_srpc, unsigned char channel_number, + TSuplaChannelExtendedValue *value) { + return 0; +} + +_supla_int_t SRPC_ICACHE_FLASH srpc_ds_async_channel_value_changed( + void *_srpc, unsigned char channel_number, char *value) { + std::vector vec(value, value + 8); + return SrpcInterface::instance->valueChanged(_srpc, channel_number, vec); +} + +SrpcInterface::SrpcInterface() { + instance = this; +} + +SrpcInterface::~SrpcInterface() { + instance = nullptr; +} + +SrpcInterface *SrpcInterface::instance = nullptr; + + diff --git a/lib/SuplaDevice/extras/test/doubles/srpc_mock.h b/lib/SuplaDevice/extras/test/doubles/srpc_mock.h new file mode 100644 index 00000000..1c7d677c --- /dev/null +++ b/lib/SuplaDevice/extras/test/doubles/srpc_mock.h @@ -0,0 +1,39 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _srpc_mock_h +#define _srpc_mock_h + +#include +#include +#include + +class SrpcInterface { + public: + SrpcInterface(); + virtual ~SrpcInterface(); + + virtual _supla_int_t valueChanged(void *srpc, unsigned char channelNumber, std::vector value) = 0; + + static SrpcInterface *instance; +}; + +class SrpcMock : public SrpcInterface { + public: + MOCK_METHOD(_supla_int_t, valueChanged, (void *, unsigned char, std::vector), (override)); +}; + +#endif diff --git a/lib/SuplaDevice/library.properties b/lib/SuplaDevice/library.properties index a1277c9e..87d96835 100644 --- a/lib/SuplaDevice/library.properties +++ b/lib/SuplaDevice/library.properties @@ -1,12 +1,11 @@ name=SuplaDevice -author=Przemyslaw Zygmunt -email=Przemyslaw Zygmunt +author=AC SOFTWARE SP. Z O.O. maintainer=Krzysztof Lewandowski -sentence=Library enables you to connect the device to SUPLA system. -paragraph=The Library supports Ethernet modules based on ENC28J60 and the popular Ethernet Shield based on W5100. Regarding the ENC28J60 chip, additional UIPEthernet library must be installed. WiFi interface on ESP8266 based devices is also supported. -url=https://www.supla.org +sentence=Library enables you to connect the device to the SUPLA automation system. +paragraph=It provides easy interface for adding various sensors, relays, buttons, roller shutters, etc. that can be controlled via SUPLA Cloud and application on mobile device. +url=https://github.com/SUPLA/arduino architectures=avr,esp32,esp8266 -version=2.3 +version=2.3.2 dependencies= core-dependencies=arduino (>=1.5.0) category=Communication diff --git a/lib/SuplaDevice/readme.md b/lib/SuplaDevice/readme.md new file mode 100644 index 00000000..a80f3eca --- /dev/null +++ b/lib/SuplaDevice/readme.md @@ -0,0 +1,248 @@ +# SUPLA project + +[SUPLA](https://www.supla.org) is an open source project for home automation. + +# SuplaDevice library for Arduino IDE + +SuplaDevice is a library for [Arduino IDE](https://www.arduino.cc/en/main/software) that allows to implement devices working with [Supla](https://www.supla.org). + +## Library installation + +There are few options how to install library in Arduino IDE. Here is one example: +1. Download SuplaDevice repository as a zip file (click green "Code" button on github repository) +2. Extract downloaded zip file +3. Copy whole SuplaDevice subfolder to a location where Arduino keeps libraries (in Arduino IDE open File->Preferences and there is a sketch location folder - libraries are kept in "libraries" subfolder) +4. You should be able to open SuplaDevice exmaples in Arduino IDE + +## Hardware requirements + +### Arduino Mega +SuplaDevice works with Arduino Mega boards. Currently Arduino Uno is not supported because of RAM limitations. It should work on other Arduino boards with at least 8 kB of RAM. +Following network interfaces are supported: +* Ethernet Shield with W5100 chipset +* ENC28J60 (not recommended - see Supported hardware section) + +Warning: WiFi shields are currently not supported + +### ESP8266 +ESP8266 boards are supported. Network connection is done via internal WiFi. Tested with ESP8266 boards 2.6.3. +Most probably it will work with other ESP8266 compatible boards. + +### ESP32 +Experimental support for ESP32 boards is provided. Some issues seen with reconnection to WiFi router which requires further analysis. + +## Installation + +Before you start, you will need to: +1. install Arduino IDE, +2. install support for your board +3. install driver for your USB to serial converter device (it can be external device, or build in on your board) +4. make sure that communication over serial interface with your board is working (i.e. check some example Arduino application) +5. download and install this librarary by copying SuplaDevice folder into your Arduino library folder + +Steps 1-4 are standard Arudino IDE setup procedures not related to SuplaDevice library. You can find many tutorials on Internet with detailed instructions. Tutorials doesn't have to be related in any way with Supla. + +After step 5 you should see Supla example applications in Arduino IDE examples. Select one and have fun! Example file requires adjustments before you compile them and upload to your board. Please read all comments in example and make proper adjustments. + +## Usage + +### Network interfaces +Supported network interfaces for Arduino Mega: +* Ethernet Shield - with W5100 chipset. Include `` and add `Supla::EthernetShield ethernet;` as a global variable. +* ENC28J60 - it requires additional UIPEthenet library (https://github.com/ntruchsess/arduino_uip). Include `` and +add `Supla::ENC28J60 ethernet;` as a global variable. Warning: network initialization on this library is blocking. In case of missing ENC28J60 board +or some other problem with network, program will stuck on initialization and will not work until connection is properly esablished. +Second warning: UIPEthernet library is consuming few hundred of bytes of RAM memory more, compared to standard Ethernet library. + +Supported network interface for ESP8266: +* There is a native WiFi controller. Include `` and add `Supla::ESPWifi wifi(ssid, password);` as a global variable and provide SSID and password in constructor. +Warning: by default connection with Supla server is encrypted. Default settings of SSL consumes big amount of RAM. +To disable SSL connection, use: + `wifi.enableSSL(false);` + + + +SSL certificate verification. +If you specify Supla's server certificate thumbprint there will be additional verification proceeded. Please use this method to configure fingerprint for validation: + `wifi.setServersCertFingerprint("9ba818295ec60652f8221500e15288d7a611177");' + + + +Supported network interface for ESP32: +* There is a native WiFi controller. Include `` and add `Supla::ESP32Wifi wifi(ssid, password);` as a global variable and provide SSID and password in constructor. + +### Exmaples + +Each example can run on Arduino Mega, ESP8266, or ESP32 board - unless mentioned otherwise in comments. Please read comments in example files and uncomment proper library for your network interface. + +SuplaSomfy - this example is not updated yet. + +### Folder structure + +* `supla-common` - Supla protocol definitions and structures. There are also methods to handle low level communication with Supla server, like message coding, decoding, sending and receiving. Those files are common with `supla-core` and the same code is run on multiple Supla platforms and services +* `supla/network` - implementation of network interfaces for supported boards +* `supla/sensor` - implementation of Supla sensor channels (thermometers, open/close sensors, etc.) +* `supla/control` - implementation of Supla control channels (various combinations of relays, buttons, action triggers) +* `supla/clock` - time services used in library (i.e. RTC) +* `supla` - all common classes are defined in main `supla` folder. You can find there classes that create framework on which all other components work. + +Some functions from above folders have dependencies to external libraries. Please check documentation included in header files. + +### How does it work? + +Everything that is visible in Supla (in Cloud, on API, mobile application) is called "channel". Supla channels are used to control relays, read temperature, check if garage door is open. + +SuplaDevice implements support for channels in `Channel` and `ChannelExtended` classes. Instances of those classes are part of objects called `Element` which are building blocks for any SuplaDevice application. + +All sensors, relays, buttons objects inherits from `Element` class. Each instance of such object will automatically register in SuplaDevice and proper virtual methods will be called by SuplaDevice in a specified way. + +All elements have to be constructed before `SuplaDevice.begin()` method is called. + +Supla channel number is assigned to each elemement with channel in an order of creation of objects. First channel will get number 0, second 1, etc. Supla server will not accept registration of device when order of channels is changed, or some channel is removed. In such case, you should remove device from Supla Cloud and register it again from scratch. + +`Element` class defines follwoing virtual methods that are called by SuplaDevice: +1. `onInit` - called first within `SuplaDevice.begin()` method. It should initialize your element. +2. `onLoadState` - called second within `SuplaDevice.begin()` method. It reads configuration data from persistent memory storage. +3. `onSaveState` - called in `SuplaDevice.iterate()` - it saves state data to persistant storage. It is not called on each iteration. `Storage` class makes sure that storing to memory does not happen to often and time delay between saves depends on implementation. +3. `iterateAlways` - called on each iteration of `SuplaDevice.iterate()` method, regardless of network/connection status. Be careful - some other methods called in `SuplaDevice.iterate()` method may block program execution for some time (even few seconds) - i.e. trying to establish connection with Supla server is blocking - in case server is not accessible, it will iterfere with `iterateAlways` method. So time critical functions should not be put here. +4. `iterateConnected` - called on each iterateion of `SuplaDevice.iterate()` method when device is connected and properly registered in Supla server. This method usually checks if there is some new data to be send to server (i.e. new temperature reading) and sends it. +5. `onTimer` - called every 10 ms after enabling in `SuplaDevice.begin()` +6. `onFastTimer` - called every 1 ms (0.5 ms in case of Arudino Mega) after enabling in `SuplaDevice.begin()` + +## How to migrate programs written in SuplaDevice libraray versions 1.6 and older + +For Arduino Mega applications include proper network interface header: +``` +// Choose proper network interface for your card: +// Arduino Mega with EthernetShield W5100: +#include +// Ethernet MAC address +uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; +Supla::EthernetShield ethernet(mac); +// +// Arduino Mega with ENC28J60: +// #include +// Supla::ENC28J60 ethernet(mac); + +``` + +For ESP8266 based applications include wifi header and provide WIFI SSID and password: +``` +// ESP8266 based board: +#include +Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +``` + +In case of ESP8266 remove all methods that defined network interface. They were usually added on the bottom of ino file, after end of loop() method, i.e.: +``` +// Supla.org ethernet layer + int supla_arduino_tcp_read(void *buf, int count) { +... +SuplaDeviceCallbacks supla_arduino_get_callbacks(void) { +... +``` + +Remove also those lines: +``` +#define SUPLADEVICE_CPP +WiFiClient client; +``` +You may also remove all WIFI related includes from ino file. + +Common instruction for all boards: + +If you use local IP address, please provide it in constructor of your network inteface class, i.e.: +``` +Supla::EthernetShield ethernet(mac, localIp); +``` + +After that go to SuplaDevice.begin() method. Old code looked like this +``` +SuplaDevice.begin(GUID, // Global Unique Identifier + mac, // Ethernet MAC address + "svr1.supla.org", // SUPLA server address + locationId, // Location ID + locationPassword); // Location Password +``` +This method requires now different set of parameters: +``` + SuplaDevice.begin(GUID, // Global Unique Identifier + "svr1.supla.org", // SUPLA server address + "mail@address.pl", // Email address + AUTHKEY); // Authentication key +``` +What is different? Well, GUID and Supla server address it the same as previously. MAC address, location ID, location password are removed. +MAC address was moved to network interface class. Location ID and password were replaced with new authentication method - via email address +and authentication key. You can generate your authentication key in the same way as GUID (it is actually in exactly the same format): +``` +// Generate AUTHKEY from https://www.supla.org/arduino/get-authkey +char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; +``` + +Next change is for custom digitalWrite and digitalRead methods. Those can be used to create virtual digital pins. Instead of adding custom +callback method that overrides digitalWrite/Read method, you should create a new class which inhertis from Supla::Io base class and define +your own customDigitalRead/Write methods. Here is short example (you can put this code in ino file, before setup()): +``` +#include + +class MyDigitalRead : public Supla::Io { + public: + int customDigitalRead(int channelNumber, uint8_t pin) { + if (channelNumber == MY_EXTRA_VIRTUAL_CHANNEL) { + return someCustomSourceOfData.getValue(); + } else { + return ::digitalRead(pin); + } + } +} + +MyDigitalRead instanceMyDigitalRead; +``` + +All channels from old version of library should be removed and created again in a new format. Please check instructions below how to add each type of channel. + +## Supported channels +### Sensors +Sensor category is for all elements/channels that reads something and provides data to Supla serwer. + + +### Control +Control category is for all elements/channels that are used to control something, i.e. relays, buttons, RGBW. + +## Supported persistant memory storage +Storage class is used as an abstraction for different persistant memory devices. Some elements/channels will not work properly without storage and some will have limitted functionalities. I.e. `ImpulseCounter` requires storage to save counter value, so it could be restored after reset, or `RollerShutter` requires storage to keep openin/closing times and current shutter possition. Currently two variants of storage classes are supported. +### EEPROM/Flash memory +EEPROM (in case of Arduino Mega) and Flash (in case of ESP) are build into most of boards. Usually writing should be safe for 100 000 write operations (on each bit). So in order to limit those operations, this kind of Storage will save data in longer time periods (every few minutes). Supla will not write data if there is no change. +``` +#include +Supla::Eeprom eeprom(SUPLA_STORAGE_OFFSET); +``` +Offset parameter is optional - use it if you already use saving to EEPROM in your application and you want SuplaDevice to use some other area of memory. + +### Adafruit FRAM SPI +FRAM is recommended for storage in Supla. It allows almost limitless writing cycles and it is very fast memory. +Currently only Adafruit FRAM SPI is supported. +``` +#include +// Hardware SPI +Supla::FramSpi fram(FRAM_CS, SUPLA_STORAGE_OFFSET); +``` +or with SW SPI: +``` +// Software SPI +Supla::FramSpi fram(SCK_PIN, MISO_PIN, MOSI_PIN, FRAM_CS, SUPLA_STORAGE_OFFSET); +``` + +## History + +Version 2.3.0 + + +## Credits + + +## License + + +Please check it [here](https://github.com/SUPLA/supla-cloud/blob/master/LICENSE). + diff --git a/lib/SuplaDevice/src/CMakeLists.txt b/lib/SuplaDevice/src/CMakeLists.txt new file mode 100644 index 00000000..55931cc9 --- /dev/null +++ b/lib/SuplaDevice/src/CMakeLists.txt @@ -0,0 +1,34 @@ +set(SRCS + supla/uptime.cpp + supla/channel.cpp + supla/channel_extended.cpp + supla/io.cpp + supla/tools.cpp + supla/element.cpp + supla/local_action.cpp + supla/channel_element.cpp + + supla/storage/storage.cpp + + supla/control/internal_pin_output.cpp + supla/control/pin_status_led.cpp + supla/control/rgbw_base.cpp + supla/control/rgb_base.cpp + supla/control/dimmer_base.cpp + supla/control/rgbw_leds.cpp + supla/control/rgb_leds.cpp + supla/control/dimmer_leds.cpp + supla/control/simple_button.cpp + + supla/condition.cpp + supla/conditions/on_less.cpp + supla/conditions/on_less_eq.cpp + supla/conditions/on_greater.cpp + supla/conditions/on_greater_eq.cpp + supla/conditions/on_between.cpp + supla/conditions/on_between_eq.cpp + supla/conditions/on_equal.cpp + +) + +add_library(supladevicelib SHARED ${SRCS}) diff --git a/lib/SuplaDevice/src/SuplaDevice.cpp b/lib/SuplaDevice/src/SuplaDevice.cpp index 210a909e..90cf5f43 100644 --- a/lib/SuplaDevice/src/SuplaDevice.cpp +++ b/lib/SuplaDevice/src/SuplaDevice.cpp @@ -94,22 +94,27 @@ bool SuplaDeviceClass::begin(unsigned char version) { // Supla::Storage::LoadDeviceConfig(); // Supla::Storage::LoadElementConfig(); - // Pefrorm dry run of write state to validate stored state section with current - // device configuration - Serial.println(F("Validating storage state section with current device configuration")); + // Pefrorm dry run of write state to validate stored state section with + // current device configuration + Serial.println( + F("Validating storage state section with current device configuration")); Supla::Storage::PrepareState(true); for (auto element = Supla::Element::begin(); element != nullptr; - element = element->next()) { + element = element->next()) { element->onSaveState(); + delay(0); } // If state storage validation was successful, perform read state if (Supla::Storage::FinalizeSaveState()) { - Serial.println(F("Storage state section validation completed. Loading elements state...")); + Serial.println( + F("Storage state section validation completed. Loading elements " + "state...")); // Iterate all elements and load state Supla::Storage::PrepareState(); for (auto element = Supla::Element::begin(); element != nullptr; - element = element->next()) { + element = element->next()) { element->onLoadState(); + delay(0); } } @@ -117,12 +122,12 @@ bool SuplaDeviceClass::begin(unsigned char version) { for (auto element = Supla::Element::begin(); element != nullptr; element = element->next()) { element->onInit(); + delay(0); } // Enable timers Supla::initTimers(); - bool emptyGuidDetected = true; for (int i = 0; i < SUPLA_GUID_SIZE; i++) { if (Supla::Channel::reg_dev.GUID[i] != 0) { @@ -244,6 +249,7 @@ void SuplaDeviceClass::iterate(void) { for (auto element = Supla::Element::begin(); element != nullptr; element = element->next()) { element->iterateAlways(); + delay(0); } // Iterate all elements and saves state @@ -252,6 +258,7 @@ void SuplaDeviceClass::iterate(void) { for (auto element = Supla::Element::begin(); element != nullptr; element = element->next()) { element->onSaveState(); + delay(0); } Supla::Storage::FinalizeSaveState(); } @@ -330,7 +337,6 @@ void SuplaDeviceClass::iterate(void) { if (!srpc_ds_async_registerdevice_e(srpc, &Supla::Channel::reg_dev)) { supla_log(LOG_DEBUG, "Fatal SRPC failure!"); } - Supla::Channel::clearAllUpdateReady(); } else if (registered == 1) { if (Supla::Network::Ping() == false) { @@ -347,6 +353,7 @@ void SuplaDeviceClass::iterate(void) { if (!element->iterateConnected(srpc)) { break; } + delay(0); } last_iterate_time = millis(); @@ -502,7 +509,8 @@ void SuplaDeviceClass::setServer(const char *server) { Supla::Channel::reg_dev.ServerName, server, SUPLA_SERVER_NAME_MAXSIZE); } -void SuplaDeviceClass::onGetUserLocaltimeResult(TSDC_UserLocalTimeResult *result) { +void SuplaDeviceClass::onGetUserLocaltimeResult( + TSDC_UserLocalTimeResult *result) { if (clock) { clock->parseLocaltimeFromServer(result); } @@ -513,7 +521,7 @@ void SuplaDeviceClass::addClock(Supla::Clock *_clock) { clock = _clock; } -Supla::Clock * SuplaDeviceClass::getClock() { +Supla::Clock *SuplaDeviceClass::getClock() { return clock; } diff --git a/lib/SuplaDevice/src/SuplaDevice.h b/lib/SuplaDevice/src/SuplaDevice.h index 5c1067fe..0057105c 100644 --- a/lib/SuplaDevice/src/SuplaDevice.h +++ b/lib/SuplaDevice/src/SuplaDevice.h @@ -17,8 +17,6 @@ #ifndef SUPLADEVICE_H #define SUPLADEVICE_H -#include - #include "supla-common/proto.h" #include "supla/network/network.h" #include "supla/uptime.h" diff --git a/lib/SuplaDevice/src/supla-common/IEEE754tools.h b/lib/SuplaDevice/src/supla-common/IEEE754tools.h index 5a42beec..c38cfbc1 100644 --- a/lib/SuplaDevice/src/supla-common/IEEE754tools.h +++ b/lib/SuplaDevice/src/supla-common/IEEE754tools.h @@ -13,13 +13,6 @@ #ifndef IEEE754tools_h #define IEEE754tools_h - -#if defined(ARDUINO) && ARDUINO >= 100 -#include "Arduino.h" -#else -#include "WProgram.h" -#endif - // IEEE754 float layout; struct IEEEfloat { diff --git a/lib/SuplaDevice/src/supla/action_handler.h b/lib/SuplaDevice/src/supla/action_handler.h new file mode 100644 index 00000000..b057371f --- /dev/null +++ b/lib/SuplaDevice/src/supla/action_handler.h @@ -0,0 +1,32 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _action_handler_h +#define _action_handler_h + +namespace Supla { +class ActionHandler { + public: + virtual ~ActionHandler() {}; + virtual void handleAction(int event, int action) = 0; + virtual bool deleteClient() { + return false; + } +}; + +}; + +#endif diff --git a/lib/SuplaDevice/src/supla/actions.h b/lib/SuplaDevice/src/supla/actions.h index e44a2647..eac71d00 100644 --- a/lib/SuplaDevice/src/supla/actions.h +++ b/lib/SuplaDevice/src/supla/actions.h @@ -17,9 +17,9 @@ #ifndef _actions_h #define _actions_h -// Actions are used in triggerable elements. They are grouped by most common +// Actions are used in ActionHandler elements. They are grouped by most common // usage, but you should not rely on it. Please check exact supported actions -// in triggerable element documentation +// in ActionHandler's element documentation namespace Supla { enum Action { diff --git a/lib/SuplaDevice/src/supla/channel.cpp b/lib/SuplaDevice/src/supla/channel.cpp index b6c9a658..65a3ea03 100644 --- a/lib/SuplaDevice/src/supla/channel.cpp +++ b/lib/SuplaDevice/src/supla/channel.cpp @@ -14,13 +14,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include + #include "supla/channel.h" #include "supla-common/log.h" #include "supla-common/srpc.h" #include "tools.h" +#include "events.h" namespace Supla { -Channel *Channel::firstPtr = nullptr; unsigned long Channel::lastCommunicationTimeMs = 0; TDS_SuplaRegisterDevice_E Channel::reg_dev; @@ -30,44 +32,20 @@ Channel::Channel() { channelNumber = -1; if (reg_dev.channel_count < SUPLA_CHANNELMAXCOUNT) { channelNumber = reg_dev.channel_count; + + memset(®_dev.channels[channelNumber], 0, sizeof(reg_dev.channels[channelNumber])); reg_dev.channels[channelNumber].Number = channelNumber; + reg_dev.channel_count++; } else { // TODO: add status CHANNEL_LIMIT_EXCEEDED } - if (firstPtr == nullptr) { - firstPtr = this; - } else { - last()->nextPtr = this; - } - nextPtr = nullptr; setFlag(SUPLA_CHANNEL_FLAG_CHANNELSTATE); } -Channel *Channel::begin() { - return firstPtr; -} - -Channel *Channel::last() { - Channel *ptr = firstPtr; - while (ptr && ptr->nextPtr) { - ptr = ptr->nextPtr; - } - return ptr; -} - -int Channel::size() { - int count = 0; - Channel *ptr = firstPtr; - if (ptr) { - count++; - } - while (ptr->nextPtr) { - count++; - ptr = ptr->nextPtr; - } - return count; +Channel::~Channel() { + reg_dev.channel_count--; } void Channel::setNewValue(double dbl) { @@ -78,19 +56,23 @@ void Channel::setNewValue(double dbl) { float2DoublePacked(dbl, (uint8_t *)(newValue)); } if (setNewValue(newValue)) { - supla_log(LOG_DEBUG, "Channel(%d) value changed to %f", channelNumber, dbl); + runAction(ON_CHANGE); + runAction(ON_SECONDARY_CHANNEL_CHANGE); + supla_log(LOG_DEBUG, "Channel(%d) value changed to %d.%d", channelNumber, static_cast(dbl), static_cast(dbl*100)%100); } } void Channel::setNewValue(double temp, double humi) { char newValue[SUPLA_CHANNELVALUE_SIZE]; - long t = temp * 1000.00; - long h = humi * 1000.00; + _supla_int_t t = temp * 1000.00; + _supla_int_t h = humi * 1000.00; memcpy(newValue, &t, 4); memcpy(&(newValue[4]), &h, 4); if (setNewValue(newValue)) { + runAction(ON_CHANGE); + runAction(ON_SECONDARY_CHANNEL_CHANGE); supla_log(LOG_DEBUG, "Channel(%d) value changed to temp(%f), humi(%f)", channelNumber, @@ -106,18 +88,22 @@ void Channel::setNewValue(_supla_int64_t value) { memcpy(newValue, &value, sizeof(_supla_int64_t)); if (setNewValue(newValue)) { + runAction(ON_CHANGE); + runAction(ON_SECONDARY_CHANNEL_CHANGE); supla_log( LOG_DEBUG, "Channel(%d) value changed to %d", channelNumber, static_cast(value)); } } -void Channel::setNewValue(int value) { +void Channel::setNewValue(_supla_int_t value) { char newValue[SUPLA_CHANNELVALUE_SIZE]; memset(newValue, 0, SUPLA_CHANNELVALUE_SIZE); - memcpy(newValue, &value, sizeof(int)); + memcpy(newValue, &value, sizeof(value)); if (setNewValue(newValue)) { + runAction(ON_CHANGE); + runAction(ON_SECONDARY_CHANNEL_CHANGE); supla_log( LOG_DEBUG, "Channel(%d) value changed to %d", channelNumber, value); } @@ -130,6 +116,14 @@ void Channel::setNewValue(bool value) { newValue[0] = value; if (setNewValue(newValue)) { + if (value) { + runAction(Supla::ON_TURN_ON); + } else { + runAction(Supla::ON_TURN_OFF); + } + runAction(Supla::ON_CHANGE); + runAction(ON_SECONDARY_CHANNEL_CHANGE); + supla_log( LOG_DEBUG, "Channel(%d) value changed to %d", channelNumber, value); } @@ -235,18 +229,6 @@ TSuplaChannelExtendedValue *Channel::getExtValue() { return nullptr; } -void Channel::clearAllUpdateReady() { - for (auto channel = begin(); channel != nullptr; channel = channel->next()) { - if (!channel->isExtended()) { - channel->clearUpdateReady(); - } - } -} - -Channel *Channel::next() { - return nextPtr; -} - void Channel::setUpdateReady() { valueChanged = true; }; @@ -272,6 +254,8 @@ void Channel::setNewValue(uint8_t red, newValue[3] = green; newValue[4] = red; if (setNewValue(newValue)) { + runAction(ON_CHANGE); + runAction(ON_SECONDARY_CHANNEL_CHANGE); supla_log(LOG_DEBUG, "Channel(%d) value changed to RGB(%d, %d, %d), colBr(%d), bright(%d)", channelNumber, red, green, blue, colorBrightness, brightness); } } @@ -283,4 +267,66 @@ _supla_int_t Channel::getChannelType() { return -1; } +double Channel::getValueDouble() { + double value; + if (sizeof(double) == 8) { + memcpy(&value, reg_dev.channels[channelNumber].value, 8); + } else if (sizeof(double) == 4) { + value = doublePacked2float((uint8_t *)(reg_dev.channels[channelNumber].value)); + } + + return value; +} + +double Channel::getValueDoubleFirst() { + _supla_int_t value; + memcpy(&value, reg_dev.channels[channelNumber].value, 4); + + return value / 1000.0; +} + +double Channel::getValueDoubleSecond() { + _supla_int_t value; + memcpy(&value, &(reg_dev.channels[channelNumber].value[4]), 4); + + return value / 1000.0; +} + +_supla_int_t Channel::getValueInt32() { + _supla_int_t value; + memcpy(&value, reg_dev.channels[channelNumber].value, sizeof(value)); + return value; +} + +_supla_int64_t Channel::getValueInt64() { + _supla_int64_t value; + memcpy(&value, reg_dev.channels[channelNumber].value, sizeof(value)); + return value; +} + +bool Channel::getValueBool() { + return reg_dev.channels[channelNumber].value[0]; +} + +uint8_t Channel::getValueRed() { + return reg_dev.channels[channelNumber].value[4]; +} + +uint8_t Channel::getValueGreen() { + return reg_dev.channels[channelNumber].value[3]; +} + +uint8_t Channel::getValueBlue() { + return reg_dev.channels[channelNumber].value[2]; +} + +uint8_t Channel::getValueColorBrightness() { + return reg_dev.channels[channelNumber].value[1]; +} + +uint8_t Channel::getValueBrightness() { + return reg_dev.channels[channelNumber].value[0]; +} + + }; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/channel.h b/lib/SuplaDevice/src/supla/channel.h index c27b68a5..453d6f88 100644 --- a/lib/SuplaDevice/src/supla/channel.h +++ b/lib/SuplaDevice/src/supla/channel.h @@ -19,21 +19,19 @@ #include -#include "../supla-common/proto.h" +#include "supla-common/proto.h" +#include "local_action.h" namespace Supla { -class Channel { +class Channel : public LocalAction { public: Channel(); - - static Channel *begin(); - static Channel *last(); - static int size(); + ~Channel(); void setNewValue(double dbl); void setNewValue(double temp, double humi); - void setNewValue(int value); + void setNewValue(_supla_int_t value); void setNewValue(bool value); void setNewValue(TElectricityMeter_ExtendedValue_V2 &emValue); void setNewValue(uint8_t red, @@ -44,6 +42,18 @@ class Channel { void setNewValue(_supla_int64_t value); bool setNewValue(char *newValue); + double getValueDouble(); + double getValueDoubleFirst(); + double getValueDoubleSecond(); + _supla_int_t getValueInt32(); + _supla_int64_t getValueInt64(); + bool getValueBool(); + uint8_t getValueRed(); + uint8_t getValueGreen(); + uint8_t getValueBlue(); + uint8_t getValueColorBrightness(); + uint8_t getValueBrightness(); + virtual bool isExtended(); bool isUpdateReady(); int getChannelNumber(); @@ -57,8 +67,6 @@ class Channel { void clearUpdateReady(); void sendUpdate(void *srpc); virtual TSuplaChannelExtendedValue *getExtValue(); - static void clearAllUpdateReady(); - Channel *next(); static unsigned long lastCommunicationTimeMs; static TDS_SuplaRegisterDevice_E reg_dev; @@ -69,8 +77,6 @@ class Channel { bool valueChanged; int channelNumber; - Channel *nextPtr; - static Channel *firstPtr; }; }; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/channel_element.cpp b/lib/SuplaDevice/src/supla/channel_element.cpp new file mode 100644 index 00000000..f9853a62 --- /dev/null +++ b/lib/SuplaDevice/src/supla/channel_element.cpp @@ -0,0 +1,41 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "channel_element.h" +#include "events.h" + +Supla::Channel *Supla::ChannelElement::getChannel() { + return &channel; +} + +void Supla::ChannelElement::addAction(int action, ActionHandler &client, int event) { + channel.addAction(action, client, event); +} + +void Supla::ChannelElement::addAction(int action, ActionHandler *client, int event) { + addAction(action, *client, event); +} + +void Supla::ChannelElement::addAction(int action, ActionHandler &client, Supla::Condition *condition) { + condition->setClient(client); + condition->setSource(this); + channel.addAction(action, condition, Supla::ON_CHANGE); +} + +void Supla::ChannelElement::addAction(int action, ActionHandler *client, Supla::Condition *condition) { + addAction(action, *client, condition); +} + diff --git a/lib/SuplaDevice/src/supla/channel_element.h b/lib/SuplaDevice/src/supla/channel_element.h new file mode 100644 index 00000000..ae4b0256 --- /dev/null +++ b/lib/SuplaDevice/src/supla/channel_element.h @@ -0,0 +1,49 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _channel_element_h +#define _channel_element_h + +#include "element.h" +#include "channel.h" +#include "local_action.h" +#include "action_handler.h" +#include "condition.h" + +namespace Supla { + +class Condition; + +class ChannelElement : public Element, public LocalAction { + public: + + Channel *getChannel(); + + // Override local action methods in order to delegate execution to Channel + void addAction(int action, ActionHandler &client, int event); + void addAction(int action, ActionHandler *client, int event); + + virtual void addAction(int action, ActionHandler &client, Supla::Condition *condition); + virtual void addAction(int action, ActionHandler *client, Supla::Condition *condition); + + protected: + Channel channel; +}; + +}; + +#endif + diff --git a/lib/SuplaDevice/src/supla/condition.cpp b/lib/SuplaDevice/src/supla/condition.cpp new file mode 100644 index 00000000..b7a04e5a --- /dev/null +++ b/lib/SuplaDevice/src/supla/condition.cpp @@ -0,0 +1,92 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "condition.h" +#include "events.h" + +Supla::Condition::Condition(double threshold, bool useAlternativeMeasurement) + : threshold(threshold), + useAlternativeMeasurement(useAlternativeMeasurement), + alreadyFired(false) { +} + +Supla::Condition::~Condition() { +} + +void Supla::Condition::handleAction(int event, int action) { + if (event == Supla::ON_CHANGE || + event == Supla::ON_SECONDARY_CHANNEL_CHANGE) { + int channelType = source->getChannel()->getChannelType(); + double value = 0; + switch (channelType) { + case SUPLA_CHANNELTYPE_DISTANCESENSOR: + case SUPLA_CHANNELTYPE_THERMOMETER: + case SUPLA_CHANNELTYPE_WINDSENSOR: + case SUPLA_CHANNELTYPE_PRESSURESENSOR: + case SUPLA_CHANNELTYPE_RAINSENSOR: + case SUPLA_CHANNELTYPE_WEIGHTSENSOR: + value = source->getChannel()->getValueDouble(); + break; + case SUPLA_CHANNELTYPE_IMPULSE_COUNTER: + value = source->getChannel()->getValueInt64(); + break; + case SUPLA_CHANNELTYPE_HUMIDITYANDTEMPSENSOR: + value = useAlternativeMeasurement + ? source->getChannel()->getValueDoubleSecond() + : source->getChannel()->getValueDoubleFirst(); + break; + default: + return; + } + if (checkConditionFor(value)) { + client->handleAction(event, action); + } + } +} + +// Condition objects will be deleted during ActionHandlerClient list cleanup +bool Supla::Condition::deleteClient() { + return true; +} + +bool Supla::Condition::checkConditionFor(double val) { + if (!alreadyFired && condition(val)) { + alreadyFired = true; + return true; + } + if (alreadyFired) { + if (!condition(val)) { + alreadyFired = false; + } + } + return false; +} + +void Supla::Condition::setSource(Supla::ChannelElement *src) { + source = src; +} + +void Supla::Condition::setClient(Supla::ActionHandler *clientPtr) { + client = clientPtr; +} + +void Supla::Condition::setSource(Supla::ChannelElement &src) { + setSource(&src); +} + +void Supla::Condition::setClient(Supla::ActionHandler &clientPtr) { + setClient(&clientPtr); +} diff --git a/lib/SuplaDevice/src/supla/condition.h b/lib/SuplaDevice/src/supla/condition.h new file mode 100644 index 00000000..d5d31f90 --- /dev/null +++ b/lib/SuplaDevice/src/supla/condition.h @@ -0,0 +1,62 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _condition_h +#define _condition_h + +#include "action_handler.h" +#include "channel_element.h" + + +namespace Supla { + +class ChannelElement; + +class Condition : public ActionHandler { + public: + Condition(double threshold, bool useAlternativeMeasurement); + virtual ~Condition(); + void setSource(ChannelElement *src); + void setClient(ActionHandler *clientPtr); + void setSource(ChannelElement &src); + void setClient(ActionHandler &clientPtr); + + void handleAction(int event, int action); + bool deleteClient(); + virtual bool checkConditionFor(double val); + + protected: + virtual bool condition(double val) = 0; + + double threshold; + bool alreadyFired; + bool useAlternativeMeasurement; + Supla::ChannelElement *source; + Supla::ActionHandler *client; + +}; + +}; + +Supla::Condition *OnLess(double threshold, bool useAlternativeMeasurement = false); +Supla::Condition *OnLessEq(double threshold, bool useAlternativeMeasurement = false); +Supla::Condition *OnGreater(double threshold, bool useAlternativeMeasurement = false); +Supla::Condition *OnGreaterEq(double threshold, bool useAlternativeMeasurement = false); +Supla::Condition *OnBetween(double threshold1, double threshold2, bool useAlternativeMeasurement = false); +Supla::Condition *OnBetweenEq(double threshold1, double threshold2, bool useAlternativeMeasurement = false); +Supla::Condition *OnEqual(double threshold, bool useAlternativeMeasurement = false); + +#endif diff --git a/lib/SuplaDevice/src/supla/conditions/on_between.cpp b/lib/SuplaDevice/src/supla/conditions/on_between.cpp new file mode 100644 index 00000000..9a1ef83e --- /dev/null +++ b/lib/SuplaDevice/src/supla/conditions/on_between.cpp @@ -0,0 +1,38 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "../condition.h" + +class OnBetweenCond : public Supla::Condition { + public: + OnBetweenCond(double threshold1, double threshold2, bool useAlternativeMeasurement) + : Supla::Condition(threshold1, useAlternativeMeasurement), threshold2(threshold2) { + } + + bool condition(double val) { + return val > threshold && val < threshold2; + } + + double threshold2; +}; + + +Supla::Condition *OnBetween(double threshold1, double threshold2, bool useAlternativeMeasurement) { + return new OnBetweenCond(threshold1, threshold2, useAlternativeMeasurement); +} + + + diff --git a/lib/SuplaDevice/src/supla/conditions/on_between_eq.cpp b/lib/SuplaDevice/src/supla/conditions/on_between_eq.cpp new file mode 100644 index 00000000..8e535db1 --- /dev/null +++ b/lib/SuplaDevice/src/supla/conditions/on_between_eq.cpp @@ -0,0 +1,39 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "../condition.h" + +class OnBetweenEqCond : public Supla::Condition { + public: + OnBetweenEqCond(double threshold1, double threshold2, bool useAlternativeMeasurement) + : Supla::Condition(threshold1, useAlternativeMeasurement), threshold2(threshold2) { + } + + bool condition(double val) { + return val >= threshold && val <= threshold2; + } + + double threshold2; +}; + + +Supla::Condition *OnBetweenEq(double threshold1, double threshold2, bool useAlternativeMeasurement) { + return new OnBetweenEqCond(threshold1, threshold2, useAlternativeMeasurement); +} + + + + diff --git a/lib/SuplaDevice/src/supla/conditions/on_equal.cpp b/lib/SuplaDevice/src/supla/conditions/on_equal.cpp new file mode 100644 index 00000000..1d7e6d25 --- /dev/null +++ b/lib/SuplaDevice/src/supla/conditions/on_equal.cpp @@ -0,0 +1,36 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "../condition.h" + +class OnEqualCond : public Supla::Condition { + public: + OnEqualCond(double threshold, bool useAlternativeMeasurement) + : Supla::Condition(threshold, useAlternativeMeasurement) { + } + + bool condition(double val) { + return val == threshold; + } +}; + + +Supla::Condition *OnEqual(double threshold, bool useAlternativeMeasurement) { + return new OnEqualCond(threshold, useAlternativeMeasurement); +} + + + diff --git a/lib/SuplaDevice/src/supla/conditions/on_greater.cpp b/lib/SuplaDevice/src/supla/conditions/on_greater.cpp new file mode 100644 index 00000000..675a667f --- /dev/null +++ b/lib/SuplaDevice/src/supla/conditions/on_greater.cpp @@ -0,0 +1,36 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "../condition.h" + +class OnGreaterCond : public Supla::Condition { + public: + OnGreaterCond(double threshold, bool useAlternativeMeasurement) + : Supla::Condition(threshold, useAlternativeMeasurement) { + } + + bool condition(double val) { + return val > threshold; + } +}; + + +Supla::Condition *OnGreater(double threshold, bool useAlternativeMeasurement) { + return new OnGreaterCond(threshold, useAlternativeMeasurement); +} + + + diff --git a/lib/SuplaDevice/src/supla/conditions/on_greater_eq.cpp b/lib/SuplaDevice/src/supla/conditions/on_greater_eq.cpp new file mode 100644 index 00000000..37aaf920 --- /dev/null +++ b/lib/SuplaDevice/src/supla/conditions/on_greater_eq.cpp @@ -0,0 +1,36 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "../condition.h" + +class OnGreaterEqCond : public Supla::Condition { + public: + OnGreaterEqCond(double threshold, bool useAlternativeMeasurement) + : Supla::Condition(threshold, useAlternativeMeasurement) { + } + + bool condition(double val) { + return val >= threshold; + } +}; + + +Supla::Condition *OnGreaterEq(double threshold, bool useAlternativeMeasurement) { + return new OnGreaterEqCond(threshold, useAlternativeMeasurement); +} + + + diff --git a/lib/SuplaDevice/src/supla/conditions/on_less.cpp b/lib/SuplaDevice/src/supla/conditions/on_less.cpp new file mode 100644 index 00000000..37541f52 --- /dev/null +++ b/lib/SuplaDevice/src/supla/conditions/on_less.cpp @@ -0,0 +1,34 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "../condition.h" + +class OnLessCond : public Supla::Condition { + public: + OnLessCond(double threshold, bool useAlternativeMeasurement) + : Supla::Condition(threshold, useAlternativeMeasurement) { + } + + bool condition(double val) { + return val < threshold; + } +}; + + +Supla::Condition *OnLess(double threshold, bool useAlternativeMeasurement) { + return new OnLessCond(threshold, useAlternativeMeasurement); +} + diff --git a/lib/SuplaDevice/src/supla/conditions/on_less_eq.cpp b/lib/SuplaDevice/src/supla/conditions/on_less_eq.cpp new file mode 100644 index 00000000..359e8058 --- /dev/null +++ b/lib/SuplaDevice/src/supla/conditions/on_less_eq.cpp @@ -0,0 +1,35 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "../condition.h" + +class OnLessEqCond : public Supla::Condition { + public: + OnLessEqCond(double threshold, bool useAlternativeMeasurement) + : Supla::Condition(threshold, useAlternativeMeasurement) { + } + + bool condition(double val) { + return val <= threshold; + } +}; + + +Supla::Condition *OnLessEq(double threshold, bool useAlternativeMeasurement) { + return new OnLessEqCond(threshold, useAlternativeMeasurement); +} + + diff --git a/lib/SuplaDevice/src/supla/control/MCP23017/S_MCP23017.cpp b/lib/SuplaDevice/src/supla/control/MCP23017/S_MCP23017.cpp new file mode 100644 index 00000000..af9a21c3 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/MCP23017/S_MCP23017.cpp @@ -0,0 +1,205 @@ +#include +#include +#include "S_MCP23017.h" + +static const uint8_t MCP23017_BASEADDRESS = 0x20; + +static const uint8_t MCP23017_IODIRA = 0x00; +static const uint8_t MCP23017_IODIRB = 0x01; +static const uint8_t MCP23017_IPOLA = 0x02; +static const uint8_t MCP23017_IPOLB = 0x03; +static const uint8_t MCP23017_GPINTENA = 0x04; +static const uint8_t MCP23017_GPINTENB = 0x05; +static const uint8_t MCP23017_DEFVALA = 0x06; +static const uint8_t MCP23017_DEFVALB = 0x07; +static const uint8_t MCP23017_INTCONA = 0x08; +static const uint8_t MCP23017_INTCONB = 0x09; +static const uint8_t MCP23017_IOCONA = 0x0A; +static const uint8_t MCP23017_IOCONB = 0x0B; +static const uint8_t MCP23017_GPPUA = 0x0C; +static const uint8_t MCP23017_GPPUB = 0x0D; +static const uint8_t MCP23017_INTFA = 0x0E; +static const uint8_t MCP23017_INTFB = 0x0F; +static const uint8_t MCP23017_INTCAPA = 0x10; +static const uint8_t MCP23017_INTCAPB = 0x11; +static const uint8_t MCP23017_GPIOA = 0x12; +static const uint8_t MCP23017_GPIOB = 0x13; +static const uint8_t MCP23017_OLATA = 0x14; +static const uint8_t MCP23017_OLATB = 0x15; + +#ifdef ESP8266 +void MCP23017::init(uint8_t sda, uint8_t scl, bool fast) { + Wire.begin(sda, scl); + if (fast) + Wire.setClock(400000); +} +#else +void MCP23017::init(bool fast) { + Wire.begin(); + if (fast) + Wire.setClock(400000); +} +#endif + +bool MCP23017::begin(uint8_t address) { + _address = MCP23017_BASEADDRESS | (address & 0x07); + return (writeReg16(MCP23017_IOCONA, 0x4242) && writeReg16(MCP23017_IODIRA, 0xFFFF)); // INT MIRROR & INT POL HIGH, ALL INPUTS +} + +void MCP23017::pinMode(uint8_t pin, uint8_t mode) { + if (pin < 16) { + if (mode == OUTPUT) { + updateReg(pin < 8 ? MCP23017_IODIRA : MCP23017_IODIRB, ~(uint8_t)(1 << (pin % 8)), 0x00); + } else if ((mode == INPUT) || (mode == INPUT_PULLUP)) { + updateReg(pin < 8 ? MCP23017_IODIRA : MCP23017_IODIRB, 0xFF, 1 << (pin % 8)); + if (mode == INPUT_PULLUP) { + updateReg(pin < 8 ? MCP23017_GPPUA : MCP23017_GPPUB, 0xFF, 1 << (pin % 8)); + updateReg(pin < 8 ? MCP23017_IPOLA : MCP23017_IPOLB, 0xFF, 1 << (pin % 8)); + } else { + updateReg(pin < 8 ? MCP23017_GPPUA : MCP23017_GPPUB, ~(uint8_t)(1 << (pin % 8)), 0x00); + updateReg(pin < 8 ? MCP23017_IPOLA : MCP23017_IPOLB, ~(uint8_t)(1 << (pin % 8)), 0x00); + } + } + } +} + +void MCP23017::setPullup(uint8_t pin, bool pullup, bool inverse) { + if (pin < 16) { + if (pullup) + updateReg(pin < 8 ? MCP23017_GPPUA : MCP23017_GPPUB, 0xFF, 1 << (pin % 8)); + else + updateReg(pin < 8 ? MCP23017_GPPUA : MCP23017_GPPUB, ~(uint8_t)(1 << (pin % 8)), 0x00); + if (inverse) + updateReg(pin < 8 ? MCP23017_IPOLA : MCP23017_IPOLB, 0xFF, 1 << (pin % 8)); + else + updateReg(pin < 8 ? MCP23017_IPOLA : MCP23017_IPOLB, ~(uint8_t)(1 << (pin % 8)), 0x00); + } +} + +bool MCP23017::digitalRead(uint8_t pin) { + unsigned long now = millis(); + if (now > get_ba){ + ba = readReg16(MCP23017_GPIOA); // --------- reads "readReg16" only once every 20 ms ---- + get_ba = now + 20; + } + if (pin < 16) { + return ((ba >> (pin % 16)) & 0x01); + } +} + +void MCP23017::digitalWrite(uint8_t pin, bool value) { + if (pin < 16) { + if (value) + updateReg(pin < 8 ? MCP23017_GPIOA : MCP23017_GPIOB, 0xFF, 1 << (pin % 8)); + else + updateReg(pin < 8 ? MCP23017_GPIOA : MCP23017_GPIOB, ~(uint8_t)(1 << (pin % 8)), 0x00); + } +} + +uint16_t MCP23017::digitalReads() { + return readReg16(MCP23017_GPIOA); +} + +void MCP23017::digitalWrites(uint16_t values) { + writeReg16(MCP23017_GPIOA, values); +} + +void MCP23017::attachInterrupt(uint8_t pin, callback_t callback) { + _callback = callback; + ::pinMode(pin, INPUT); + ::attachInterrupt(digitalPinToInterrupt(pin), std::bind(&MCP23017::_interrupt, this), RISING); +} + +void MCP23017::detachInterrupt(uint8_t pin) { + ::detachInterrupt(digitalPinToInterrupt(pin)); + _callback = NULL; +} + +void MCP23017::setupInterrupt(uint8_t pin, bool enable) { + if (pin < 16) { + if (enable) + updateReg(pin < 8 ? MCP23017_GPINTENA : MCP23017_GPINTENB, 0xFF, 1 << (pin % 8)); + else + updateReg(pin < 8 ? MCP23017_GPINTENA : MCP23017_GPINTENB, ~(uint8_t)(1 << (pin % 8)), 0x00); + } +} + +void MCP23017::setupInterrupts(uint16_t pins, bool enable) { + if (enable) + updateReg16(MCP23017_GPINTENA, 0xFFFF, pins); + else + updateReg16(MCP23017_GPINTENA, ~pins, 0x00); +} + +bool MCP23017::writeReg(uint8_t reg, uint8_t value) { + Wire.beginTransmission(_address); + Wire.write(reg); + Wire.write(value); + return (Wire.endTransmission() == 0); +} + +bool MCP23017::writeReg16(uint8_t reg, uint16_t value) { + Wire.beginTransmission(_address); + Wire.write(reg); + Wire.write(value & 0xFF); + Wire.write(value >> 8); + return (Wire.endTransmission() == 0); +} + +uint8_t MCP23017::readReg(uint8_t reg) { + Wire.beginTransmission(_address); + Wire.write(reg); + if (Wire.endTransmission() != 0) + return 0; // Error! + Wire.requestFrom(_address, (uint8_t)1); + return Wire.read(); +} + +uint16_t MCP23017::readReg16(uint8_t reg) { + Wire.beginTransmission(_address); + Wire.write(reg); + if (Wire.endTransmission() != 0) + return 0; // Error! + Wire.requestFrom(_address, (uint8_t)2); + uint8_t a = Wire.read(); + return ((Wire.read() << 8) | a); +} + +bool MCP23017::updateReg(uint8_t reg, uint8_t andMask, uint8_t orMask) { + Wire.beginTransmission(_address); + Wire.write(reg); + if (Wire.endTransmission() != 0) + return false; // Error! + Wire.requestFrom(_address, (uint8_t)1); + uint8_t a = (Wire.read() & andMask) | orMask; + Wire.beginTransmission(_address); + Wire.write(reg); + Wire.write(a); + return (Wire.endTransmission() == 0); +} + +bool MCP23017::updateReg16(uint8_t reg, uint16_t andMask, uint16_t orMask) { + Wire.beginTransmission(_address); + Wire.write(reg); + if (Wire.endTransmission() != 0) + return false; // Error! + Wire.requestFrom(_address, (uint8_t)2); + uint16_t ab = Wire.read(); + ab |= (Wire.read() << 8); + ab &= andMask; + ab |= orMask; + Wire.beginTransmission(_address); + Wire.write(reg); + Wire.write(ab & 0xFF); + Wire.write(ab >> 8); + return (Wire.endTransmission() == 0); +} + +void IRAM_ATTR MCP23017::_interrupt() { + uint16_t pins, values; + + pins = readReg16(MCP23017_INTFA); + values = readReg16(MCP23017_INTCAPA); + if (_callback) + _callback(pins, values); +} diff --git a/lib/SuplaDevice/src/supla/control/MCP23017/S_MCP23017.h b/lib/SuplaDevice/src/supla/control/MCP23017/S_MCP23017.h new file mode 100644 index 00000000..ef08791e --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/MCP23017/S_MCP23017.h @@ -0,0 +1,52 @@ +#ifndef _S_MCP23017_H +#define _S_MCP23017_H + +#include +#include + +class MCP23017 { +public: + typedef std::function callback_t; + + MCP23017() : _callback(NULL) {} + +#ifdef ESP8266 + static void init(uint8_t sda, uint8_t scl, bool fast = true); + static void init(bool fast = true) { + init(SDA, SCL, fast); + } +#else + static void init(bool fast = true); +#endif + + bool begin(uint8_t address = 0); + + void pinMode(uint8_t pin, uint8_t mode); + void setPullup(uint8_t pin, bool pullup, bool inverse = false); + bool digitalRead(uint8_t pin); + void digitalWrite(uint8_t pin, bool value); + uint16_t digitalReads(); + void digitalWrites(uint16_t values); + + void attachInterrupt(uint8_t pin, callback_t callback); + void detachInterrupt(uint8_t pin); + void setupInterrupt(uint8_t pin, bool enable = true); + void setupInterrupts(uint16_t pins, bool enable = true); + +protected: + bool writeReg(uint8_t reg, uint8_t value); + bool writeReg16(uint8_t reg, uint16_t value); + uint8_t readReg(uint8_t reg); + uint16_t readReg16(uint8_t reg); + bool updateReg(uint8_t reg, uint8_t andMask, uint8_t orMask); + bool updateReg16(uint8_t reg, uint16_t andMask, uint16_t orMask); + + void _interrupt(); + + uint8_t _address; + uint16_t ba = 0; + unsigned long get_ba = 0; + callback_t _callback; +}; + +#endif diff --git a/lib/SuplaDevice/src/supla/control/MCP23017/examples/Ejemplo_Mcp23017_io/Ejemplo_Mcp23017_io.ino b/lib/SuplaDevice/src/supla/control/MCP23017/examples/Ejemplo_Mcp23017_io/Ejemplo_Mcp23017_io.ino new file mode 100644 index 00000000..dd4ee2be --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/MCP23017/examples/Ejemplo_Mcp23017_io/Ejemplo_Mcp23017_io.ino @@ -0,0 +1,189 @@ +#define supla_lib_config_h_ // silences unnecessary debug messages "should be disabled by default" +#include +#include +#include +#include + +// +// ESP8266 based board: +#include +Supla::ESPWifi wifi("your_wifi_ssid", "your_wifi_password"); +// + +void setup() { + + Serial.begin(115200); + + mcp1.init(4, 5); // init(uint8_t sda, uint8_t scl, bool fast) = Wire.begin + + if (! mcp1.begin(0))Serial.println("MCP23017 1 not found!"); // begin(uint8_t address) "Pin 100 - 115" + if (! mcp2.begin(1))Serial.println("MCP23017 2 not found!"); // begin(uint8_t address) "Pin 116 - 131" + if (! mcp3.begin(2))Serial.println("MCP23017 3 not found!"); // begin(uint8_t address) "Pin 132 - 147" + if (! mcp4.begin(3))Serial.println("MCP23017 4 not found!"); // begin(uint8_t address) "Pin 148 - 163" + + // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid + char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + // Replace the following AUTHKEY with value that you can retrieve from: https://www.supla.org/arduino/get-authkey + char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + + /* + * Having your device already registered at cloud.supla.org, + * you want to change CHANNEL sequence or remove any of them, + * then you must also remove the device itself from cloud.supla.org. + * Otherwise you will get "Channel conflict!" error. + */ + + auto relay_1 = new Supla::Control::Relay(100, true); + auto relay_2 = new Supla::Control::Relay(101, true); + auto relay_3 = new Supla::Control::Relay(102, true); + auto relay_4 = new Supla::Control::Relay(103, true); + auto relay_5 = new Supla::Control::Relay(104, true); + auto relay_6 = new Supla::Control::Relay(105, true); + auto relay_7 = new Supla::Control::Relay(106, true); + auto relay_8 = new Supla::Control::Relay(107, true); + auto relay_9 = new Supla::Control::Relay(108, true); + auto relay_10 = new Supla::Control::Relay(109, true); + auto relay_11 = new Supla::Control::Relay(110, true); + auto relay_12 = new Supla::Control::Relay(111, true); + auto relay_13 = new Supla::Control::Relay(112, true); + auto relay_14 = new Supla::Control::Relay(113, true); + auto relay_15 = new Supla::Control::Relay(114, true); + auto relay_16 = new Supla::Control::Relay(115, true); + + auto relay_17 = new Supla::Control::Relay(148, true); + auto relay_18 = new Supla::Control::Relay(149, true); + auto relay_19 = new Supla::Control::Relay(150, true); + auto relay_20 = new Supla::Control::Relay(151, true); + auto relay_21 = new Supla::Control::Relay(152, true); + auto relay_22 = new Supla::Control::Relay(153, true); + auto relay_23 = new Supla::Control::Relay(154, true); + auto relay_24 = new Supla::Control::Relay(155, true); + auto relay_25 = new Supla::Control::Relay(156, true); + auto relay_26 = new Supla::Control::Relay(157, true); + auto relay_27 = new Supla::Control::Relay(158, true); + auto relay_28 = new Supla::Control::Relay(159, true); + auto relay_29 = new Supla::Control::Relay(160, true); + auto relay_30 = new Supla::Control::Relay(161, true); + auto relay_31 = new Supla::Control::Relay(162, true); + auto relay_32 = new Supla::Control::Relay(163, true); + + auto button_1 = new Supla::Control::Button(116, true, true); + auto button_2 = new Supla::Control::Button(117, true, true); + auto button_3 = new Supla::Control::Button(118, true, true); + auto button_4 = new Supla::Control::Button(119, true, true); + auto button_5 = new Supla::Control::Button(120, true, true); + auto button_6 = new Supla::Control::Button(121, true, true); + auto button_7 = new Supla::Control::Button(122, true, true); + auto button_8 = new Supla::Control::Button(123, true, true); + auto button_9 = new Supla::Control::Button(124, true, true); + auto button_10 = new Supla::Control::Button(125, true, true); + auto button_11 = new Supla::Control::Button(126, true, true); + auto button_12 = new Supla::Control::Button(127, true, true); + auto button_13 = new Supla::Control::Button(128, true, true); + auto button_14 = new Supla::Control::Button(129, true, true); + auto button_15 = new Supla::Control::Button(130, true, true); + auto button_16 = new Supla::Control::Button(131, true, true); + + auto button_17 = new Supla::Control::Button(132, true, true); + auto button_18 = new Supla::Control::Button(133, true, true); + auto button_19 = new Supla::Control::Button(134, true, true); + auto button_20 = new Supla::Control::Button(135, true, true); + auto button_21 = new Supla::Control::Button(136, true, true); + auto button_22 = new Supla::Control::Button(137, true, true); + auto button_23 = new Supla::Control::Button(138, true, true); + auto button_24 = new Supla::Control::Button(139, true, true); + auto button_25 = new Supla::Control::Button(140, true, true); + auto button_26 = new Supla::Control::Button(141, true, true); + auto button_27 = new Supla::Control::Button(142, true, true); + auto button_28 = new Supla::Control::Button(143, true, true); + auto button_29 = new Supla::Control::Button(144, true, true); + auto button_30 = new Supla::Control::Button(145, true, true); + auto button_31 = new Supla::Control::Button(146, true, true); + auto button_32 = new Supla::Control::Button(147, true, true); + + button_1->addAction(Supla::TOGGLE, relay_1, Supla::ON_PRESS); + button_1->setSwNoiseFilterDelay(50); + button_2->addAction(Supla::TOGGLE, relay_2, Supla::ON_PRESS); + button_2->setSwNoiseFilterDelay(50); + button_3->addAction(Supla::TOGGLE, relay_3, Supla::ON_PRESS); + button_3->setSwNoiseFilterDelay(50); + button_4->addAction(Supla::TOGGLE, relay_4, Supla::ON_PRESS); + button_4->setSwNoiseFilterDelay(50); + button_5->addAction(Supla::TOGGLE, relay_5, Supla::ON_PRESS); + button_5->setSwNoiseFilterDelay(50); + button_6->addAction(Supla::TOGGLE, relay_6, Supla::ON_PRESS); + button_6->setSwNoiseFilterDelay(50); + button_7->addAction(Supla::TOGGLE, relay_7, Supla::ON_PRESS); + button_7->setSwNoiseFilterDelay(50); + button_8->addAction(Supla::TOGGLE, relay_8, Supla::ON_PRESS); + button_8->setSwNoiseFilterDelay(50); + button_9->addAction(Supla::TOGGLE, relay_9, Supla::ON_PRESS); + button_9->setSwNoiseFilterDelay(50); + button_10->addAction(Supla::TOGGLE, relay_10, Supla::ON_PRESS); + button_10->setSwNoiseFilterDelay(50); + button_11->addAction(Supla::TOGGLE, relay_11, Supla::ON_PRESS); + button_11->setSwNoiseFilterDelay(50); + button_12->addAction(Supla::TOGGLE, relay_12, Supla::ON_PRESS); + button_12->setSwNoiseFilterDelay(50); + button_13->addAction(Supla::TOGGLE, relay_13, Supla::ON_PRESS); + button_13->setSwNoiseFilterDelay(50); + button_14->addAction(Supla::TOGGLE, relay_14, Supla::ON_PRESS); + button_14->setSwNoiseFilterDelay(50); + button_15->addAction(Supla::TOGGLE, relay_15, Supla::ON_PRESS); + button_15->setSwNoiseFilterDelay(50); + button_16->addAction(Supla::TOGGLE, relay_16, Supla::ON_PRESS); + button_16->setSwNoiseFilterDelay(50); + + button_17->addAction(Supla::TOGGLE, relay_17, Supla::ON_PRESS); + button_17->setSwNoiseFilterDelay(50); + button_18->addAction(Supla::TOGGLE, relay_18, Supla::ON_PRESS); + button_18->setSwNoiseFilterDelay(50); + button_19->addAction(Supla::TOGGLE, relay_19, Supla::ON_PRESS); + button_19->setSwNoiseFilterDelay(50); + button_20->addAction(Supla::TOGGLE, relay_20, Supla::ON_PRESS); + button_20->setSwNoiseFilterDelay(50); + button_21->addAction(Supla::TOGGLE, relay_21, Supla::ON_PRESS); + button_21->setSwNoiseFilterDelay(50); + button_22->addAction(Supla::TOGGLE, relay_22, Supla::ON_PRESS); + button_22->setSwNoiseFilterDelay(50); + button_23->addAction(Supla::TOGGLE, relay_23, Supla::ON_PRESS); + button_23->setSwNoiseFilterDelay(50); + button_24->addAction(Supla::TOGGLE, relay_24, Supla::ON_PRESS); + button_24->setSwNoiseFilterDelay(50); + button_25->addAction(Supla::TOGGLE, relay_25, Supla::ON_PRESS); + button_25->setSwNoiseFilterDelay(50); + button_26->addAction(Supla::TOGGLE, relay_26, Supla::ON_PRESS); + button_26->setSwNoiseFilterDelay(50); + button_27->addAction(Supla::TOGGLE, relay_27, Supla::ON_PRESS); + button_27->setSwNoiseFilterDelay(50); + button_28->addAction(Supla::TOGGLE, relay_28, Supla::ON_PRESS); + button_28->setSwNoiseFilterDelay(50); + button_29->addAction(Supla::TOGGLE, relay_29, Supla::ON_PRESS); + button_29->setSwNoiseFilterDelay(50); + button_30->addAction(Supla::TOGGLE, relay_30, Supla::ON_PRESS); + button_30->setSwNoiseFilterDelay(50); + button_31->addAction(Supla::TOGGLE, relay_31, Supla::ON_PRESS); + button_31->setSwNoiseFilterDelay(50); + button_32->addAction(Supla::TOGGLE, relay_32, Supla::ON_PRESS); + button_32->setSwNoiseFilterDelay(50); + + /* + * SuplaDevice Initialization. + * Server address is available at https://cloud.supla.org + * If you do not have an account, you can create it at https://cloud.supla.org/account/create + * SUPLA and SUPLA CLOUD are free of charge + * + */ + + SuplaDevice.begin(GUID, // Global Unique Identifier + "svr1.supla.org", // SUPLA server address + "email@address", // Email address used to login to Supla Cloud + AUTHKEY); // Authorization key + +} + +void loop() { + SuplaDevice.iterate(); + delay(25); +} diff --git a/lib/SuplaDevice/src/supla/control/MCP23017/readme.txt b/lib/SuplaDevice/src/supla/control/MCP23017/readme.txt new file mode 100644 index 00000000..9cb235ee --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/MCP23017/readme.txt @@ -0,0 +1,15 @@ +Support for up to 4 x "MCP23017" additional 64 Gpios. + + +BASIC USE: + +INSTANTIATE +#include + +SETUP + mcp1.init(uint8_t sda, uint8_t scl); // init(uint8_t sda, uint8_t scl) = Wire.begin(); + + mcp1.begin(uint8_t mcp23017_address); // Pin 100 - 115 + mcp2.begin(uint8_t mcp23017_address); // Pin 116 - 131 + mcp3.begin(uint8_t mcp23017_address); // Pin 132 - 147 + mcp4.begin(uint8_t mcp23017_address); // Pin 148 - 163 \ No newline at end of file diff --git a/lib/SuplaDevice/src/supla/control/MCP23017/supla_mcp23017.h b/lib/SuplaDevice/src/supla/control/MCP23017/supla_mcp23017.h new file mode 100644 index 00000000..8dfa9598 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/MCP23017/supla_mcp23017.h @@ -0,0 +1,78 @@ + +#ifndef S_mcp_23017_h +#define S_mcp_23017_h + +#include +#include +#include + + + MCP23017 mcp1; + MCP23017 mcp2; + MCP23017 mcp3; + MCP23017 mcp4; + + +class CustomControl : public Supla::Io { + public: + void customDigitalWrite(int channelNumber, uint8_t pin, uint8_t val) { + if (pin < 100) { + return ::digitalWrite(pin,val); + } + if ((pin > 99) && (pin < 116)){ + mcp1.digitalWrite(pin - 100, val); + return; + } + if ((pin > 115) && (pin < 132)){ + mcp2.digitalWrite(pin - 116, val); + } + if ((pin > 131) && (pin < 148)){ + mcp3.digitalWrite(pin - 132, val); + return; + } + if ((pin > 147) && (pin < 164)){ + mcp4.digitalWrite(pin - 148, val); + return; + } + } + int customDigitalRead(int channelNumber, uint8_t pin) { + if (pin < 100){ + return ::digitalRead(pin); + } + if ((pin > 99)&& (pin < 116)){ + return mcp1.digitalRead(pin - 100); + } + if ((pin > 115)&& (pin < 132)){ + return mcp2.digitalRead(pin - 116); + } + if ((pin > 131)&& (pin < 148)){ + return mcp3.digitalRead(pin - 132); + } + if ((pin > 147)&& (pin < 164)){ + return mcp4.digitalRead(pin - 148); + } + } + void customPinMode(int channelNumber, uint8_t pin, uint8_t mode) { + (void)(channelNumber); + if (pin < 100){ + return ::pinMode(pin, mode); + } + if ((pin > 99)&& (pin < 116)){ + mcp1.pinMode(pin - 100, mode); + } + if ((pin > 115)&& (pin < 132)){ + mcp2.pinMode(pin - 116, mode); + } + if ((pin > 131)&& (pin < 148)){ + mcp3.pinMode(pin - 132, mode); + } + if ((pin > 147)&& (pin < 164)){ + mcp4.pinMode(pin - 148, mode); + } + } + +}CustomControl; + + +#endif + diff --git a/lib/SuplaDevice/src/supla/control/bistable_relay.cpp b/lib/SuplaDevice/src/supla/control/bistable_relay.cpp index 3fabf297..d03f2aa4 100644 --- a/lib/SuplaDevice/src/supla/control/bistable_relay.cpp +++ b/lib/SuplaDevice/src/supla/control/bistable_relay.cpp @@ -40,7 +40,7 @@ BistableRelay::BistableRelay(int pin, void BistableRelay::onInit() { if (statusPin >= 0) { - pinMode(statusPin, statusPullUp ? INPUT_PULLUP : INPUT); + Supla::Io::pinMode(channel.getChannelNumber(), statusPin, statusPullUp ? INPUT_PULLUP : INPUT); channel.setNewValue(isOn()); } else { channel.setNewValue(false); @@ -56,7 +56,7 @@ void BistableRelay::onInit() { turnOff(); } - pinMode(pin, OUTPUT); + Supla::Io::pinMode(channel.getChannelNumber(), pin, OUTPUT); } void BistableRelay::iterateAlways() { @@ -138,6 +138,9 @@ void BistableRelay::internalToggle() { busy = true; disarmTimeMs = millis(); Supla::Io::digitalWrite(channel.getChannelNumber(), pin, pinOnValue()); + + // Schedule save in 5 s after state change + Supla::Storage::ScheduleSave(5000); } void BistableRelay::toggle(_supla_int_t duration) { diff --git a/lib/SuplaDevice/src/supla/control/bistable_roller_shutter.cpp b/lib/SuplaDevice/src/supla/control/bistable_roller_shutter.cpp index 5fc8fe1f..9401633f 100644 --- a/lib/SuplaDevice/src/supla/control/bistable_roller_shutter.cpp +++ b/lib/SuplaDevice/src/supla/control/bistable_roller_shutter.cpp @@ -15,6 +15,7 @@ */ #include "bistable_roller_shutter.h" +#include namespace Supla { namespace Control { @@ -33,28 +34,30 @@ void BistableRollerShutter::stopMovement() { } currentDirection = STOP_DIR; doNothingTime = millis(); + // Schedule save in 5 s after stop movement of roller shutter + Supla::Storage::ScheduleSave(5000); } void BistableRollerShutter::relayDownOn() { activeBiRelay = true; toggleTime = millis(); - digitalWrite(pinDown, highIsOn ? HIGH : LOW); + Supla::Io::digitalWrite(channel.getChannelNumber(), pinDown, highIsOn ? HIGH : LOW); } void BistableRollerShutter::relayUpOn() { activeBiRelay = true; toggleTime = millis(); - digitalWrite(pinUp, highIsOn ? HIGH : LOW); + Supla::Io::digitalWrite(channel.getChannelNumber(), pinUp, highIsOn ? HIGH : LOW); } void BistableRollerShutter::relayDownOff() { activeBiRelay = false; - digitalWrite(pinDown, highIsOn ? LOW : HIGH); + Supla::Io::digitalWrite(channel.getChannelNumber(), pinDown, highIsOn ? LOW : HIGH); } void BistableRollerShutter::relayUpOff() { activeBiRelay = false; - digitalWrite(pinUp, highIsOn ? LOW : HIGH); + Supla::Io::digitalWrite(channel.getChannelNumber(), pinUp, highIsOn ? LOW : HIGH); } void BistableRollerShutter::onTimer() { diff --git a/lib/SuplaDevice/src/supla/control/button.cpp b/lib/SuplaDevice/src/supla/control/button.cpp index 950b9f3e..9971de8c 100644 --- a/lib/SuplaDevice/src/supla/control/button.cpp +++ b/lib/SuplaDevice/src/supla/control/button.cpp @@ -16,61 +16,15 @@ #include "button.h" -enum StateResults {PRESSED, RELEASED, TO_PRESSED, TO_RELEASED}; - -Supla::Control::ButtonState::ButtonState(int pin, bool pullUp, bool invertLogic) - : debounceTimeMs(0), - filterTimeMs(0), - debounceDelayMs(50), - swNoiseFilterDelayMs(20), - pin(pin), - newStatusCandidate(LOW), - prevState(LOW), - pullUp(pullUp), - invertLogic(invertLogic) { -} - -int Supla::Control::ButtonState::update() { - if (millis() - debounceTimeMs > debounceDelayMs) { - int currentState = digitalRead(pin); - if (currentState != prevState) { - // If status is changed, then make sure that it will be kept at - // least swNoiseFilterDelayMs ms to avoid noise - if (currentState != newStatusCandidate) { - newStatusCandidate = currentState; - filterTimeMs = millis(); - } else if (millis() - filterTimeMs > swNoiseFilterDelayMs) { - // If new status is kept at least swNoiseFilterDelayMs ms, then apply - // change of status - debounceTimeMs = millis(); - prevState = currentState; - if (currentState == valueOnPress()) { - return TO_PRESSED; - } else { - return TO_RELEASED; - } - } - } else { - // If current status is the same as prevState, then reset - // new status candidate - newStatusCandidate = prevState; - } - } - if (prevState == valueOnPress()) { - return PRESSED; - } else { - return RELEASED; - } -} Supla::Control::Button::Button(int pin, bool pullUp, bool invertLogic) - : state(pin, pullUp, invertLogic), + : SimpleButton(pin, pullUp, invertLogic), holdTimeMs(0), + repeatOnHoldMs(0), multiclickTimeMs(0), - clickCounter(0), lastStateChangeMs(0), - enableExtDetection(false), - holdSend(false), + clickCounter(0), + holdSend(0), bistable(false) { } @@ -88,15 +42,26 @@ void Supla::Control::Button::onTimer() { runAction(ON_CHANGE); } + if (stateChanged) { + lastStateChangeMs = millis(); + if (stateResult == TO_PRESSED || bistable) { + clickCounter++; + } + } + if (!stateChanged) { if (!bistable && stateResult == PRESSED) { - if (clickCounter <= 1 && holdTimeMs > 0 && timeDelta > holdTimeMs && !holdSend) { + if (clickCounter <= 1 && holdTimeMs > 0 && timeDelta > (holdTimeMs + holdSend*repeatOnHoldMs) && (repeatOnHoldMs == 0 ? !holdSend : true)) { runAction(ON_HOLD); - holdSend = true; + ++holdSend; + } + } else if (clickCounter > 0 && (bistable || stateResult == RELEASED)) { + if (multiclickTimeMs == 0) { + holdSend = 0; + clickCounter = 0; } - } else if (bistable || stateResult == RELEASED) { if (multiclickTimeMs > 0 && timeDelta > multiclickTimeMs) { - if (!holdSend) { + if (holdSend == 0) { switch (clickCounter) { case 1: runAction(ON_CLICK_1); @@ -129,49 +94,15 @@ void Supla::Control::Button::onTimer() { runAction(ON_CLICK_10); break; } - } - holdSend = false; + if (clickCounter >= 10) { + runAction(ON_CRAZY_CLICKER); + } + } + holdSend = 0; clickCounter = 0; } } } - - if (stateChanged) { - lastStateChangeMs = millis(); - if (stateResult == TO_PRESSED || bistable) { - clickCounter++; - } - - } -} - -void Supla::Control::Button::onInit() { - state.init(); -} - -void Supla::Control::ButtonState::init() { - pinMode(pin, pullUp ? INPUT_PULLUP : INPUT); - prevState = digitalRead(pin); - newStatusCandidate = prevState; -} - -int Supla::Control::ButtonState::valueOnPress() { - return invertLogic ? LOW : HIGH; -} - -void Supla::Control::Button::setSwNoiseFilterDelay(unsigned int newDelayMs) { - state.setSwNoiseFilterDelay(newDelayMs); -} -void Supla::Control::ButtonState::setSwNoiseFilterDelay(unsigned int newDelayMs) { - swNoiseFilterDelayMs = newDelayMs; -} - -void Supla::Control::Button::setDebounceDelay(unsigned int newDelayMs) { - state.setDebounceDelay(newDelayMs); -} - -void Supla::Control::ButtonState::setDebounceDelay(unsigned int newDelayMs) { - debounceDelayMs = newDelayMs; } void Supla::Control::Button::setHoldTime(unsigned int timeMs) { @@ -188,3 +119,7 @@ void Supla::Control::Button::setMulticlickTime(unsigned int timeMs, bool bistabl holdTimeMs = 0; } } + +void Supla::Control::Button::repeatOnHoldEvery(unsigned int timeMs) { + repeatOnHoldMs = timeMs; +} diff --git a/lib/SuplaDevice/src/supla/control/button.h b/lib/SuplaDevice/src/supla/control/button.h index 7cf146a3..ba31ab58 100644 --- a/lib/SuplaDevice/src/supla/control/button.h +++ b/lib/SuplaDevice/src/supla/control/button.h @@ -18,59 +18,27 @@ #define _button_h #include - -#include "../element.h" -#include "../events.h" -#include "../local_action.h" +#include "simple_button.h" namespace Supla { namespace Control { -class ButtonState { - public: - ButtonState(int pin, bool pullUp, bool invertLogic); - int update(); - void init(); - - void setSwNoiseFilterDelay(unsigned int newDelayMs); - void setDebounceDelay(unsigned int newDelayMs); - void setHoldTime(unsigned int timeMs); - void setMulticlickTime(unsigned int timeMs); - - protected: - int valueOnPress(); - - unsigned long debounceTimeMs; - unsigned long filterTimeMs; - unsigned int debounceDelayMs; - unsigned int swNoiseFilterDelayMs; - int pin; - int8_t newStatusCandidate; - int8_t prevState; - bool pullUp; - bool invertLogic; -}; - -class Button : public Element, - public LocalAction { +class Button : public SimpleButton { public: Button(int pin, bool pullUp = false, bool invertLogic = false); void onTimer(); - void onInit(); - void setSwNoiseFilterDelay(unsigned int newDelayMs); - void setDebounceDelay(unsigned int newDelayMs); void setHoldTime(unsigned int timeMs); + void repeatOnHoldEvery(unsigned int timeMs); void setMulticlickTime(unsigned int timeMs, bool bistableButton = false); protected: - ButtonState state; unsigned int holdTimeMs; + unsigned int repeatOnHoldMs; unsigned int multiclickTimeMs; - uint8_t clickCounter; unsigned long lastStateChangeMs; - bool enableExtDetection; - bool holdSend; + uint8_t clickCounter; + unsigned int holdSend; bool bistable; }; diff --git a/lib/SuplaDevice/src/supla/control/dimmer_base.cpp b/lib/SuplaDevice/src/supla/control/dimmer_base.cpp new file mode 100644 index 00000000..88f6f1e6 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/dimmer_base.cpp @@ -0,0 +1,48 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "dimmer_base.h" +#include "../storage/storage.h" + +Supla::Control::DimmerBase::DimmerBase() { + channel.setType(SUPLA_CHANNELTYPE_DIMMER); + channel.setDefault(SUPLA_CHANNELFNC_DIMMER); +} + +void Supla::Control::DimmerBase::setRGBW(int red, + int green, + int blue, + int colorBrightness, + int brightness, + bool toggle) { + Supla::Control::RGBWBase::setRGBW(0, 0, 0, 0, brightness, toggle); +} + +void Supla::Control::DimmerBase::onLoadState() { + Supla::Storage::ReadState((unsigned char *)&curBrightness, + sizeof(curBrightness)); + Supla::Storage::ReadState((unsigned char *)&lastBrightness, sizeof(lastBrightness)); +} + +void Supla::Control::DimmerBase::onSaveState() { + Supla::Storage::WriteState((unsigned char *)&curBrightness, + sizeof(curBrightness)); + Supla::Storage::WriteState((unsigned char *)&lastBrightness, sizeof(lastBrightness)); +} + +void Supla::Control::DimmerBase::iterateDimmerRGBW(int rgbStep, int wStep) { + Supla::Control::RGBWBase::iterateDimmerRGBW(0, wStep); +} diff --git a/lib/SuplaDevice/src/supla/control/dimmer_base.h b/lib/SuplaDevice/src/supla/control/dimmer_base.h index cd88b026..29b665c0 100644 --- a/lib/SuplaDevice/src/supla/control/dimmer_base.h +++ b/lib/SuplaDevice/src/supla/control/dimmer_base.h @@ -24,10 +24,20 @@ namespace Supla { namespace Control { class DimmerBase : public RGBWBase { public: - DimmerBase() { - channel.setType(SUPLA_CHANNELTYPE_DIMMER); - channel.setDefault(SUPLA_CHANNELFNC_DIMMER); - } + DimmerBase(); + + void setRGBW(int red, + int green, + int blue, + int colorBrightness, + int brightness, + bool toggle = false); + + void onLoadState(); + void onSaveState(); + + protected: + virtual void iterateDimmerRGBW(int rgbStep, int wStep); }; diff --git a/lib/SuplaDevice/src/supla/control/dimmer_leds.cpp b/lib/SuplaDevice/src/supla/control/dimmer_leds.cpp new file mode 100644 index 00000000..10abcb05 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/dimmer_leds.cpp @@ -0,0 +1,67 @@ +/* +Copyright (C) AC SOFTWARE SP. Z O.O. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "dimmer_leds.h" + +#ifdef ARDUINO_ARCH_ESP32 +extern int esp32PwmChannelCouner; +#endif + +Supla::Control::DimmerLeds::DimmerLeds(int brightnessPin) + : brightnessPin(brightnessPin) { +} + +void Supla::Control::DimmerLeds::setRGBWValueOnDevice(uint32_t red, + uint32_t green, + uint32_t blue, + uint32_t colorBrightness, + uint32_t brightness) { + uint32_t brightnessAdj = brightness; + +#ifdef ARDUINO_ARCH_AVR + brightnessAdj = map(brightnessAdj, 0, 1023, 0, 255); +#endif + +#ifdef ARDUINO_ARCH_ESP32 + ledcWrite(brightnessPin, brightnessAdj); +#else + analogWrite(brightnessPin, brightnessAdj); +#endif +} + +void Supla::Control::DimmerLeds::onInit() { +#ifdef ARDUINO_ARCH_ESP32 + Serial.print(F("Dimmer: attaching pin ")); + Serial.print(brightnessPin); + Serial.print(F(" to PWM channel: ")); + Serial.println(esp32PwmChannelCouner); + + ledcSetup(esp32PwmChannelCouner, 12000, 10); + ledcAttachPin(brightnessPin, esp32PwmChannelCouner); + // on ESP32 we write to PWM channels instead of pins, so we copy channel + // number as pin in order to reuse variable + brightnessPin = esp32PwmChannelCouner; + esp32PwmChannelCouner++; +#else + pinMode(brightnessPin, OUTPUT); + +#ifdef ARDUINO_ARCH_ESP8266 + analogWriteRange(1024); +#endif +#endif + + Supla::Control::DimmerBase::onInit(); +} diff --git a/lib/SuplaDevice/src/supla/control/dimmer_leds.h b/lib/SuplaDevice/src/supla/control/dimmer_leds.h new file mode 100644 index 00000000..041639e5 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/dimmer_leds.h @@ -0,0 +1,44 @@ +/* +Copyright (C) AC SOFTWARE SP. Z O.O. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef __dimmer_leds_h +#define __dimmer_leds_h + +#include "dimmer_base.h" + +namespace Supla { +namespace Control { +class DimmerLeds : public DimmerBase { + public: + DimmerLeds(int brightnessPin); + + void setRGBWValueOnDevice(uint32_t red, + uint32_t green, + uint32_t blue, + uint32_t colorBrightness, + uint32_t brightness); + + void onInit(); + + protected: + int brightnessPin; +}; + +}; // namespace Control +}; // namespace Supla + +#endif + diff --git a/lib/SuplaDevice/src/supla/control/direct_links.cpp b/lib/SuplaDevice/src/supla/control/direct_links.cpp new file mode 100644 index 00000000..60cad047 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/direct_links.cpp @@ -0,0 +1,156 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "direct_links.h" + +#include + +namespace Supla { +namespace Control { + +DirectLinks::DirectLinks(const char *host, bool isSecured) + : _lastStateON(false), _lastStateOFF(false) { + setHost(host); + enableSSL(isSecured); +} + +DirectLinks::~DirectLinks() { + delete[] client; +} + +void DirectLinks::setHost(const char *host) { + if (host) { + strncpy(_host, host, MAX_HOST_SIZE); + } +} + +void DirectLinks::setUrlON(const char *url) { + if (url) { + strncpy(_urlON, url, MAX_DIRECT_LINKS_SIZE); + } +} + +void DirectLinks::setUrlOFF(const char *url) { + if (url) { + strncpy(_urlOFF, url, MAX_DIRECT_LINKS_SIZE); + } +} + +void DirectLinks::enableSSL(bool isSecured) { + _isSecured = isSecured; +} + +bool DirectLinks::openConnection() { + if (!client->connect(_host, _isSecured ? 443 : 80)) { + return false; + } + return true; +} + +bool DirectLinks::closeConnection() { + client->stop(); + return checkConnection(); +} + +bool DirectLinks::checkConnection() { + if (client->connected() == 1) { + return true; + } else { + return false; + } +} + +void DirectLinks::toggleConnection() { + if (client == NULL) { + if (_isSecured) { + client = new WiFiClientSecure(); + ((WiFiClientSecure *)client)->setInsecure(); + ((WiFiClientSecure *)client)->setBufferSizes(256, 256); + ((WiFiClientSecure *)client)->setTimeout(200); + } else { + client = new WiFiClient(); + } + } + + if (checkConnection()) { + closeConnection(); + } else { + openConnection(); + } +} + +void DirectLinks::sendRequest(const char *url) { + if (client) { + (WiFiClientSecure *)client->print( + String("GET /direct/") + url + " HTTP/1.1\r\n" + "Host: " + _host + + "\r\n" + "User-Agent: BuildFailureDetectorESP8266\r\n" + + "Connection: close\r\n\r\n"); + + while (client->connected() || client->available()) { + String line = client->readStringUntil('\n'); + if (line == "\r") { + Serial.println(F("Direct links - Headers received")); + break; + } + } + + String line = client->readString(); + if (line.indexOf("true") != -1) { + Serial.println(F("Alert sent successfully!")); + } else { + Serial.print(F("Alert failure")); + Serial.println(line); + } + } +} + +void DirectLinks::send(const char *url) { + toggleConnection(); + sendRequest(url); + toggleConnection(); + + if (client) { + delete client; + client = nullptr; + } +} + +void DirectLinks::iterateAlways() { + if (_lastStateON) { + _lastStateON = false; + send(_urlON); + } + if (_lastStateOFF) { + _lastStateOFF = false; + send(_urlOFF); + } +} + +void DirectLinks::handleAction(int event, int action) { + (void)(event); + + switch (action) { + case SEND_DIRECT_LINKS_ON: + _lastStateON = true; + break; + case SEND_DIRECT_LINKS_OFF: + _lastStateOFF = true; + break; + } +} + +}; // namespace Control +}; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/control/direct_links.h b/lib/SuplaDevice/src/supla/control/direct_links.h new file mode 100644 index 00000000..6526c66e --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/direct_links.h @@ -0,0 +1,75 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _direct_link_h +#define _direct_link_h + +#include +#include + +#include "../action_handler.h" +#include "../actions.h" +#include "../channel_element.h" + +#define MAX_DIRECT_LINKS_SIZE 32 +#define MAX_HOST_SIZE 32 + +namespace Supla { +enum DirectLinks { + SEND_DIRECT_LINKS_ON, + SEND_DIRECT_LINKS_OFF, +}; +} + +namespace Supla { +namespace Control { + +class DirectLinks : public Element, public ActionHandler { + public: + DirectLinks(const char *host, bool isSecured = true); + ~DirectLinks(); + + void setHost(const char *host); + void setUrlON(const char *url); + void setUrlOFF(const char *url); + void enableSSL(bool isSecured); + + void iterateAlways(); + void handleAction(int event, int action); + + bool checkConnection(); + void toggleConnection(); + bool openConnection(); + bool closeConnection(); + void sendRequest(const char *url); + void send(const char *url); + + protected: + WiFiClient *client = NULL; + bool _lastStateON; + bool _lastStateOFF; + int _action; + + bool _isSecured; + char _urlON[MAX_DIRECT_LINKS_SIZE]; + char _urlOFF[MAX_DIRECT_LINKS_SIZE]; + char _host[MAX_HOST_SIZE]; +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/control/internal_pin_output.cpp b/lib/SuplaDevice/src/supla/control/internal_pin_output.cpp new file mode 100644 index 00000000..c889cb40 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/internal_pin_output.cpp @@ -0,0 +1,124 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include "internal_pin_output.h" +#include "../events.h" + +Supla::Control::InternalPinOutput::InternalPinOutput(int pin, bool highIsOn) + : pin(pin), + highIsOn(highIsOn), + stateOnInit(STATE_ON_INIT_OFF), + storedTurnOnDurationMs(0), + durationTimestamp(0), + durationMs(0) { +} + +Supla::Control::InternalPinOutput & +Supla::Control::InternalPinOutput::setDefaultStateOn() { + stateOnInit = STATE_ON_INIT_ON; + return *this; +} +Supla::Control::InternalPinOutput & +Supla::Control::InternalPinOutput::setDefaultStateOff() { + stateOnInit = STATE_ON_INIT_OFF; + return *this; +} + +uint8_t Supla::Control::InternalPinOutput::pinOnValue() { + return highIsOn ? HIGH : LOW; +} + +uint8_t Supla::Control::InternalPinOutput::pinOffValue() { + return highIsOn ? LOW : HIGH; +} + +void Supla::Control::InternalPinOutput::turnOn(_supla_int_t duration) { + durationMs = duration; + durationTimestamp = millis(); + if (storedTurnOnDurationMs) { + durationMs = storedTurnOnDurationMs; + } + + runAction(Supla::ON_TURN_ON); + runAction(Supla::ON_CHANGE); + + Supla::Io::digitalWrite(pin, pinOnValue()); +} + +void Supla::Control::InternalPinOutput::turnOff(_supla_int_t duration) { + durationMs = duration; + durationTimestamp = millis(); + + runAction(Supla::ON_TURN_OFF); + runAction(Supla::ON_CHANGE); + + Supla::Io::digitalWrite(pin, pinOffValue()); +} + +bool Supla::Control::InternalPinOutput::isOn() { + return Supla::Io::digitalRead(pin) == pinOnValue(); +} + +void Supla::Control::InternalPinOutput::toggle(_supla_int_t duration) { + if (isOn()) { + turnOff(duration); + } else { + turnOn(duration); + } +} + +void Supla::Control::InternalPinOutput::handleAction(int event, int action) { + (void)(event); + switch (action) { + case TURN_ON: { + turnOn(); + break; + } + case TURN_OFF: { + turnOff(); + break; + } + case TOGGLE: { + toggle(); + break; + } + } +} + +void Supla::Control::InternalPinOutput::onInit() { + if (stateOnInit == STATE_ON_INIT_ON) { + turnOn(); + } else { + turnOff(); + } + + Supla::Io::pinMode( + pin, OUTPUT); // pin mode is set after setting pin value in order to + // avoid problems with LOW trigger relays +} +void Supla::Control::InternalPinOutput::iterateAlways() { + if (durationMs && millis() - durationTimestamp > durationMs) { + toggle(); + } +} + +Supla::Control::InternalPinOutput & +Supla::Control::InternalPinOutput::setDurationMs(_supla_int_t duration) { + storedTurnOnDurationMs = duration; + return *this; +} diff --git a/lib/SuplaDevice/src/supla/control/internal_pin_output.h b/lib/SuplaDevice/src/supla/control/internal_pin_output.h new file mode 100644 index 00000000..e1efe8bd --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/internal_pin_output.h @@ -0,0 +1,67 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _internal_pin_output_h +#define _internal_pin_output_h + +#include "../action_handler.h" +#include "../actions.h" +#include "../element.h" +#include "../io.h" +#include "../local_action.h" + +#define STATE_ON_INIT_OFF 0 +#define STATE_ON_INIT_ON 1 + +namespace Supla { +namespace Control { +class InternalPinOutput : public Element, + public ActionHandler, + public LocalAction { + public: + InternalPinOutput(int pin, bool highIsOn = true); + + virtual InternalPinOutput &setDefaultStateOn(); + virtual InternalPinOutput &setDefaultStateOff(); + virtual InternalPinOutput &setDurationMs(_supla_int_t duration); + + virtual uint8_t pinOnValue(); + virtual uint8_t pinOffValue(); + virtual void turnOn(_supla_int_t duration = 0); + virtual void turnOff(_supla_int_t duration = 0); + virtual bool isOn(); + virtual void toggle(_supla_int_t duration = 0); + + void handleAction(int event, int action); + + void onInit(); + void iterateAlways(); + + protected: + int pin; + bool highIsOn; + + int8_t stateOnInit; + + unsigned _supla_int_t durationMs; + unsigned _supla_int_t storedTurnOnDurationMs; + unsigned long durationTimestamp; +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/control/pin_status_led.cpp b/lib/SuplaDevice/src/supla/control/pin_status_led.cpp new file mode 100644 index 00000000..dd4d89e3 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/pin_status_led.cpp @@ -0,0 +1,48 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include "../io.h" +#include "pin_status_led.h" + +Supla::Control::PinStatusLed::PinStatusLed(uint8_t srcPin, + uint8_t outPin, + bool invert) + : srcPin(srcPin), outPin(outPin), invert(invert) { +} + +void Supla::Control::PinStatusLed::onInit() { + updatePin(); + Supla::Io::pinMode(outPin, OUTPUT); +} + +void Supla::Control::PinStatusLed::iterateAlways() { + updatePin(); +} + +void Supla::Control::PinStatusLed::setInvertedLogic(bool invertedLogic) { + invert = invertedLogic; + updatePin(); +} + +void Supla::Control::PinStatusLed::updatePin() { + int value = Supla::Io::digitalRead(srcPin); + value = invert ? !value : value; + if (value != Supla::Io::digitalRead(outPin)) { + Supla::Io::digitalWrite(outPin, value); + } +} diff --git a/lib/SuplaDevice/src/supla/control/pin_status_led.h b/lib/SuplaDevice/src/supla/control/pin_status_led.h new file mode 100644 index 00000000..39d2fc0a --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/pin_status_led.h @@ -0,0 +1,43 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _pin_status_led_h +#define _pin_status_led_h + +#include "../element.h" + +namespace Supla { +namespace Control { +class PinStatusLed : public Element { + public: + PinStatusLed(uint8_t srcPin, uint8_t outPin, bool invert = false); + + void onInit(); + void iterateAlways(); + void setInvertedLogic(bool invertedLogic); + + protected: + void updatePin(); + + uint8_t srcPin; + uint8_t outPin; + bool invert; +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/control/pushover.cpp b/lib/SuplaDevice/src/supla/control/pushover.cpp new file mode 100644 index 00000000..b996da54 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/pushover.cpp @@ -0,0 +1,159 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "pushover.h" + +#include + +namespace Supla { +namespace Control { + +Pushover::Pushover(const char *token, const char *user, bool isSecured) + : lastMsgReceivedMs(0) { + setToken(token); + setUser(user); + _isSecured = isSecured; +} + +Pushover::~Pushover() { + delete[] client; +} + +void Pushover::setToken(const char *token) { + if (token) { + strncpy(_token, token, MAX_TOKEN_SIZE); + } +} + +void Pushover::setUser(const char *user) { + if (user) { + strncpy(_user, user, MAX_TOKEN_SIZE); + } +} + +void Pushover::setTitle(const char *title) { + if (title) { + strncpy(_title, title, MAX_TOKEN_SIZE); + } +} + +void Pushover::setMessage(const char *message) { + if (message) { + strncpy(_message, message, MAX_TOKEN_SIZE); + } +} + +bool Pushover::openConnection() { + if (!client->connect(host, _isSecured ? 443 : 80)) { + return false; + } + return true; +} + +bool Pushover::closeConnection() { + client->stop(); + return checkConnection(); +} + +bool Pushover::checkConnection() { + if (client->connected() == 1) { + return true; + } else { + return false; + } +} + +void Pushover::toggleConnection() { + if (client == NULL) { + if (_isSecured) { + client = new WiFiClientSecure(); + ((WiFiClientSecure *)client)->setInsecure(); + ((WiFiClientSecure *)client)->setBufferSizes(256, 256); + ((WiFiClientSecure *)client)->setTimeout(200); + } else { + client = new WiFiClient(); + } + } + + if (checkConnection()) { + closeConnection(); + } else { + openConnection(); + } +} + +void Pushover::sendRequest() { + if (client) { + String post = String("token=") + _token + "&user=" + _user + + "&title=" + _title + "&message=" + _message; + + /* String("token=") + _token + "&user=" + _user + "&title=" + _title + + "&message=" + _message + "&device=" + _device + "&url=" + _url + + "&url_title=" + _url_title + "&priority=" + _priority + + "&retry=" + _retry + "&expire=" + _expire + "&sound=" + _sound;*/ + + (WiFiClientSecure *)client->print( + String("POST ") + path + " HTTP/1.1\r\n" + "host: " + host + "\r\n" + + "Content-length: " + String(post.length(), DEC) + + "\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + + "Connection: close\r\n\r\n" + post); + + while (client->connected()) { + String line = client->readStringUntil('\n'); + if (line == "\r") { + Serial.println(F("Headers received")); + break; + } + } + + /* String line = client->readString(); + if (line.indexOf("\"status\":1") != -1 || line.indexOf("200 OK") != -1) { + Serial.println(F("Alert sent successfully!")); + } else { + Serial.print(F("Alert failure")); + Serial.println(line); + }*/ + } +} + +void Pushover::send() { + toggleConnection(); + sendRequest(); + toggleConnection(); + + if (client) { + delete client; + client = nullptr; + } +} + +void Pushover::iterateAlways() { + if (lastMsgReceivedMs != 0 && millis() - lastMsgReceivedMs > 1000) { + lastMsgReceivedMs = 0; + send(); + } +} + +void Pushover::handleAction(int event, int action) { + (void)(event); + if (action == SEND_NOTIF_1) { + lastMsgReceivedMs = millis(); + } +} + +}; // namespace Control +}; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/control/pushover.h b/lib/SuplaDevice/src/supla/control/pushover.h new file mode 100644 index 00000000..bf026e83 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/pushover.h @@ -0,0 +1,77 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _pushover_h +#define _pushover_h + +#include +#include + +#include "../action_handler.h" +#include "../actions.h" +#include "../channel_element.h" + +#define MAX_TOKEN_SIZE 32 +#define MAX_USER_SIZE 32 +#define MAX_TITLE_SIZE 32 +#define MAX_MESSAGE_SIZE 64 + +namespace Supla { +enum Pushover { SEND_NOTIF_1 }; +} // namespace Supla + +namespace Supla { +namespace Control { + +class Pushover : public Element, public ActionHandler { + public: + Pushover(const char *token = nullptr, + const char *user = nullptr, + bool isSecured = true); + ~Pushover(); + + void setToken(const char *token); + void setUser(const char *user); + void setMessage(const char *message); + void setTitle(const char *title); + + void iterateAlways(); + void handleAction(int event, int action); + + bool checkConnection(); + void toggleConnection(); + bool openConnection(); + bool closeConnection(); + void sendRequest(); + void send(); + + protected: + WiFiClient *client = NULL; + const char *host = "api.pushover.net"; + const char *path = "/1/messages.json"; + unsigned long lastMsgReceivedMs; + + bool _isSecured; + char _token[MAX_TOKEN_SIZE]; + char _user[MAX_USER_SIZE]; + char _title[MAX_TITLE_SIZE]; + char _message[MAX_MESSAGE_SIZE]; +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/control/relay.cpp b/lib/SuplaDevice/src/supla/control/relay.cpp index 081d1fe7..488e9b3c 100644 --- a/lib/SuplaDevice/src/supla/control/relay.cpp +++ b/lib/SuplaDevice/src/supla/control/relay.cpp @@ -57,7 +57,7 @@ void Relay::onInit() { turnOff(); } - pinMode(pin, OUTPUT); // pin mode is set after setting pin value in order to + Supla::Io::pinMode(channel.getChannelNumber(), pin, OUTPUT); // pin mode is set after setting pin value in order to // avoid problems with LOW trigger relays } @@ -94,6 +94,9 @@ void Relay::turnOn(_supla_int_t duration) { Supla::Io::digitalWrite(channel.getChannelNumber(), pin, pinOnValue()); channel.setNewValue(true); + + // Schedule save in 5 s after state change + Supla::Storage::ScheduleSave(5000); } void Relay::turnOff(_supla_int_t duration) { @@ -102,6 +105,9 @@ void Relay::turnOff(_supla_int_t duration) { Supla::Io::digitalWrite(channel.getChannelNumber(), pin, pinOffValue()); channel.setNewValue(false); + + // Schedule save in 5 s after state change + Supla::Storage::ScheduleSave(5000); } bool Relay::isOn() { @@ -117,7 +123,7 @@ void Relay::toggle(_supla_int_t duration) { } } -void Relay::runAction(int event, int action) { +void Relay::handleAction(int event, int action) { (void)(event); switch (action) { case TURN_ON: { @@ -135,44 +141,40 @@ void Relay::runAction(int event, int action) { } } -Channel *Relay::getChannel() { - return &channel; -} - void Relay::onSaveState() { - if (keepTurnOnDurationMs) { - Supla::Storage::WriteState((unsigned char *)&storedTurnOnDurationMs, - sizeof(storedTurnOnDurationMs)); - } + Supla::Storage::WriteState((unsigned char *)&storedTurnOnDurationMs, + sizeof(storedTurnOnDurationMs)); + bool enabled = false; if (stateOnInit < 0) { - bool enabled = isOn(); - Supla::Storage::WriteState((unsigned char *)&enabled, sizeof(enabled)); - } + enabled = isOn(); + } + Supla::Storage::WriteState((unsigned char *)&enabled, sizeof(enabled)); } void Relay::onLoadState() { + Supla::Storage::ReadState((unsigned char *)&storedTurnOnDurationMs, + sizeof(storedTurnOnDurationMs)); if (keepTurnOnDurationMs) { - Supla::Storage::ReadState((unsigned char *)&storedTurnOnDurationMs, - sizeof(storedTurnOnDurationMs)); Serial.print(F("Relay[")); Serial.print(channel.getChannelNumber()); Serial.print(F("]: restored durationMs: ")); Serial.println(storedTurnOnDurationMs); + } else { + storedTurnOnDurationMs = 0; } + bool enabled = false; + Supla::Storage::ReadState((unsigned char *)&enabled, sizeof(enabled)); if (stateOnInit < 0) { - bool enabled = false; - if (Supla::Storage::ReadState((unsigned char *)&enabled, sizeof(enabled))) { - Serial.print(F("Relay[")); - Serial.print(channel.getChannelNumber()); - Serial.print(F("]: restored relay state: ")); - if (enabled) { - Serial.println(F("ON")); - stateOnInit = STATE_ON_INIT_RESTORED_ON; - } else { - Serial.println(F("OFF")); - stateOnInit = STATE_ON_INIT_RESTORED_OFF; - } + Serial.print(F("Relay[")); + Serial.print(channel.getChannelNumber()); + Serial.print(F("]: restored relay state: ")); + if (enabled) { + Serial.println(F("ON")); + stateOnInit = STATE_ON_INIT_RESTORED_ON; + } else { + Serial.println(F("OFF")); + stateOnInit = STATE_ON_INIT_RESTORED_OFF; } } } @@ -196,3 +198,7 @@ Relay &Relay::keepTurnOnDuration(bool keep) { keepTurnOnDurationMs = keep; return *this; } + +unsigned _supla_int_t Relay::getStoredTurnOnDurationMs() { + return storedTurnOnDurationMs; +} diff --git a/lib/SuplaDevice/src/supla/control/relay.h b/lib/SuplaDevice/src/supla/control/relay.h index f7a8813f..dcbf6f6e 100644 --- a/lib/SuplaDevice/src/supla/control/relay.h +++ b/lib/SuplaDevice/src/supla/control/relay.h @@ -25,11 +25,11 @@ #include #include "../actions.h" -#include "../channel.h" -#include "../element.h" +#include "../channel_element.h" #include "../io.h" #include "../storage/storage.h" -#include "../triggerable.h" +#include "../action_handler.h" +#include "../local_action.h" #define STATE_ON_INIT_RESTORED_OFF -3 #define STATE_ON_INIT_RESTORED_ON -2 @@ -39,7 +39,7 @@ namespace Supla { namespace Control { -class Relay : public Element, public Triggerable { +class Relay : public ChannelElement, public ActionHandler { public: Relay(int pin, bool highIsOn = true, @@ -58,17 +58,16 @@ class Relay : public Element, public Triggerable { virtual bool isOn(); virtual void toggle(_supla_int_t duration = 0); - void runAction(int event, int action); + void handleAction(int event, int action); void onInit(); void onLoadState(); void onSaveState(); void iterateAlways(); int handleNewValueFromServer(TSD_SuplaChannelNewValue *newValue); + unsigned _supla_int_t getStoredTurnOnDurationMs(); protected: - Channel *getChannel(); - Channel channel; int pin; bool highIsOn; diff --git a/lib/SuplaDevice/src/supla/control/rgb_base.cpp b/lib/SuplaDevice/src/supla/control/rgb_base.cpp new file mode 100644 index 00000000..9869e5a1 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/rgb_base.cpp @@ -0,0 +1,61 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "rgb_base.h" +#include "../storage/storage.h" + +Supla::Control::RGBBase::RGBBase() { + channel.setType(SUPLA_CHANNELTYPE_RGBLEDCONTROLLER); + channel.setDefault(SUPLA_CHANNELFNC_RGBLIGHTING); +} + +void Supla::Control::RGBBase::setRGBW(int red, + int green, + int blue, + int colorBrightness, + int brightness, + bool toggle) { + Supla::Control::RGBWBase::setRGBW( + red, green, blue, colorBrightness, 0, toggle); +} + +void Supla::Control::RGBBase::onSaveState() { + /* + uint8_t curRed; // 0 - 255 + uint8_t curGreen; // 0 - 255 + uint8_t curBlue; // 0 - 255 + uint8_t curColorBrightness; // 0 - 100 + uint8_t lastColorBrightness; // 0 - 100 + */ + Supla::Storage::WriteState((unsigned char *)&curRed, sizeof(curRed)); + Supla::Storage::WriteState((unsigned char *)&curGreen, sizeof(curGreen)); + Supla::Storage::WriteState((unsigned char *)&curBlue, sizeof(curBlue)); + Supla::Storage::WriteState((unsigned char *)&curColorBrightness, + sizeof(curColorBrightness)); + Supla::Storage::WriteState((unsigned char *)&lastColorBrightness, + sizeof(lastColorBrightness)); +} + +void Supla::Control::RGBBase::onLoadState() { + Supla::Storage::ReadState((unsigned char *)&curRed, sizeof(curRed)); + Supla::Storage::ReadState((unsigned char *)&curGreen, sizeof(curGreen)); + Supla::Storage::ReadState((unsigned char *)&curBlue, sizeof(curBlue)); + Supla::Storage::ReadState((unsigned char *)&curColorBrightness, + sizeof(curColorBrightness)); + Supla::Storage::ReadState((unsigned char *)&lastColorBrightness, + sizeof(lastColorBrightness)); +} + diff --git a/lib/SuplaDevice/src/supla/control/rgb_base.h b/lib/SuplaDevice/src/supla/control/rgb_base.h index 7bcc3936..2bc534d9 100644 --- a/lib/SuplaDevice/src/supla/control/rgb_base.h +++ b/lib/SuplaDevice/src/supla/control/rgb_base.h @@ -23,11 +23,16 @@ namespace Supla { namespace Control { class RGBBase : public RGBWBase { public: - RGBBase() { - channel.setType(SUPLA_CHANNELTYPE_RGBLEDCONTROLLER); - channel.setDefault(SUPLA_CHANNELFNC_RGBLIGHTING); - } + RGBBase(); + void setRGBW(int red, + int green, + int blue, + int colorBrightness, + int brightness, + bool toggle = false); + void onLoadState(); + void onSaveState(); }; }; // namespace Control diff --git a/lib/SuplaDevice/src/supla/control/rgb_leds.cpp b/lib/SuplaDevice/src/supla/control/rgb_leds.cpp new file mode 100644 index 00000000..1efc29dd --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/rgb_leds.cpp @@ -0,0 +1,88 @@ +/* +Copyright (C) AC SOFTWARE SP. Z O.O. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "rgb_leds.h" + +#ifdef ARDUINO_ARCH_ESP32 +extern int esp32PwmChannelCouner; +#endif + +Supla::Control::RGBLeds::RGBLeds(int redPin, int greenPin, int bluePin) + : redPin(redPin), greenPin(greenPin), bluePin(bluePin) { +} + +void Supla::Control::RGBLeds::setRGBWValueOnDevice(uint32_t red, + uint32_t green, + uint32_t blue, + uint32_t colorBrightness, + uint32_t brightness) { + uint32_t redAdj = red * colorBrightness / 1023; + uint32_t greenAdj = green * colorBrightness / 1023; + uint32_t blueAdj = blue * colorBrightness / 1023; + +#ifdef ARDUINO_ARCH_AVR + redAdj = map(redAdj, 0, 1023, 0, 255); + greenAdj = map(greenAdj, 0, 1023, 0, 255); + blueAdj = map(blueAdj, 0, 1023, 0, 255); +#endif + +#ifdef ARDUINO_ARCH_ESP32 + ledcWrite(redPin, redAdj); + ledcWrite(greenPin, greenAdj); + ledcWrite(bluePin, blueAdj); +#else + analogWrite(redPin, redAdj); + analogWrite(greenPin, greenAdj); + analogWrite(bluePin, blueAdj); +#endif +} + +void Supla::Control::RGBLeds::onInit() { +#ifdef ARDUINO_ARCH_ESP32 + Serial.print(F("RGB: attaching pin ")); + Serial.print(redPin); + Serial.print(F(" to PWM channel: ")); + Serial.println(esp32PwmChannelCouner); + + ledcSetup(esp32PwmChannelCouner, 12000, 10); + ledcAttachPin(redPin, esp32PwmChannelCouner); + // on ESP32 we write to PWM channels instead of pins, so we copy channel + // number as pin in order to reuse variable + redPin = esp32PwmChannelCouner; + esp32PwmChannelCouner++; + + ledcSetup(esp32PwmChannelCouner, 12000, 10); + ledcAttachPin(greenPin, esp32PwmChannelCouner); + greenPin = esp32PwmChannelCouner; + esp32PwmChannelCouner++; + + ledcSetup(esp32PwmChannelCouner, 12000, 10); + ledcAttachPin(bluePin, esp32PwmChannelCouner); + bluePin = esp32PwmChannelCouner; + esp32PwmChannelCouner++; + +#else + pinMode(redPin, OUTPUT); + pinMode(greenPin, OUTPUT); + pinMode(bluePin, OUTPUT); + +#ifdef ARDUINO_ARCH_ESP8266 + analogWriteRange(1024); +#endif +#endif + + Supla::Control::RGBBase::onInit(); +} diff --git a/lib/SuplaDevice/src/supla/control/rgb_leds.h b/lib/SuplaDevice/src/supla/control/rgb_leds.h new file mode 100644 index 00000000..bebddd3c --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/rgb_leds.h @@ -0,0 +1,45 @@ +/* +Copyright (C) AC SOFTWARE SP. Z O.O. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef __rgb_leds_h +#define __rgb_leds_h + +#include "rgb_base.h" + +namespace Supla { +namespace Control { +class RGBLeds : public RGBBase { + public: + RGBLeds(int redPin, int greenPin, int bluePin); + + void setRGBWValueOnDevice(uint32_t red, + uint32_t green, + uint32_t blue, + uint32_t colorBrightness, + uint32_t brightness); + + void onInit(); + + protected: + int redPin; + int greenPin; + int bluePin; +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/control/rgbw_base.cpp b/lib/SuplaDevice/src/supla/control/rgbw_base.cpp index 461b69b8..228b5a44 100644 --- a/lib/SuplaDevice/src/supla/control/rgbw_base.cpp +++ b/lib/SuplaDevice/src/supla/control/rgbw_base.cpp @@ -17,13 +17,23 @@ #include #include +#include "../storage/storage.h" #include "rgbw_base.h" +#define RGBW_STATE_ON_INIT_RESTORE -1 +#define RGBW_STATE_ON_INIT_OFF 0 +#define RGBW_STATE_ON_INIT_ON 1 + +#ifdef ARDUINO_ARCH_ESP32 + int esp32PwmChannelCouner = 0; +#endif + + namespace Supla { namespace Control { RGBWBase::RGBWBase() - : buttonStep(10), + : buttonStep(5), curRed(0), curGreen(255), curBlue(0), @@ -34,25 +44,40 @@ RGBWBase::RGBWBase() defaultDimmedBrightness(20), dimIterationDirection(false), iterationDelayCounter(0), - fadeEffect(1000), - hwRed(0), - hwGreen(255), + fadeEffect(500), + hwRed(-1), + hwGreen(0), hwBlue(0), hwColorBrightness(0), hwBrightness(0), - lastTick(0) { + lastTick(0), + lastMsgReceivedMs(0), + stateOnInit(RGBW_STATE_ON_INIT_RESTORE) { channel.setType(SUPLA_CHANNELTYPE_DIMMERANDRGBLED); channel.setDefault(SUPLA_CHANNELFNC_DIMMERANDRGBLIGHTING); } +void RGBWBase::setRGBW(int red, + int green, + int blue, + int colorBrightness, + int brightness, + bool toggle) { + if (toggle) { + lastMsgReceivedMs = 1; + } else { + lastMsgReceivedMs = millis(); + } -void RGBWBase::setRGBW( - int red, int green, int blue, int colorBrightness, int brightness) { // Store last non 0 brightness for turn on/toggle operations - if (colorBrightness > 0) { + if (toggle && colorBrightness == 100 && curColorBrightness == 0) { + colorBrightness = lastColorBrightness; + } else if (colorBrightness > 0) { lastColorBrightness = colorBrightness; } - if (brightness > 0) { + if (toggle && brightness == 100 && curBrightness == 0) { + brightness = lastBrightness; + } else if (brightness > 0) { lastBrightness = brightness; } @@ -73,25 +98,28 @@ void RGBWBase::setRGBW( curBrightness = brightness; } - // If fade effect is disabled, then set new values to device directly - if (fadeEffect <= 0) { - setRGBWValueOnDevice( + // Schedule save in 5 s after state change + Supla::Storage::ScheduleSave(5000); +} + +void RGBWBase::iterateAlways() { + if (lastMsgReceivedMs != 0 && millis() - lastMsgReceivedMs > 400) { + lastMsgReceivedMs = 0; + // Send to Supla server new values + channel.setNewValue( curRed, curGreen, curBlue, curColorBrightness, curBrightness); } - - // Send to Supla server new values - channel.setNewValue( - curRed, curGreen, curBlue, curColorBrightness, curBrightness); } int RGBWBase::handleNewValueFromServer(TSD_SuplaChannelNewValue *newValue) { + uint8_t toggle = static_cast(newValue->value[5]); uint8_t red = static_cast(newValue->value[4]); uint8_t green = static_cast(newValue->value[3]); uint8_t blue = static_cast(newValue->value[2]); uint8_t colorBrightness = static_cast(newValue->value[1]); uint8_t brightness = static_cast(newValue->value[0]); - setRGBW(red, green, blue, colorBrightness, brightness); + setRGBW(red, green, blue, colorBrightness, brightness, toggle == 1); return -1; } @@ -121,7 +149,7 @@ uint8_t RGBWBase::addWithLimit(int value, int addition, int limit) { return value + addition; } -void RGBWBase::runAction(int event, int action) { +void RGBWBase::handleAction(int event, int action) { (void)(event); switch (action) { case TURN_ON: { @@ -302,14 +330,10 @@ void RGBWBase::iterateDimmerRGBW(int rgbStep, int wStep) { } setRGBW(-1, - -1, - -1, - addWithLimit(curColorBrightness, rgbStep, 100), - addWithLimit(curBrightness, wStep, 100)); -} - -Channel *RGBWBase::getChannel() { - return &channel; + -1, + -1, + addWithLimit(curColorBrightness, rgbStep, 100), + addWithLimit(curBrightness, wStep, 100)); } void RGBWBase::setStep(int step) { @@ -325,104 +349,166 @@ void RGBWBase::setFadeEffectTime(int timeMs) { } void RGBWBase::onTimer() { - // exit it fade effect is disabled - if (fadeEffect <= 0) { - return; - } unsigned long timeDiff = millis() - lastTick; lastTick = millis(); if (timeDiff > 0) { - int divider = fadeEffect / timeDiff; + double divider = 1.0* fadeEffect / timeDiff; if (divider <= 0) { divider = 1; } - uint8_t rgbStep = 255 / divider; - uint8_t brightnessStep = 100 / divider; + double step = 1023 / divider; bool valueChanged = false; - if (rgbStep < 1) { - rgbStep = 1; - } - if (brightnessStep < 1) { - brightnessStep = 1; + if (step < 1) { + step = 1; } - if (curRed > hwRed) { + int curRedAdj = map(curRed, 0, 255, 0, 1023); + int curGreenAdj = map(curGreen, 0, 255, 0 , 1023); + int curBlueAdj = map(curBlue, 0, 255, 0, 1023); + int curColorBrightnessAdj = map(curColorBrightness, 0, 100, 0, 1023); + int curBrightnessAdj = map(curBrightness, 0, 100, 0, 1023); + + if (curRedAdj > hwRed) { valueChanged = true; - hwRed += rgbStep; - if (hwRed > curRed) { - hwRed = curRed; + hwRed += step; + if (hwRed > curRedAdj) { + hwRed = curRedAdj; } - } else if (curRed < hwRed) { + } else if (curRedAdj < hwRed) { valueChanged = true; - hwRed -= rgbStep; - if (hwRed < curRed) { - hwRed = curRed; + hwRed -= step; + if (hwRed < curRedAdj) { + hwRed = curRedAdj; } } - if (curGreen > hwGreen) { + if (curGreenAdj > hwGreen) { valueChanged = true; - hwGreen += rgbStep; - if (hwGreen > curGreen) { - hwGreen = curGreen; + hwGreen += step; + if (hwGreen > curGreenAdj) { + hwGreen = curGreenAdj; } - } else if (curGreen < hwGreen) { + } else if (curGreenAdj < hwGreen) { valueChanged = true; - hwGreen -= rgbStep; - if (hwGreen < curGreen) { - hwGreen = curGreen; + hwGreen -= step; + if (hwGreen < curGreenAdj) { + hwGreen = curGreenAdj; } } - if (curBlue > hwBlue) { + if (curBlueAdj > hwBlue) { valueChanged = true; - hwBlue += rgbStep; - if (hwBlue > curBlue) { - hwBlue = curBlue; + hwBlue += step; + if (hwBlue > curBlueAdj) { + hwBlue = curBlueAdj; } - } else if (curBlue < hwBlue) { + } else if (curBlueAdj < hwBlue) { valueChanged = true; - hwBlue -= rgbStep; - if (hwBlue < curBlue) { - hwBlue = curBlue; + hwBlue -= step; + if (hwBlue < curBlueAdj) { + hwBlue = curBlueAdj; } } - if (curColorBrightness > hwColorBrightness) { + if (curColorBrightnessAdj > hwColorBrightness) { valueChanged = true; - hwColorBrightness += brightnessStep; - if (hwColorBrightness > curColorBrightness) { - hwColorBrightness = curColorBrightness; + hwColorBrightness += step; + if (hwColorBrightness > curColorBrightnessAdj) { + hwColorBrightness = curColorBrightnessAdj; } - } else if (curColorBrightness < hwColorBrightness) { + } else if (curColorBrightnessAdj < hwColorBrightness) { valueChanged = true; - hwColorBrightness -= brightnessStep; - if (hwColorBrightness < curColorBrightness) { - hwColorBrightness = curColorBrightness; + hwColorBrightness -= step; + if (hwColorBrightness < curColorBrightnessAdj) { + hwColorBrightness = curColorBrightnessAdj; } } - if (curBrightness > hwBrightness) { + if (curBrightnessAdj > hwBrightness) { valueChanged = true; - hwBrightness += brightnessStep; - if (hwBrightness > curBrightness) { - hwBrightness = curBrightness; + hwBrightness += step; + if (hwBrightness > curBrightnessAdj) { + hwBrightness = curBrightnessAdj; } - } else if (curBrightness < hwBrightness) { + } else if (curBrightnessAdj < hwBrightness) { valueChanged = true; - hwBrightness -= brightnessStep; - if (hwBrightness < curBrightness) { - hwBrightness = curBrightness; + hwBrightness -= step; + if (hwBrightness < curBrightnessAdj) { + hwBrightness = curBrightnessAdj; } } if (valueChanged) { - setRGBWValueOnDevice(hwRed, hwGreen, hwBlue, hwColorBrightness, hwBrightness); + setRGBWValueOnDevice( + hwRed, hwGreen, hwBlue, hwColorBrightness, hwBrightness); } } } +void RGBWBase::onInit() { + if (stateOnInit == RGBW_STATE_ON_INIT_ON) { + curColorBrightness = 100; + curBrightness = 100; + } else if (stateOnInit == RGBW_STATE_ON_INIT_OFF) { + curColorBrightness = 0; + curBrightness = 0; + } + + setRGBW(curRed, curGreen, curBlue, curColorBrightness, curBrightness); +} + +void RGBWBase::onSaveState() { + /* + uint8_t curRed; // 0 - 255 + uint8_t curGreen; // 0 - 255 + uint8_t curBlue; // 0 - 255 + uint8_t curColorBrightness; // 0 - 100 + uint8_t curBrightness; // 0 - 100 + uint8_t lastColorBrightness; // 0 - 100 + uint8_t lastBrightness; // 0 - 100 + */ + Supla::Storage::WriteState((unsigned char *)&curRed, sizeof(curRed)); + Supla::Storage::WriteState((unsigned char *)&curGreen, sizeof(curGreen)); + Supla::Storage::WriteState((unsigned char *)&curBlue, sizeof(curBlue)); + Supla::Storage::WriteState((unsigned char *)&curColorBrightness, + sizeof(curColorBrightness)); + Supla::Storage::WriteState((unsigned char *)&curBrightness, + sizeof(curBrightness)); + Supla::Storage::WriteState((unsigned char *)&lastColorBrightness, + sizeof(lastColorBrightness)); + Supla::Storage::WriteState((unsigned char *)&lastBrightness, sizeof(lastBrightness)); +} + +void RGBWBase::onLoadState() { + Supla::Storage::ReadState((unsigned char *)&curRed, sizeof(curRed)); + Supla::Storage::ReadState((unsigned char *)&curGreen, sizeof(curGreen)); + Supla::Storage::ReadState((unsigned char *)&curBlue, sizeof(curBlue)); + Supla::Storage::ReadState((unsigned char *)&curColorBrightness, + sizeof(curColorBrightness)); + Supla::Storage::ReadState((unsigned char *)&curBrightness, + sizeof(curBrightness)); + Supla::Storage::ReadState((unsigned char *)&lastColorBrightness, + sizeof(lastColorBrightness)); + Supla::Storage::ReadState((unsigned char *)&lastBrightness, sizeof(lastBrightness)); + +} + +RGBWBase &RGBWBase::setDefaultStateOn() { + stateOnInit = RGBW_STATE_ON_INIT_ON; + return *this; +} + +RGBWBase &RGBWBase::setDefaultStateOff() { + stateOnInit = RGBW_STATE_ON_INIT_OFF; + return *this; +} + +RGBWBase &RGBWBase::setDefaultStateRestore() { + stateOnInit = RGBW_STATE_ON_INIT_RESTORE; + return *this; +} + }; // namespace Control }; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/control/rgbw_base.h b/lib/SuplaDevice/src/supla/control/rgbw_base.h index 452a66cf..c6d3ac41 100644 --- a/lib/SuplaDevice/src/supla/control/rgbw_base.h +++ b/lib/SuplaDevice/src/supla/control/rgbw_base.h @@ -20,48 +20,52 @@ #include #include -#include "../channel.h" -#include "../element.h" -#include "../triggerable.h" +#include "../action_handler.h" #include "../actions.h" +#include "../channel_element.h" namespace Supla { namespace Control { -class RGBWBase : public Element, public Triggerable { +class RGBWBase : public ChannelElement, public ActionHandler { public: RGBWBase(); - virtual void setRGBWValueOnDevice(uint8_t red, - uint8_t green, - uint8_t blue, - uint8_t colorBrightness, - uint8_t brightness) = 0; + virtual void setRGBWValueOnDevice(uint32_t red, + uint32_t green, + uint32_t blue, + uint32_t colorBrightness, + uint32_t brightness) = 0; - virtual void setRGBW( - int red, int green, int blue, int colorBrightness, int brightness); + virtual void setRGBW(int red, + int green, + int blue, + int colorBrightness, + int brightness, + bool toggle = false); int handleNewValueFromServer(TSD_SuplaChannelNewValue *newValue); virtual void turnOn(); virtual void turnOff(); virtual void toggle(); - void runAction(int event, int action); + void handleAction(int event, int action); void setStep(int step); void setDefaultDimmedBrightness(int dimmedBrightness); void setFadeEffectTime(int timeMs); + + void onInit(); + void iterateAlways(); void onTimer(); + void onLoadState(); + void onSaveState(); -void onInit() { + virtual RGBWBase &setDefaultStateOn(); + virtual RGBWBase &setDefaultStateOff(); + virtual RGBWBase &setDefaultStateRestore(); - // Send to Supla server new values - channel.setNewValue( - curRed, curGreen, curBlue, curColorBrightness, curBrightness); -} protected: uint8_t addWithLimit(int value, int addition, int limit = 255); - Channel *getChannel(); - void iterateDimmerRGBW(int rgbStep, int wStep); + virtual void iterateDimmerRGBW(int rgbStep, int wStep); - Channel channel; uint8_t buttonStep; // 10 uint8_t curRed; // 0 - 255 uint8_t curGreen; // 0 - 255 @@ -74,13 +78,14 @@ void onInit() { bool dimIterationDirection; int iterationDelayCounter; int fadeEffect; - int hwRed; // 0 - 255 - int hwGreen; // 0 - 255 - int hwBlue; // 0 - 255 - int hwColorBrightness; // 0 - 100 - int hwBrightness; // 0 - 100 + int hwRed; // 0 - 255 + int hwGreen; // 0 - 255 + int hwBlue; // 0 - 255 + int hwColorBrightness; // 0 - 100 + int hwBrightness; // 0 - 100 unsigned long lastTick; - + unsigned long lastMsgReceivedMs; + int8_t stateOnInit; }; }; // namespace Control diff --git a/lib/SuplaDevice/src/supla/control/rgbw_leds.cpp b/lib/SuplaDevice/src/supla/control/rgbw_leds.cpp new file mode 100644 index 00000000..11f85f9d --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/rgbw_leds.cpp @@ -0,0 +1,105 @@ +/* +Copyright (C) AC SOFTWARE SP. Z O.O. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "rgbw_leds.h" +#include + +#ifdef ARDUINO_ARCH_ESP32 +extern int esp32PwmChannelCouner; +#endif + +Supla::Control::RGBWLeds::RGBWLeds(int redPin, + int greenPin, + int bluePin, + int brightnessPin) + : redPin(redPin), + greenPin(greenPin), + bluePin(bluePin), + brightnessPin(brightnessPin) { +} + +void Supla::Control::RGBWLeds::setRGBWValueOnDevice(uint32_t red, + uint32_t green, + uint32_t blue, + uint32_t colorBrightness, + uint32_t brightness) { + uint32_t redAdj = red * colorBrightness / 1023; + uint32_t greenAdj = green * colorBrightness / 1023; + uint32_t blueAdj = blue * colorBrightness / 1023; + uint32_t brightnessAdj = brightness; + +#ifdef ARDUINO_ARCH_AVR + redAdj = map(redAdj, 0, 1023, 0, 255); + greenAdj = map(greenAdj, 0, 1023, 0, 255); + blueAdj = map(blueAdj, 0, 1023, 0, 255); + brightnessAdj = map(brightnessAdj, 0, 1023, 0, 255); +#endif + +#ifdef ARDUINO_ARCH_ESP32 + ledcWrite(redPin, redAdj); + ledcWrite(greenPin, greenAdj); + ledcWrite(bluePin, blueAdj); + ledcWrite(brightnessPin, brightnessAdj); +#else + analogWrite(redPin, redAdj); + analogWrite(greenPin, greenAdj); + analogWrite(bluePin, blueAdj); + analogWrite(brightnessPin, brightnessAdj); +#endif +} + +void Supla::Control::RGBWLeds::onInit() { +#ifdef ARDUINO_ARCH_ESP32 + Serial.print(F("RGBW: attaching pin ")); + Serial.print(redPin); + Serial.print(F(" to PWM channel: ")); + Serial.println(esp32PwmChannelCouner); + + ledcSetup(esp32PwmChannelCouner, 12000, 10); + ledcAttachPin(redPin, esp32PwmChannelCouner); + // on ESP32 we write to PWM channels instead of pins, so we copy channel + // number as pin in order to reuse variable + redPin = esp32PwmChannelCouner; + esp32PwmChannelCouner++; + + ledcSetup(esp32PwmChannelCouner, 12000, 10); + ledcAttachPin(greenPin, esp32PwmChannelCouner); + greenPin = esp32PwmChannelCouner; + esp32PwmChannelCouner++; + + ledcSetup(esp32PwmChannelCouner, 12000, 10); + ledcAttachPin(bluePin, esp32PwmChannelCouner); + bluePin = esp32PwmChannelCouner; + esp32PwmChannelCouner++; + + ledcSetup(esp32PwmChannelCouner, 12000, 10); + ledcAttachPin(brightnessPin, esp32PwmChannelCouner); + brightnessPin = esp32PwmChannelCouner; + esp32PwmChannelCouner++; + +#else + pinMode(redPin, OUTPUT); + pinMode(greenPin, OUTPUT); + pinMode(bluePin, OUTPUT); + pinMode(brightnessPin, OUTPUT); + + #ifdef ARDUINO_ARCH_ESP8266 + analogWriteRange(1024); + #endif +#endif + + Supla::Control::RGBWBase::onInit(); +} diff --git a/lib/SuplaDevice/src/supla/control/rgbw_leds.h b/lib/SuplaDevice/src/supla/control/rgbw_leds.h new file mode 100644 index 00000000..b3133e99 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/rgbw_leds.h @@ -0,0 +1,49 @@ +/* +Copyright (C) AC SOFTWARE SP. Z O.O. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef __rgbw_leds_h +#define __rgbw_leds_h + +#include "rgbw_base.h" + +namespace Supla { +namespace Control { +class RGBWLeds : public RGBWBase { + public: + RGBWLeds(int redPin, + int greenPin, + int bluePin, + int brightnessPin); + + void setRGBWValueOnDevice(uint32_t red, + uint32_t green, + uint32_t blue, + uint32_t colorBrightness, + uint32_t brightness); + + void onInit(); + + protected: + int redPin; + int greenPin; + int bluePin; + int brightnessPin; +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/control/roller_shutter.cpp b/lib/SuplaDevice/src/supla/control/roller_shutter.cpp index 630fb105..c249ef34 100644 --- a/lib/SuplaDevice/src/supla/control/roller_shutter.cpp +++ b/lib/SuplaDevice/src/supla/control/roller_shutter.cpp @@ -14,8 +14,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include + #include "roller_shutter.h" -#include "supla/storage/storage.h" namespace Supla { namespace Control { @@ -53,10 +54,12 @@ RollerShutter::RollerShutter(int pinUp, int pinDown, bool highIsOn) } void RollerShutter::onInit() { - pinMode(pinUp, OUTPUT); - pinMode(pinDown, OUTPUT); - digitalWrite(pinUp, highIsOn ? LOW : HIGH); - digitalWrite(pinDown, highIsOn ? LOW : HIGH); + Supla::Io::digitalWrite( + channel.getChannelNumber(), pinUp, highIsOn ? LOW : HIGH); + Supla::Io::digitalWrite( + channel.getChannelNumber(), pinDown, highIsOn ? LOW : HIGH); + Supla::Io::pinMode(channel.getChannelNumber(), pinUp, OUTPUT); + Supla::Io::pinMode(channel.getChannelNumber(), pinDown, OUTPUT); } /* @@ -124,7 +127,7 @@ void RollerShutter::setOpenCloseTime(uint32_t newClosingTimeMs, } } -void RollerShutter::runAction(int event, int action) { +void RollerShutter::handleAction(int event, int action) { (void)(event); switch (action) { case CLOSE_OR_STOP: { @@ -268,22 +271,28 @@ void RollerShutter::stopMovement() { switchOffRelays(); currentDirection = STOP_DIR; doNothingTime = millis(); + // Schedule save in 5 s after stop movement of roller shutter + Supla::Storage::ScheduleSave(5000); } void RollerShutter::relayDownOn() { - digitalWrite(pinDown, highIsOn ? HIGH : LOW); + Supla::Io::digitalWrite( + channel.getChannelNumber(), pinDown, highIsOn ? HIGH : LOW); } void RollerShutter::relayUpOn() { - digitalWrite(pinUp, highIsOn ? HIGH : LOW); + Supla::Io::digitalWrite( + channel.getChannelNumber(), pinUp, highIsOn ? HIGH : LOW); } void RollerShutter::relayDownOff() { - digitalWrite(pinDown, highIsOn ? LOW : HIGH); + Supla::Io::digitalWrite( + channel.getChannelNumber(), pinDown, highIsOn ? LOW : HIGH); } void RollerShutter::relayUpOff() { - digitalWrite(pinUp, highIsOn ? LOW : HIGH); + Supla::Io::digitalWrite( + channel.getChannelNumber(), pinUp, highIsOn ? LOW : HIGH); } void RollerShutter::startClosing() { @@ -383,7 +392,7 @@ void RollerShutter::onTimer() { // just handle roller movement/status if (currentDirection == UP_DIR && currentPosition > 0) { int movementDistance = lastPositionBeforeMovement; - int timeRequired = (1.0 * openingTimeMs * movementDistance / 100.0); + uint32_t timeRequired = (1.0 * openingTimeMs * movementDistance / 100.0); float fractionOfMovemendDone = (1.0 * (millis() - lastMovementStartTime) / timeRequired); if (fractionOfMovemendDone > 1) { @@ -396,7 +405,7 @@ void RollerShutter::onTimer() { } } else if (currentDirection == DOWN_DIR && currentPosition < 100) { int movementDistance = 100 - lastPositionBeforeMovement; - int timeRequired = (1.0 * closingTimeMs * movementDistance / 100.0); + uint32_t timeRequired = (1.0 * closingTimeMs * movementDistance / 100.0); float fractionOfMovemendDone = (1.0 * (millis() - lastMovementStartTime) / timeRequired); if (fractionOfMovemendDone > 1) { @@ -453,14 +462,10 @@ void RollerShutter::onTimer() { } // if (newCurrentPosition != currentPosition) { // currentPosition = newCurrentPosition; - channel.setNewValue( - currentPosition); // value set on channel will be send to server - // during iterateConnected() execution - // } -} - -Channel *RollerShutter::getChannel() { - return &channel; + channel.setNewValue(static_cast<_supla_int_t>( + currentPosition)); // value set on channel will be send to server + // during iterateConnected() execution + // } } void RollerShutter::configComfortUpValue(uint8_t position) { diff --git a/lib/SuplaDevice/src/supla/control/roller_shutter.h b/lib/SuplaDevice/src/supla/control/roller_shutter.h index 26e46f81..d9a55233 100644 --- a/lib/SuplaDevice/src/supla/control/roller_shutter.h +++ b/lib/SuplaDevice/src/supla/control/roller_shutter.h @@ -20,9 +20,8 @@ #include #include "../io.h" -#include "../channel.h" -#include "../element.h" -#include "../triggerable.h" +#include "../channel_element.h" +#include "../action_handler.h" #include "../actions.h" #define UNKNOWN_POSITION -1 @@ -35,12 +34,12 @@ namespace Control { enum Directions { STOP_DIR, DOWN_DIR, UP_DIR }; -class RollerShutter : public Element, public Triggerable { +class RollerShutter : public ChannelElement, public ActionHandler { public: RollerShutter(int pinUp, int pinDown, bool highIsOn = true); int handleNewValueFromServer(TSD_SuplaChannelNewValue *newValue); - void runAction(int event, int action); + void handleAction(int event, int action); void close(); // Sets target position to 100% void open(); // Sets target position to 0% @@ -74,10 +73,6 @@ class RollerShutter : public Element, public Triggerable { bool lastDirectionWasClose(); bool inMove(); - Channel *getChannel(); - - Channel channel; - uint32_t closingTimeMs; uint32_t openingTimeMs; bool calibrate; // set to true when new closing/opening time is given - calibration is done to sync roller shutter position diff --git a/lib/SuplaDevice/src/supla/control/sequence_button.cpp b/lib/SuplaDevice/src/supla/control/sequence_button.cpp new file mode 100644 index 00000000..f86dd4c8 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/sequence_button.cpp @@ -0,0 +1,137 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sequence_button.h" +#include + +Supla::Control::SequenceButton::SequenceButton(int pin, bool pullUp, bool invertLogic) + : SimpleButton(pin, pullUp, invertLogic), + lastStateChangeMs(0), + longestSequenceTimeDeltaWithMargin(800), + clickCounter(0), + sequenceDetectecion(true), + currentSequence(), + matchSequence(), + margin(0.3) { +} + +void Supla::Control::SequenceButton::onTimer() { + unsigned int timeDelta = millis() - lastStateChangeMs; + bool stateChanged = false; + int stateResult = state.update(); + if (stateResult == TO_PRESSED) { + stateChanged = true; + runAction(ON_PRESS); + runAction(ON_CHANGE); + } else if (stateResult == TO_RELEASED) { + stateChanged = true; + runAction(ON_RELEASE); + runAction(ON_CHANGE); + } + + if (stateChanged) { + lastStateChangeMs = millis(); + if (clickCounter > 0 && clickCounter < SEQUENCE_MAX_SIZE + 1) { + currentSequence.data[clickCounter - 1] = timeDelta; + } + if (clickCounter == 0) { + memset(currentSequence.data, 0, sizeof(uint16_t [SEQUENCE_MAX_SIZE])); + } + clickCounter++; + } + + if (!stateChanged) { + if (clickCounter > 0 && stateResult == RELEASED) { + if (timeDelta > longestSequenceTimeDeltaWithMargin) { + Serial.print(F("Recorded sequence: ")); + if (clickCounter > 31) { + clickCounter = 31; + } + for (int i = 0; i < clickCounter - 1; i++) { + Serial.print(currentSequence.data[i]); + Serial.print(F(", ")); + } + Serial.println(); + + int matchSequenceSize = 0; + for (; matchSequenceSize < 30; matchSequenceSize++) { + if (matchSequence.data[matchSequenceSize] == 0) { + break; + } + } + if (matchSequenceSize != clickCounter - 1) { + Serial.println(F("Sequence size doesn't match")); + runAction(ON_SEQUENCE_DOESNT_MATCH); + } else { + bool match = true; + for (int i = 0; i < clickCounter - 1; i++) { + unsigned int marginValue = calculateMargin(matchSequence.data[i]); + if (!(matchSequence.data[i] - marginValue <= currentSequence.data[i] && matchSequence.data[i] + marginValue >= currentSequence.data[i])) { + match = false; + break; + } + } + if (match) { + Serial.println(F("Sequence match")); + runAction(ON_SEQUENCE_MATCH); + } else { + Serial.println(F("Sequence doesn't match")); + runAction(ON_SEQUENCE_DOESNT_MATCH); + } + + } + clickCounter = 0; + } + } + } + +} + +unsigned int Supla::Control::SequenceButton::calculateMargin(unsigned int value) { + unsigned int result = margin*value; + if (result < 20) { + result = 20; + } + return result; +} + +void Supla::Control::SequenceButton::setMargin(float newMargin) { + margin = newMargin; + if (margin < 0) { + margin = 0; + } else if (margin > 1) { + margin = 1; + } +} + +void Supla::Control::SequenceButton::setSequence(uint16_t *sequence) { + uint16_t maxValue = 0; + for (int i = 0; i < SEQUENCE_MAX_SIZE; i++) { + matchSequence.data[i] = sequence[i]; + if (sequence[i] > maxValue) { + maxValue = sequence[i]; + } + } + maxValue *= 1.5; + if (maxValue < 500) { + maxValue = 500; + } + longestSequenceTimeDeltaWithMargin = maxValue; +} + +void Supla::Control::SequenceButton::getLastRecordedSequence(uint16_t *sequence) { + memcpy(sequence, currentSequence.data, sizeof(uint16_t [SEQUENCE_MAX_SIZE])); +} diff --git a/lib/SuplaDevice/src/supla/control/sequence_button.h b/lib/SuplaDevice/src/supla/control/sequence_button.h new file mode 100644 index 00000000..d4e12b12 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/sequence_button.h @@ -0,0 +1,58 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _sequence_button_h +#define _sequence_button_h + +#include "button.h" + +namespace Supla { +namespace Control { + +#define SEQUENCE_MAX_SIZE 30 + +struct ClickSequence { + uint16_t data[SEQUENCE_MAX_SIZE]; +}; + +class SequenceButton : public SimpleButton { + public: + SequenceButton(int pin, bool pullUp = false, bool invertLogic = false); + + void onTimer(); + + void setSequence(uint16_t *sequence); + void setMargin(float); + void getLastRecordedSequence(uint16_t *sequence); + + protected: + unsigned long lastStateChangeMs; + uint16_t longestSequenceTimeDeltaWithMargin; + uint8_t clickCounter; + bool sequenceDetectecion; + + ClickSequence currentSequence; + ClickSequence matchSequence; + + float margin; + unsigned int calculateMargin(unsigned int); + +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/control/simple_button.cpp b/lib/SuplaDevice/src/supla/control/simple_button.cpp new file mode 100644 index 00000000..2bbbd387 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/simple_button.cpp @@ -0,0 +1,109 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "button.h" +#include "../io.h" + +Supla::Control::ButtonState::ButtonState(int pin, bool pullUp, bool invertLogic) + : debounceTimeMs(0), + filterTimeMs(0), + debounceDelayMs(50), + swNoiseFilterDelayMs(20), + pin(pin), + newStatusCandidate(LOW), + prevState(LOW), + pullUp(pullUp), + invertLogic(invertLogic) { +} + +int Supla::Control::ButtonState::update() { + unsigned long curMillis = millis(); + if (debounceDelayMs == 0 || curMillis - debounceTimeMs > debounceDelayMs) { + int currentState = Supla::Io::digitalRead(pin); + if (currentState != prevState) { + // If status is changed, then make sure that it will be kept at + // least swNoiseFilterDelayMs ms to avoid noise + if (swNoiseFilterDelayMs != 0 && currentState != newStatusCandidate) { + newStatusCandidate = currentState; + filterTimeMs = curMillis; + } else if (curMillis - filterTimeMs > swNoiseFilterDelayMs) { + // If new status is kept at least swNoiseFilterDelayMs ms, then apply + // change of status + debounceTimeMs = curMillis; + prevState = currentState; + if (currentState == valueOnPress()) { + return TO_PRESSED; + } else { + return TO_RELEASED; + } + } + } else { + // If current status is the same as prevState, then reset + // new status candidate + newStatusCandidate = prevState; + } + } + if (prevState == valueOnPress()) { + return PRESSED; + } else { + return RELEASED; + } +} + +Supla::Control::SimpleButton::SimpleButton(int pin, bool pullUp, bool invertLogic) + : state(pin, pullUp, invertLogic) { +} + +void Supla::Control::SimpleButton::onTimer() { + int stateResult = state.update(); + if (stateResult == TO_PRESSED) { + runAction(ON_PRESS); + runAction(ON_CHANGE); + } else if (stateResult == TO_RELEASED) { + runAction(ON_RELEASE); + runAction(ON_CHANGE); + } +} + +void Supla::Control::SimpleButton::onInit() { + state.init(); +} + +void Supla::Control::ButtonState::init() { + Supla::Io::pinMode(pin, pullUp ? INPUT_PULLUP : INPUT); + prevState = Supla::Io::digitalRead(pin); + newStatusCandidate = prevState; +} + +int Supla::Control::ButtonState::valueOnPress() { + return invertLogic ? LOW : HIGH; +} + +void Supla::Control::SimpleButton::setSwNoiseFilterDelay(unsigned int newDelayMs) { + state.setSwNoiseFilterDelay(newDelayMs); +} +void Supla::Control::ButtonState::setSwNoiseFilterDelay(unsigned int newDelayMs) { + swNoiseFilterDelayMs = newDelayMs; +} + +void Supla::Control::SimpleButton::setDebounceDelay(unsigned int newDelayMs) { + state.setDebounceDelay(newDelayMs); +} + +void Supla::Control::ButtonState::setDebounceDelay(unsigned int newDelayMs) { + debounceDelayMs = newDelayMs; +} + diff --git a/lib/SuplaDevice/src/supla/control/simple_button.h b/lib/SuplaDevice/src/supla/control/simple_button.h new file mode 100644 index 00000000..8b66fb7e --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/simple_button.h @@ -0,0 +1,71 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _simple_button_h +#define _simple_button_h + +#include + +#include "../element.h" +#include "../events.h" +#include "../local_action.h" + +namespace Supla { +namespace Control { + +enum StateResults {PRESSED, RELEASED, TO_PRESSED, TO_RELEASED}; + +class ButtonState { + public: + ButtonState(int pin, bool pullUp, bool invertLogic); + int update(); + void init(); + + void setSwNoiseFilterDelay(unsigned int newDelayMs); + void setDebounceDelay(unsigned int newDelayMs); + + protected: + int valueOnPress(); + + unsigned long debounceTimeMs; + unsigned long filterTimeMs; + unsigned int debounceDelayMs; + unsigned int swNoiseFilterDelayMs; + int pin; + int8_t newStatusCandidate; + int8_t prevState; + bool pullUp; + bool invertLogic; +}; + +class SimpleButton : public Element, + public LocalAction { + public: + SimpleButton(int pin, bool pullUp = false, bool invertLogic = false); + + void onTimer(); + void onInit(); + void setSwNoiseFilterDelay(unsigned int newDelayMs); + void setDebounceDelay(unsigned int newDelayMs); + + protected: + ButtonState state; +}; + +}; // namespace Control +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/control/virtual_relay.cpp b/lib/SuplaDevice/src/supla/control/virtual_relay.cpp new file mode 100644 index 00000000..9cfc3ea8 --- /dev/null +++ b/lib/SuplaDevice/src/supla/control/virtual_relay.cpp @@ -0,0 +1,57 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "virtual_relay.h" + +Supla::Control::VirtualRelay::VirtualRelay(_supla_int_t functions) + : Relay(-1, true, functions), state(false) { +} + +void Supla::Control::VirtualRelay::onInit() { + if (stateOnInit == STATE_ON_INIT_ON || + stateOnInit == STATE_ON_INIT_RESTORED_ON) { + turnOn(); + } else { + turnOff(); + } +} + +void Supla::Control::VirtualRelay::turnOn(_supla_int_t duration) { + durationMs = duration; + durationTimestamp = millis(); + if (keepTurnOnDurationMs) { + durationMs = storedTurnOnDurationMs; + } + state = true; + + channel.setNewValue(state); + // Schedule save in 5 s after state change + Supla::Storage::ScheduleSave(5000); +} + +void Supla::Control::VirtualRelay::turnOff(_supla_int_t duration) { + durationMs = duration; + durationTimestamp = millis(); + state = false; + + channel.setNewValue(state); + // Schedule save in 5 s after state change + Supla::Storage::ScheduleSave(5000); +} + +bool Supla::Control::VirtualRelay::isOn() { + return state; +} diff --git a/lib/SuplaDevice/src/supla/control/virtual_relay.h b/lib/SuplaDevice/src/supla/control/virtual_relay.h index 1a54c51f..ee51affd 100644 --- a/lib/SuplaDevice/src/supla/control/virtual_relay.h +++ b/lib/SuplaDevice/src/supla/control/virtual_relay.h @@ -24,41 +24,12 @@ namespace Control { class VirtualRelay : public Relay { public: VirtualRelay(_supla_int_t functions = - (0xFF ^ SUPLA_BIT_FUNC_CONTROLLINGTHEROLLERSHUTTER)) - : Relay(-1, true, functions), state(false) { - } + (0xFF ^ SUPLA_BIT_FUNC_CONTROLLINGTHEROLLERSHUTTER)); - void onInit() { - if (stateOnInit == STATE_ON_INIT_ON || - stateOnInit == STATE_ON_INIT_RESTORED_ON) { - turnOn(); - } else { - turnOff(); - } - } - - void turnOn(_supla_int_t duration = 0) { - durationMs = duration; - durationTimestamp = millis(); - if (keepTurnOnDurationMs) { - durationMs = storedTurnOnDurationMs; - } - state = true; - - channel.setNewValue(state); - } - - virtual void turnOff(_supla_int_t duration = 0) { - durationMs = duration; - durationTimestamp = millis(); - state = false; - - channel.setNewValue(state); - } - - virtual bool isOn() { - return state; - } + void onInit(); + void turnOn(_supla_int_t duration = 0); + void turnOff(_supla_int_t duration = 0); + bool isOn(); protected: bool state; diff --git a/lib/SuplaDevice/src/supla/crc16.h b/lib/SuplaDevice/src/supla/crc16.h new file mode 100644 index 00000000..5713f7bd --- /dev/null +++ b/lib/SuplaDevice/src/supla/crc16.h @@ -0,0 +1,14 @@ + +uint16_t crc16_update(uint16_t crc, uint8_t a) { + int i; + + crc ^= a; + for (i = 0; i < 8; ++i) { + if (crc & 1) + crc = (crc >> 1) ^ 0xA001; + else + crc = (crc >> 1); + } + + return crc; +} diff --git a/lib/SuplaDevice/src/supla/element.cpp b/lib/SuplaDevice/src/supla/element.cpp index b7f1b22c..70fc8a11 100644 --- a/lib/SuplaDevice/src/supla/element.cpp +++ b/lib/SuplaDevice/src/supla/element.cpp @@ -29,6 +29,20 @@ Element::Element() : nextPtr(nullptr) { } } +Element::~Element() { + if (begin() == this) { + firstPtr = next(); + return; + } + + auto ptr = begin(); + while (ptr->next() != this) { + ptr = ptr->next(); + } + + ptr->nextPtr = ptr->next()->next(); +} + Element *Element::begin() { return firstPtr; } @@ -95,6 +109,10 @@ Channel *Element::getChannel() { return nullptr; } +Channel *Element::getSecondaryChannel() { + return nullptr; +} + void Element::handleGetChannelState(TDSC_ChannelState &channelState) { (void)(channelState); return; diff --git a/lib/SuplaDevice/src/supla/element.h b/lib/SuplaDevice/src/supla/element.h index f2ee9fdc..34e48cc5 100644 --- a/lib/SuplaDevice/src/supla/element.h +++ b/lib/SuplaDevice/src/supla/element.h @@ -27,6 +27,7 @@ namespace Supla { class Element { public: Element(); + virtual ~Element(); static Element *begin(); static Element *last(); static Element *getElementByChannelNumber(int channelNumber); @@ -76,11 +77,12 @@ class Element { virtual int handleCalcfgFromServer(TSD_DeviceCalCfgRequest *request); int getChannelNumber(); + virtual Channel *getChannel(); + virtual Channel *getSecondaryChannel(); Element &disableChannelState(); protected: - virtual Channel *getChannel(); static Element *firstPtr; Element *nextPtr; }; diff --git a/lib/SuplaDevice/src/supla/events.h b/lib/SuplaDevice/src/supla/events.h index c72c122f..a65b0a4c 100644 --- a/lib/SuplaDevice/src/supla/events.h +++ b/lib/SuplaDevice/src/supla/events.h @@ -34,7 +34,13 @@ enum Event { ON_CLICK_7, ON_CLICK_8, ON_CLICK_9, - ON_CLICK_10 + ON_CLICK_10, + ON_CRAZY_CLICKER, // triggered on >= 10 clicks + ON_SEQUENCE_MATCH, // triggered by SequenceButton + ON_SEQUENCE_DOESNT_MATCH, // triggered by SequenceButton + ON_TURN_ON, + ON_TURN_OFF, + ON_SECONDARY_CHANNEL_CHANGE }; }; diff --git a/lib/SuplaDevice/src/supla/io.cpp b/lib/SuplaDevice/src/supla/io.cpp index 59cccc81..6132709c 100644 --- a/lib/SuplaDevice/src/supla/io.cpp +++ b/lib/SuplaDevice/src/supla/io.cpp @@ -19,6 +19,26 @@ #include namespace Supla { +void Io::pinMode(uint8_t pin, uint8_t mode) { + return pinMode(-1, pin, mode); +} + +int Io::digitalRead(uint8_t pin) { + return digitalRead(-1, pin); +} + +void Io::digitalWrite(uint8_t pin, uint8_t val) { + digitalWrite(-1, pin, val); +} + +void Io::pinMode(int channelNumber, uint8_t pin, uint8_t mode) { + if (ioInstance) { + ioInstance->customPinMode(channelNumber, pin, mode); + } else { + ::pinMode(pin, mode); + } +} + int Io::digitalRead(int channelNumber, uint8_t pin) { if (ioInstance) { return ioInstance->customDigitalRead(channelNumber, pin); @@ -46,6 +66,10 @@ Io::Io() { ioInstance = this; } +Io::~Io() { + ioInstance = nullptr; +} + int Io::customDigitalRead(int channelNumber, uint8_t pin) { (void)(channelNumber); return ::digitalRead(pin); @@ -56,4 +80,9 @@ void Io::customDigitalWrite(int channelNumber, uint8_t pin, uint8_t val) { ::digitalWrite(pin, val); } +void Io::customPinMode(int channelNumber, uint8_t pin, uint8_t mode) { + (void)(channelNumber); + ::pinMode(pin, mode); +} + }; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/io.h b/lib/SuplaDevice/src/supla/io.h index 461f8b6a..918ceb0b 100644 --- a/lib/SuplaDevice/src/supla/io.h +++ b/lib/SuplaDevice/src/supla/io.h @@ -30,12 +30,18 @@ namespace Supla { // changed. class Io { public: + static void pinMode(uint8_t pin, uint8_t mode); + static int digitalRead(uint8_t pin); + static void digitalWrite(uint8_t pin, uint8_t val); + static void pinMode(int channelNumber, uint8_t pin, uint8_t mode); static int digitalRead(int channelNumber, uint8_t pin); static void digitalWrite(int channelNumber, uint8_t pin, uint8_t val); static Io *ioInstance; Io(); + virtual ~Io(); + virtual void customPinMode(int channelNumber, uint8_t pin, uint8_t mode); virtual int customDigitalRead(int channelNumber, uint8_t pin); virtual void customDigitalWrite(int channelNumber, uint8_t pin, uint8_t val); }; diff --git a/lib/SuplaDevice/src/supla/local_action.cpp b/lib/SuplaDevice/src/supla/local_action.cpp index cc4c08eb..478c4bd9 100644 --- a/lib/SuplaDevice/src/supla/local_action.cpp +++ b/lib/SuplaDevice/src/supla/local_action.cpp @@ -18,29 +18,91 @@ namespace Supla { -LocalAction::LocalAction() : registeredClientsCount(0) { -} +class ActionHandlerClient; + +class ActionHandlerClient { + public: + ActionHandlerClient() + : trigger(nullptr), + client(nullptr), + next(nullptr), + onEvent(0), + action(0) { + if (begin == nullptr) { + begin = this; + } else { + auto ptr = begin; + while (ptr->next) { + ptr = ptr->next; + } + ptr->next = this; + } + } + + ~ActionHandlerClient() { + if (begin == this) { + begin = next; + return; + } + + auto ptr = begin; + while (ptr->next != this) { + ptr = ptr->next; + } + + ptr->next = ptr->next->next; + } + + LocalAction *trigger; + ActionHandler *client; + ActionHandlerClient *next; + uint8_t onEvent; + uint8_t action; + static ActionHandlerClient *begin; +}; + +ActionHandlerClient *ActionHandlerClient::begin = nullptr; -void LocalAction::addAction(int action, Triggerable &client, int event) { - if (registeredClientsCount < MAX_TRIGGERABLE_CLIENTS) { - clients[registeredClientsCount].client = &client; - clients[registeredClientsCount].onEvent = event; - clients[registeredClientsCount].action = action; - registeredClientsCount++; +LocalAction::~LocalAction() { + auto ptr = ActionHandlerClient::begin; + while (ptr) { + if (ptr->trigger == this) { + auto tbdptr = ptr; + ptr = ptr->next; + if (tbdptr->client->deleteClient()) { + delete tbdptr->client; + } + delete tbdptr; + } else { + ptr = ptr->next; + } } } -void LocalAction::addAction(int action, Triggerable *client, int event) { +void LocalAction::addAction(int action, ActionHandler &client, int event) { + auto ptr = new ActionHandlerClient; + ptr->trigger = this; + ptr->client = &client; + ptr->onEvent = event; + ptr->action = action; +} + +void LocalAction::addAction(int action, ActionHandler *client, int event) { addAction(action, *client, event); } void LocalAction::runAction(int event) { - for (int i = 0; i < registeredClientsCount; i++) { - if (clients[i].onEvent == event) { - clients[i].client->runAction(event, clients[i].action); + auto ptr = ActionHandlerClient::begin; + while (ptr) { + if (ptr->trigger == this && ptr->onEvent == event) { + ptr->client->handleAction(event, ptr->action); } + ptr = ptr->next; } } -}; // namespace Supla +ActionHandlerClient *LocalAction::getClientListPtr() { + return ActionHandlerClient::begin; +} +}; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/local_action.h b/lib/SuplaDevice/src/supla/local_action.h index 4ec39711..33a91adf 100644 --- a/lib/SuplaDevice/src/supla/local_action.h +++ b/lib/SuplaDevice/src/supla/local_action.h @@ -18,31 +18,21 @@ #define _local_action_h #include -#include "triggerable.h" - -#define MAX_TRIGGERABLE_CLIENTS 10 +#include "action_handler.h" namespace Supla { -class TriggerableClient { - public: - Triggerable *client; - uint8_t onEvent; - uint8_t action; -}; +class ActionHandlerClient; class LocalAction { public: - LocalAction(); - - virtual void addAction(int action, Triggerable &client, int event); - virtual void addAction(int action, Triggerable *client, int event); + virtual ~LocalAction(); + virtual void addAction(int action, ActionHandler &client, int event); + virtual void addAction(int action, ActionHandler *client, int event); virtual void runAction(int event); - protected: - TriggerableClient clients[MAX_TRIGGERABLE_CLIENTS]; - uint8_t registeredClientsCount; + static ActionHandlerClient *getClientListPtr(); }; }; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/network/esp32_wifi.h b/lib/SuplaDevice/src/supla/network/esp32_wifi.h index fa3915d1..e45ee3cc 100644 --- a/lib/SuplaDevice/src/supla/network/esp32_wifi.h +++ b/lib/SuplaDevice/src/supla/network/esp32_wifi.h @@ -14,120 +14,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifndef esp_wifi_h__ -#define esp_wifi_h__ +// DEPRECATED: please use esp_wifi.h instead -#include -#include +#ifndef esp32_wifi_h__ +#define esp32_wifi_h__ -#include "../supla_lib_config.h" -#include "network.h" - -#define MAX_SSID_SIZE 32 -#define MAX_WIFI_PASSWORD_SIZE 64 - -// TODO: change logs to supla_log +#include "esp_wifi.h" namespace Supla { -class ESP32Wifi : public Supla::Network { - public: - ESP32Wifi(const char *wifiSsid, - const char *wifiPassword, - IPAddress *ip = NULL) - : Network(ip) { - strcpy(ssid, wifiSsid); - strcpy(password, wifiPassword); - } - - int read(void *buf, int count) { - _supla_int_t size = client.available(); - - if (size > 0) { - if (size > count) size = count; - long readSize = client.read((uint8_t *)buf, size); -#ifdef SUPLA_COMM_DEBUG - Serial.print(F("Received: [")); - for (int i = 0; i < readSize; i++) { - Serial.print(static_cast(buf)[i], HEX); - Serial.print(F(" ")); - } - Serial.println(F("]")); -#endif - - return readSize; - } - return -1; - } - - int write(void *buf, int count) { -#ifdef SUPLA_COMM_DEBUG - Serial.print(F("Sending: [")); - for (int i = 0; i < count; i++) { - Serial.print(static_cast(buf)[i], HEX); - Serial.print(F(" ")); - } - Serial.println(F("]")); -#endif - long sendSize = client.write((const uint8_t *)buf, count); - return sendSize; - } - - int connect(const char *server, int port = -1) { - int connectionPort = (port == -1 ? 2015 : port); - supla_log( - LOG_DEBUG, "Establishing connection with: %s (port: %d)", server, connectionPort); - return client.connect(server, connectionPort); - } - - bool connected() { - return client.connected(); - } - - bool isReady() { - return WiFi.status() == WL_CONNECTED; - } - - void disconnect() { - client.stop(); - } - - // TODO: add handling of custom local ip - void setup() { - WiFiEventId_t event_gotIP = WiFi.onEvent( - [](WiFiEvent_t event, WiFiEventInfo_t info) { - Serial.print(F("local IP: ")); - Serial.println(WiFi.localIP()); - Serial.print(F("subnetMask: ")); - Serial.println(WiFi.subnetMask()); - Serial.print(F("gatewayIP: ")); - Serial.println(WiFi.gatewayIP()); - long rssi = WiFi.RSSI(); - Serial.print(F("Signal Strength (RSSI): ")); - Serial.print(rssi); - Serial.println(F(" dBm")); - }, - WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP); - - WiFiEventId_t event_disconnected = WiFi.onEvent( - [](WiFiEvent_t event, WiFiEventInfo_t info) { - Serial.println(F("wifi Station disconnected")); - }, - WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED); - - Serial.print(F("WIFI: establishing connection with SSID: \"")); - Serial.print(ssid); - Serial.println(F("\"")); - WiFi.begin(ssid, password); - yield(); - } - - protected: - WiFiClient client; - - char ssid[MAX_SSID_SIZE]; - char password[MAX_WIFI_PASSWORD_SIZE]; +typedef ESPWifi ESP32Wifi; }; -}; // namespace Supla - #endif diff --git a/lib/SuplaDevice/src/supla/network/esp_wifi.h b/lib/SuplaDevice/src/supla/network/esp_wifi.h index b819e23f..b0d60130 100644 --- a/lib/SuplaDevice/src/supla/network/esp_wifi.h +++ b/lib/SuplaDevice/src/supla/network/esp_wifi.h @@ -18,7 +18,13 @@ #define esp_wifi_h__ #include + +#ifdef ARDUINO_ARCH_ESP8266 #include +#else +#include +#endif + #include #include "../supla_lib_config.h" @@ -27,7 +33,9 @@ #define MAX_SSID_SIZE 32 #define MAX_WIFI_PASSWORD_SIZE 64 +#ifdef ARDUINO_ARCH_ESP8266 WiFiEventHandler gotIpEventHandler, disconnectedEventHandler; +#endif // TODO: change logs to supla_log @@ -36,12 +44,16 @@ class ESPWifi : public Supla::Network { public: ESPWifi(const char *wifiSsid = nullptr, const char *wifiPassword = nullptr, - IPAddress *ip = nullptr) + unsigned char *ip = nullptr) : Network(ip), client(nullptr), isSecured(true), wifiConfigured(false) { ssid[0] = '\0'; password[0] = '\0'; setSsid(wifiSsid); setPassword(wifiPassword); +#ifdef ARDUINO_ARCH_ESP32 + enableSSL( + false); // current ESP32 WiFiClientSecure does not suport "setInsecure" +#endif } int read(void *buf, int count) { @@ -55,6 +67,7 @@ class ESPWifi : public Supla::Network { for (int i = 0; i < readSize; i++) { Serial.print(static_cast(buf)[i], HEX); Serial.print(F(" ")); + delay(0); } Serial.println(F("]")); #endif @@ -70,6 +83,7 @@ class ESPWifi : public Supla::Network { for (int i = 0; i < count; i++) { Serial.print(static_cast(buf)[i], HEX); Serial.print(F(" ")); + delay(0); } Serial.println(F("]")); #endif @@ -85,10 +99,18 @@ class ESPWifi : public Supla::Network { client = new WiFiClientSecure(); if (fingerprint.length() > 0) { message += " with certificate matching"; +#ifdef ARDUINO_ARCH_ESP8266 ((WiFiClientSecure *)client)->setFingerprint(fingerprint.c_str()); +#else + message += " - NOT SUPPORTED ON ESP32 implmentation"; +#endif } else { message += " without certificate matching"; +#ifdef ARDUINO_ARCH_ESP8266 ((WiFiClientSecure *)client)->setInsecure(); +#else + message += " - NOT SUPPORTED ON ESP32 implmentation"; +#endif } } else { message = "unsecured connection"; @@ -107,7 +129,7 @@ class ESPWifi : public Supla::Network { server, connectionPort); - // static_cast(client)->setBufferSizes(512, 512); // +// static_cast(client)->setBufferSizes(512, 512); // // EXPERIMENTAL bool result = client->connect(server, connectionPort); @@ -141,30 +163,52 @@ class ESPWifi : public Supla::Network { void setup() { if (!wifiConfigured) { wifiConfigured = true; - gotIpEventHandler = - WiFi.onStationModeGotIP([](const WiFiEventStationModeGotIP &event) { - (void)(event); - Serial.print(F("local IP: ")); - Serial.println(WiFi.localIP()); - Serial.print(F("subnetMask: ")); - Serial.println(WiFi.subnetMask()); - Serial.print(F("gatewayIP: ")); - Serial.println(WiFi.gatewayIP()); - long rssi = WiFi.RSSI(); - Serial.print(F("Signal strength (RSSI): ")); - Serial.print(rssi); - Serial.println(F(" dBm")); - }); - disconnectedEventHandler = WiFi.onStationModeDisconnected( - [](const WiFiEventStationModeDisconnected &event) { - (void)(event); - Serial.println(F("WiFi station disconnected")); - }); - - Serial.print(F("WiFi: establishing connection with SSID: \"")); - Serial.print(ssid); - Serial.println(F("\"")); - WiFi.begin(ssid, password); +#ifdef ARDUINO_ARCH_ESP8266 + gotIpEventHandler = + WiFi.onStationModeGotIP([](const WiFiEventStationModeGotIP &event) { + (void)(event); + Serial.print(F("local IP: ")); + Serial.println(WiFi.localIP()); + Serial.print(F("subnetMask: ")); + Serial.println(WiFi.subnetMask()); + Serial.print(F("gatewayIP: ")); + Serial.println(WiFi.gatewayIP()); + long rssi = WiFi.RSSI(); + Serial.print(F("Signal strength (RSSI): ")); + Serial.print(rssi); + Serial.println(F(" dBm")); + }); + disconnectedEventHandler = WiFi.onStationModeDisconnected( + [](const WiFiEventStationModeDisconnected &event) { + (void)(event); + Serial.println(F("WiFi station disconnected")); + }); +#else + WiFiEventId_t event_gotIP = WiFi.onEvent( + [](WiFiEvent_t event, WiFiEventInfo_t info) { + Serial.print(F("local IP: ")); + Serial.println(WiFi.localIP()); + Serial.print(F("subnetMask: ")); + Serial.println(WiFi.subnetMask()); + Serial.print(F("gatewayIP: ")); + Serial.println(WiFi.gatewayIP()); + long rssi = WiFi.RSSI(); + Serial.print(F("Signal Strength (RSSI): ")); + Serial.print(rssi); + Serial.println(F(" dBm")); + }, + WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP); + + WiFiEventId_t event_disconnected = WiFi.onEvent( + [](WiFiEvent_t event, WiFiEventInfo_t info) { + Serial.println(F("wifi Station disconnected")); + }, + WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED); +#endif + Serial.print(F("WiFi: establishing connection with SSID: \"")); + Serial.print(ssid); + Serial.println(F("\"")); + WiFi.begin(ssid, password); } else { Serial.println(F("WiFi: resetting WiFi connection")); if (client) { diff --git a/lib/SuplaDevice/src/supla/network/ethernet_shield.h b/lib/SuplaDevice/src/supla/network/ethernet_shield.h index 7d5e7e7a..683acf94 100644 --- a/lib/SuplaDevice/src/supla/network/ethernet_shield.h +++ b/lib/SuplaDevice/src/supla/network/ethernet_shield.h @@ -28,7 +28,7 @@ namespace Supla { class EthernetShield : public Supla::Network { public: - EthernetShield(uint8_t mac[6], IPAddress *ip = NULL) : Network(ip), isDeviceReady(false) { + EthernetShield(uint8_t mac[6], unsigned char *ip = NULL) : Network(ip), isDeviceReady(false) { memcpy(this->mac, mac, 6); } diff --git a/lib/SuplaDevice/src/supla/network/network.cpp b/lib/SuplaDevice/src/supla/network/network.cpp index 4cf98681..b8864583 100644 --- a/lib/SuplaDevice/src/supla/network/network.cpp +++ b/lib/SuplaDevice/src/supla/network/network.cpp @@ -145,7 +145,7 @@ void message_received(void *_srpc, } } -Network::Network(IPAddress *ip) { +Network::Network(unsigned char *ip) { lastSentMs = 0; srpc = NULL; lastPingTimeMs = 0; @@ -158,7 +158,7 @@ Network::Network(IPAddress *ip) { useLocalIp = false; } else { useLocalIp = true; - localIp = *ip; + memcpy(localIp, ip, 4); } } diff --git a/lib/SuplaDevice/src/supla/network/network.h b/lib/SuplaDevice/src/supla/network/network.h index 9316eca2..8e5a5da1 100644 --- a/lib/SuplaDevice/src/supla/network/network.h +++ b/lib/SuplaDevice/src/supla/network/network.h @@ -17,8 +17,6 @@ #ifndef _network_interface_h #define _network_interface_h -#include - #include "supla-common/log.h" #include "supla-common/proto.h" @@ -99,7 +97,7 @@ class Network { return false; } - Network(IPAddress *ip); + Network(uint8_t ip[4]); virtual int read(void *buf, int count) = 0; virtual int write(void *buf, int count) = 0; virtual int connect(const char *server, int port = -1) = 0; @@ -129,7 +127,7 @@ class Network { void *srpc; bool useLocalIp; - IPAddress localIp; + unsigned char localIp[4]; }; // Method passed to SRPC as a callback to read raw data from network interface diff --git a/lib/SuplaDevice/src/supla/pv/afore.cpp b/lib/SuplaDevice/src/supla/pv/afore.cpp index 7b6f5484..06c0933f 100644 --- a/lib/SuplaDevice/src/supla/pv/afore.cpp +++ b/lib/SuplaDevice/src/supla/pv/afore.cpp @@ -30,7 +30,9 @@ Afore::Afore(IPAddress ip, int port, const char *loginAndPass) vFound(false), varFound(false), dataIsReady(false), - dataFetchInProgress(false) { + dataFetchInProgress(false), + connectionTimeoutMs(0) { + refreshRateSec = 15; int len = strlen(loginAndPass); if (len > LOGIN_AND_PASSOWORD_MAX_LENGTH) { len = LOGIN_AND_PASSOWORD_MAX_LENGTH; @@ -40,6 +42,13 @@ Afore::Afore(IPAddress ip, int port, const char *loginAndPass) void Afore::iterateAlways() { if (dataFetchInProgress) { + if (millis() - connectionTimeoutMs > 30000) { + Serial.println(F("AFORE: connection timeout. Remote host is not responding")); + pvClient.stop(); + dataFetchInProgress = false; + dataIsReady = false; + return; + } if (!pvClient.connected()) { Serial.println(F("AFORE fetch completed")); dataFetchInProgress = false; @@ -105,12 +114,13 @@ void Afore::iterateAlways() { bool Afore::iterateConnected(void *srpc) { if (!dataFetchInProgress) { - if (lastReadTime == 0 || millis() - lastReadTime > 15000) { + if (lastReadTime == 0 || millis() - lastReadTime > refreshRateSec*1000) { lastReadTime = millis(); Serial.println(F("AFORE connecting")); if (pvClient.connect(ip, port)) { retryCounter = 0; dataFetchInProgress = true; + connectionTimeoutMs = lastReadTime; Serial.println(F("Succesful connect")); pvClient.print("GET /status.html HTTP/1.1\nAuthorization: Basic "); @@ -137,3 +147,4 @@ bool Afore::iterateConnected(void *srpc) { void Afore::readValuesFromDevice() { } + diff --git a/lib/SuplaDevice/src/supla/pv/afore.h b/lib/SuplaDevice/src/supla/pv/afore.h index 85a978e6..bb46c26a 100644 --- a/lib/SuplaDevice/src/supla/pv/afore.h +++ b/lib/SuplaDevice/src/supla/pv/afore.h @@ -55,6 +55,7 @@ class Afore : public Supla::Sensor::OnePhaseElectricityMeter { bool varFound; bool dataIsReady; bool dataFetchInProgress; + unsigned long connectionTimeoutMs; }; }; // namespace PV }; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/pv/fronius.cpp b/lib/SuplaDevice/src/supla/pv/fronius.cpp index 3e2200b4..871f80a3 100644 --- a/lib/SuplaDevice/src/supla/pv/fronius.cpp +++ b/lib/SuplaDevice/src/supla/pv/fronius.cpp @@ -36,11 +36,20 @@ Fronius::Fronius(IPAddress ip, int port, int deviceId) deviceId(deviceId), startCharFound(false), dataIsReady(false), - dataFetchInProgress(false) { + dataFetchInProgress(false), + connectionTimeoutMs(0) { + refreshRateSec = 15; } void Fronius::iterateAlways() { if (dataFetchInProgress) { + if (millis() - connectionTimeoutMs > 30000) { + Serial.println(F("Fronius: connection timeout. Remote host is not responding")); + pvClient.stop(); + dataFetchInProgress = false; + dataIsReady = false; + return; + } if (!pvClient.connected()) { Serial.println(F("Fronius fetch completed")); dataFetchInProgress = false; @@ -147,13 +156,14 @@ void Fronius::iterateAlways() { bool Fronius::iterateConnected(void *srpc) { if (!dataFetchInProgress) { - if (lastReadTime == 0 || millis() - lastReadTime > 15000) { + if (lastReadTime == 0 || millis() - lastReadTime > refreshRateSec*1000) { lastReadTime = millis(); Serial.print(F("Fronius connecting ")); Serial.println(deviceId); if (pvClient.connect(ip, port)) { retryCounter = 0; dataFetchInProgress = true; + connectionTimeoutMs = lastReadTime; Serial.println(F("Succesful connect")); char buf[100]; @@ -189,3 +199,4 @@ bool Fronius::iterateConnected(void *srpc) { void Fronius::readValuesFromDevice() { } + diff --git a/lib/SuplaDevice/src/supla/pv/fronius.h b/lib/SuplaDevice/src/supla/pv/fronius.h index 1f761732..f32d4699 100644 --- a/lib/SuplaDevice/src/supla/pv/fronius.h +++ b/lib/SuplaDevice/src/supla/pv/fronius.h @@ -56,6 +56,7 @@ class Fronius : public Supla::Sensor::OnePhaseElectricityMeter { bool startCharFound; bool dataIsReady; bool dataFetchInProgress; + unsigned long connectionTimeoutMs; }; }; // namespace PV }; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/sensor/DS18B20.h b/lib/SuplaDevice/src/supla/sensor/DS18B20.h index 7b27d211..8e0b1e58 100644 --- a/lib/SuplaDevice/src/supla/sensor/DS18B20.h +++ b/lib/SuplaDevice/src/supla/sensor/DS18B20.h @@ -182,6 +182,11 @@ class DS18B20 : public Thermometer { channel.setNewValue(getValue()); } + + DallasTemperature &getHwSensors() { + return myBus->sensors; + } + protected: static OneWireBus *oneWireBus; OneWireBus *myBus; diff --git a/lib/SuplaDevice/src/supla/sensor/HC_SR04.cpp b/lib/SuplaDevice/src/supla/sensor/HC_SR04.cpp new file mode 100644 index 00000000..4639556c --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/HC_SR04.cpp @@ -0,0 +1,111 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include "HC_SR04.h" + +namespace Supla { +namespace Sensor { +HC_SR04::HC_SR04(int8_t trigPin, + int8_t echoPin, + int16_t minIn, + int16_t maxIn, + int16_t minOut, + int16_t maxOut) + : failCount(0), readouts{}, index(0) { + _trigPin = trigPin; + _echoPin = echoPin; + _minIn = minIn; + _maxIn = maxIn; + _minOut = minOut; + _maxOut = maxOut; +} + +void HC_SR04::onInit() { + pinMode(_trigPin, OUTPUT); + pinMode(_echoPin, INPUT); + digitalWrite(_trigPin, LOW); + delayMicroseconds(2); + + channel.setNewValue(getValue()); + channel.setNewValue(getValue()); +} + +double HC_SR04::getValue() { + noInterrupts(); + digitalWrite(_trigPin, HIGH); + delayMicroseconds(10); + digitalWrite(_trigPin, LOW); + unsigned long duration = pulseIn(_echoPin, HIGH, 60000); + interrupts(); + if (duration > 50) { + index++; + if (index > 4) index = 0; + readouts[index] = duration; + failCount = 0; + } else { + failCount++; + } + + unsigned long min = 0, max = 0, sum = 0; + int count = 0; + for (int i = 0; i < 5; i++) { + if (readouts[i] > 0) { + count++; + if (min > readouts[i] || min == 0) min = readouts[i]; + if (max < readouts[i]) max = readouts[i]; + sum += readouts[i]; + } + } + + if (count == 5) { + if (min > 0) { + sum -= min; + count--; + } + if (max > 0) { + sum -= max; + count--; + } + } + if (count > 0) { + duration = sum / count; + } + + long distance = (duration / 2.0) / 29.1; + long value = map(distance, _minIn, _maxIn, _minOut, _maxOut); + if (_minOut < _maxOut) { + value = constrain(value, _minOut, _maxOut); + } else { + value = constrain(value, _maxOut, _minOut); + } + return failCount <= 3 ? static_cast(value) / 100.0 + : DISTANCE_NOT_AVAILABLE; +} + +void HC_SR04::setMinMaxIn(int16_t minIn, int16_t maxIn) { + _minIn = minIn; + _maxIn = maxIn; +} + +void HC_SR04::setMinMaxOut(int16_t minOut, int16_t maxOut) { + _minOut = minOut; + _maxOut = maxOut; +} + +}; // namespace Sensor +}; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/sensor/HC_SR04.h b/lib/SuplaDevice/src/supla/sensor/HC_SR04.h index 6750ba73..2f222ee8 100644 --- a/lib/SuplaDevice/src/supla/sensor/HC_SR04.h +++ b/lib/SuplaDevice/src/supla/sensor/HC_SR04.h @@ -26,42 +26,27 @@ namespace Supla { namespace Sensor { class HC_SR04 : public Distance { public: - HC_SR04(int8_t trigPin, int8_t echoPin) : failCount(0), lastDuration(0) { - _trigPin = trigPin; - _echoPin = echoPin; - } - void onInit() { - pinMode(_trigPin, OUTPUT); - pinMode(_echoPin, INPUT); - digitalWrite(_trigPin, LOW); - delayMicroseconds(2); - - channel.setNewValue(getValue()); - channel.setNewValue(getValue()); - } - - virtual double getValue() { - digitalWrite(_trigPin, HIGH); - delayMicroseconds(10); - digitalWrite(_trigPin, LOW); - unsigned long duration = pulseIn(_echoPin, HIGH, 60000); - if (duration > 50) { - lastDuration = duration; - failCount = 0; - } else { - duration = lastDuration; - failCount++; - } - - return failCount <= 3 ? duration * 0.034 / 2 / 100 : DISTANCE_NOT_AVAILABLE; - } + HC_SR04(int8_t trigPin, + int8_t echoPin, + int16_t minIn = 0, + int16_t maxIn = 500, + int16_t minOut = 0, + int16_t maxOut = 500); + void onInit(); + virtual double getValue(); + void setMinMaxIn(int16_t minIn, int16_t maxIn); + void setMinMaxOut(int16_t minOut, int16_t maxOut); protected: int8_t _trigPin; int8_t _echoPin; + int16_t _minIn; + int16_t _maxIn; + int16_t _minOut; + int16_t _maxOut; char failCount; - bool ready; - unsigned long lastDuration; + unsigned long readouts[5]; + int index; }; }; // namespace Sensor diff --git a/lib/SuplaDevice/src/supla/sensor/HC_SR04_NewPing.h b/lib/SuplaDevice/src/supla/sensor/HC_SR04_NewPing.h new file mode 100644 index 00000000..8b664ffd --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/HC_SR04_NewPing.h @@ -0,0 +1,71 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _hc_sr04_newping_h +#define _hc_sr04_newping_h + +#include + +#include "supla/channel.h" +#include "supla/sensor/distance.h" + +namespace Supla { +namespace Sensor { +class HC_SR04_NewPing : public Distance { + public: + HC_SR04_NewPing(int8_t trigPin, int8_t echoPin, int16_t depth = 0) : retryCount(0) { + _depth = depth; + sonar = new NewPing(trigPin, echoPin); + } + void onInit() { + channel.setNewValue(getValue()); + } + + virtual double getValue() { + unsigned long uS = sonar->ping_median(); + + if (uS < 50) { + retryCount++; + if (retryCount > 3) { + retryCount = 0; + value = DISTANCE_NOT_AVAILABLE; + } + } else { + retryCount = 0; + unsigned long distance = sonar->convert_cm(uS); + + if (_depth > 0) { + value = map(distance, 0, _depth, _depth, 0); + if (value > _depth) value = 0; + } else { + value = distance; + } + value = value / 100.0; + } + return value; + } + + protected: + NewPing *sonar = nullptr; + double value; + int16_t _depth; + int8_t retryCount; +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/HJ101.cpp b/lib/SuplaDevice/src/supla/sensor/HJ101.cpp new file mode 100644 index 00000000..7430e4c3 --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/HJ101.cpp @@ -0,0 +1,194 @@ +#include "HJ101.h" + +namespace Supla { +namespace Sensor { + +HJ101::HJ101(int8_t pinCF, + int8_t pinCF1, + int8_t pinSEL, + bool currentWhen, + bool use_interrupts) + : pinCF(pinCF), + pinCF1(pinCF1), + pinSEL(pinSEL), + currentWhen(currentWhen), + use_interrupts(use_interrupts) { + hj101 = new HLW8012(); + hj101->begin(pinCF, pinCF1, pinSEL, currentWhen, use_interrupts); + + attachInterrupt(pinCF, hjl01_cf_interrupt, FALLING); + attachInterrupt(pinCF1, hjl01_cf1_interrupt, FALLING); +} + +void HJ101::onInit() { + readValuesFromDevice(); + updateChannelValues(); +} + +void HJ101::readValuesFromDevice() { + for (auto element = Supla::Element::begin(); element != nullptr; + element = element->next()) { + if (element->getChannel()) { + auto channel = element->getChannel(); + if (channel->getChannelType() == SUPLA_CHANNELTYPE_RELAY) { + if (channel->getValueBool()) { + energy = _energy + (hj101->getEnergy() / + 36); // current energy value = value at start + } + } + } + } + + unsigned int _reactive = 0; + double _pf = 0; + double _current = hj101->getCurrent(); + unsigned int _voltage = hj101->getVoltage(); + unsigned int _active = hj101->getActivePower(); + unsigned int _apparent = _voltage * _current; + if (_apparent > _active) { + _reactive = sqrt(_apparent * _apparent - _active * _active); + } else { + _reactive = 0; + } + if (_active > _apparent) { + _pf = 1; + } + if (_apparent == 0) { + _pf = 0; + } else { + _pf = (double)_active / _apparent; + } + setVoltage(0, _voltage * 100); // voltage in 0.01 V + setCurrent(0, _current * 1000); // current in 0.001 A + setPowerActive(0, _active * 100000); // power in 0.00001 kW + setFwdActEnergy(0, energy); // energy in 0.00001 kWh + setPowerApparent(0, _apparent * 100000); // power in 0.00001 kVA + setPowerReactive(0, _reactive * 100000); // power in 0.00001 kvar + setPowerFactor(0, _pf * 1000); // power in 0.001 +} + +void HJ101::onSaveState() { + double current_multiplier = getCurrentMultiplier(); + double voltage_multiplier = getVoltageMultiplier(); + double power_multiplier = getPowerMultiplier(); + + Supla::Storage::WriteState((unsigned char *)&energy, sizeof(energy)); + Supla::Storage::WriteState((unsigned char *)¤t_multiplier, + sizeof(current_multiplier)); + Supla::Storage::WriteState((unsigned char *)&voltage_multiplier, + sizeof(voltage_multiplier)); + Supla::Storage::WriteState((unsigned char *)&power_multiplier, + sizeof(power_multiplier)); +} + +void HJ101::onLoadState() { + double current_multiplier; + double voltage_multiplier; + double power_multiplier; + + if (Supla::Storage::ReadState((unsigned char *)&energy, sizeof(energy))) { + setCounter(energy); + } + + if (Supla::Storage::ReadState((unsigned char *)¤t_multiplier, + sizeof(current_multiplier))) { + setCurrentMultiplier(current_multiplier); + } else { + setCurrentMultiplier(18388); + } + + if (Supla::Storage::ReadState((unsigned char *)&voltage_multiplier, + sizeof(voltage_multiplier))) { + setVoltageMultiplier(voltage_multiplier); + } else { + setVoltageMultiplier(247704); + } + + if (Supla::Storage::ReadState((unsigned char *)&power_multiplier, + sizeof(power_multiplier))) { + setPowerMultiplier(power_multiplier); + } else { + setPowerMultiplier(2586583); + } +} + +double HJ101::getCurrentMultiplier() { + return hj101->getCurrentMultiplier(); +}; + +double HJ101::getVoltageMultiplier() { + return hj101->getVoltageMultiplier(); +}; + +double HJ101::getPowerMultiplier() { + return hj101->getPowerMultiplier(); +}; + +_supla_int64_t HJ101::getCounter() { + return energy; +} + +void HJ101::setCurrentMultiplier(double current_multiplier) { + hj101->setCurrentMultiplier(current_multiplier); +}; +void HJ101::setVoltageMultiplier(double voltage_multiplier) { + hj101->setVoltageMultiplier(voltage_multiplier); +}; +void HJ101::setPowerMultiplier(double power_multiplier) { + hj101->setPowerMultiplier(power_multiplier); +}; + +void HJ101::setCounter(_supla_int64_t energy) { + _energy = energy; // ------- energy value read from memory at startup + setFwdActEnergy(0, energy); +} + +// When using interrupts we have to call the library entry point +// whenever an interrupt is triggered +void ICACHE_RAM_ATTR HJ101::hjl01_cf1_interrupt() { + hj101->cf1_interrupt(); +} + +void ICACHE_RAM_ATTR HJ101::hjl01_cf_interrupt() { + hj101->cf_interrupt(); +} + +void HJ101::calibrate(double calibPower, double calibVoltage) { + unsigned long timeout1 = millis(); + while ((millis() - timeout1) < 10000) { + delay(10); + } + + Serial.print(F("[HLW] Active Power (W) : ")); + Serial.println(hj101->getActivePower()); + Serial.print(F("[HLW] Voltage (V) : ")); + Serial.println(hj101->getVoltage()); + Serial.print(F("[HLW] Current (A) : ")); + Serial.println(hj101->getCurrent()); + + hj101->expectedActivePower(calibPower); + hj101->expectedVoltage(calibVoltage); + hj101->expectedCurrent(calibPower / calibVoltage); + + unsigned long timeout2 = millis(); + while ((millis() - timeout2) < 2000) { + delay(10); + } + + double current_multi = getCurrentMultiplier(); + double voltage_multi = getVoltageMultiplier(); + double power_multi = getPowerMultiplier(); + + Serial.print(F("[HLW] New current multiplier : ")); + Serial.println(current_multi); + Serial.print(F("[HLW] New voltage multiplier : ")); + Serial.println(voltage_multi); + Serial.print(F("[HLW] New power multiplier : ")); + Serial.println(power_multi); + Supla::Storage::ScheduleSave(2000); + yield(); +} + +HLW8012 *HJ101::hj101 = nullptr; +}; // namespace Sensor +}; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/sensor/HJ101.h b/lib/SuplaDevice/src/supla/sensor/HJ101.h new file mode 100644 index 00000000..ad392868 --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/HJ101.h @@ -0,0 +1,59 @@ +#ifndef _Hj101_h +#define _Hj101_h + +#include +#include + +// https://github.com/xoseperez/hlw8012 +#include +#include +#include + +#include "one_phase_electricity_meter.h" + +namespace Supla { +namespace Sensor { + +class HJ101 : public OnePhaseElectricityMeter, public Element { + public: + HJ101(int8_t pinCF, + int8_t pinCF1, + int8_t pinSEL, + bool currentWhen = LOW, + bool use_interrupts = true); + + void onInit(); + void readValuesFromDevice(); + void onSaveState(); + void onLoadState(); + + double getCurrentMultiplier(); + double getVoltageMultiplier(); + double getPowerMultiplier(); + _supla_int64_t getCounter(); + + void setCurrentMultiplier(double current_multiplier); + void setVoltageMultiplier(double voltage_multiplier); + void setPowerMultiplier(double power_multiplier); + void setCounter(_supla_int64_t energy); + + static void ICACHE_RAM_ATTR hjl01_cf1_interrupt(); + static void ICACHE_RAM_ATTR hjl01_cf_interrupt(); + void calibrate(double calibPower, double calibVoltage); + + protected: + static HLW8012 *hj101; + int8_t pinCF; + int8_t pinCF1; + int8_t pinSEL; + bool currentWhen; + bool use_interrupts; + + unsigned _supla_int64_t energy; + unsigned _supla_int64_t _energy; // energy value read from memory at startup +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif \ No newline at end of file diff --git a/lib/SuplaDevice/src/supla/sensor/MAX6675_K.cpp b/lib/SuplaDevice/src/supla/sensor/MAX6675_K.cpp new file mode 100644 index 00000000..4f610197 --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/MAX6675_K.cpp @@ -0,0 +1,74 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "MAX6675_K.h" + +namespace Supla { +namespace Sensor { +MAX6675_K::MAX6675_K(uint8_t pin_CLK, uint8_t pin_CS, uint8_t pin_DO) + : pin_CLK(pin_CLK), pin_CS(pin_CS), pin_DO(pin_DO) { +} + +double MAX6675_K::getValue() { + uint16_t value; + + digitalWrite(pin_CS, LOW); + delay(1); + + value = spiRead(); + value <<= 8; + value |= spiRead(); + + digitalWrite(pin_CS, HIGH); + + if (value & 0x4) { // this means there is no probe connected to Max6675 + Serial.print(F("no probe connected to Max6675")); + return TEMPERATURE_NOT_AVAILABLE; + } + value >>= 3; + + return value * 0.25; +} + +void MAX6675_K::onInit() { + digitalWrite(pin_CS, HIGH); + + pinMode(pin_CS, OUTPUT); + pinMode(pin_CLK, OUTPUT); + pinMode(pin_DO, INPUT); + + channel.setNewValue(getValue()); +} + +byte MAX6675_K::spiRead() { + int i; + byte d = 0; + + for (i = 7; i >= 0; i--) { + digitalWrite(pin_CLK, LOW); + delay(1); + if (digitalRead(pin_DO)) { + d |= (1 << i); + } + + digitalWrite(pin_CLK, HIGH); + delay(1); + } + return d; +} + +}; // namespace Sensor +}; // namespace Supla \ No newline at end of file diff --git a/lib/SuplaDevice/src/supla/sensor/MAX6675_K.h b/lib/SuplaDevice/src/supla/sensor/MAX6675_K.h index e0f52438..868a9bff 100644 --- a/lib/SuplaDevice/src/supla/sensor/MAX6675_K.h +++ b/lib/SuplaDevice/src/supla/sensor/MAX6675_K.h @@ -1,80 +1,42 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + #ifndef _max6675_k_h #define _max6675_k_h -#if defined(ESP8266) -#include -#endif -#ifdef avr -#include -#endif - -#include -#include "supla/channel.h" -#include "supla/sensor/thermometer.h" +#include +#include namespace Supla { namespace Sensor { class MAX6675_K : public Thermometer { public: - MAX6675_K(uint8_t pin_CLK, uint8_t pin_CS, uint8_t pin_DO) { - _pin_CLK = pin_CLK; - _pin_CS = pin_CS; - _pin_DO = pin_DO; - } - - double getValue() { - uint16_t value; - - digitalWrite(_pin_CS, LOW); - delay(1); - - value = spi_read(); - value <<= 8; - value |= spi_read(); - - digitalWrite(_pin_CS, HIGH); - - if (value & 0x4) { - return -275; - } - value >>= 3; - - return value * 0.25; - } + MAX6675_K(uint8_t pin_CLK, uint8_t pin_CS, uint8_t pin_DO); + double getValue(); - void onInit() { - pinMode(_pin_CS, OUTPUT); - pinMode(_pin_CLK, OUTPUT); - pinMode(_pin_DO, INPUT); - - digitalWrite(_pin_CS, HIGH); - - channel.setNewValue(getValue()); - } - - byte spi_read(void) { - int i; - byte d = 0; - - for (i = 7; i >= 0; i--) { - digitalWrite(_pin_CLK, LOW); - delay(1); - if (digitalRead(_pin_DO)) { - d |= (1 << i); - } - - digitalWrite(_pin_CLK, HIGH); - delay(1); - } - - return d; - } + private: + void onInit(); + byte spiRead(); protected: - int8_t _pin_CLK; - int8_t _pin_CS; - int8_t _pin_DO; + int8_t pin_CLK; + int8_t pin_CS; + int8_t pin_DO; }; + }; // namespace Sensor }; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/sensor/MAXThermocouple.cpp b/lib/SuplaDevice/src/supla/sensor/MAXThermocouple.cpp new file mode 100644 index 00000000..34d8eed8 --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/MAXThermocouple.cpp @@ -0,0 +1,90 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "MAXThermocouple.h" + +namespace Supla { +namespace Sensor { +MAXThermocouple::MAXThermocouple(uint8_t pin_CLK, uint8_t pin_CS, uint8_t pin_DO) + : pin_CLK(pin_CLK), pin_CS(pin_CS), pin_DO(pin_DO) { +} + +double MAXThermocouple::getValue() { + int32_t value; + + digitalWrite(pin_CS, LOW); + delay(1); + + value = spiRead(); + + digitalWrite(pin_CS, HIGH); + + if ((value >> 16) == (value & 0xffff)) { // MAX6675 + value >>= 16; + + if ((value & 0x4) || (value <= 0)) { // this means there is no probe connected to Max6675 + Serial.println(F("Max6675 Error")); + return TEMPERATURE_NOT_AVAILABLE; + } + value >>= 3; + + return (double)value * 0.25; + + } else { // MAX31855 + + if (value & 0x7) { + Serial.println(F("Max31855 Error")); + return TEMPERATURE_NOT_AVAILABLE; + } else { + uint16_t _internTemp = (value >> 4) & 0xfff; + + value >>= 18; + if (value & 0x2000) { // is - + value |= 0xffffc000; + } + + return (double)value * 0.25; + } + } +} + +void MAXThermocouple::onInit() { + digitalWrite(pin_CS, HIGH); + + pinMode(pin_CS, OUTPUT); + pinMode(pin_CLK, OUTPUT); + pinMode(pin_DO, INPUT); + + channel.setNewValue(getValue()); +} + +uint32_t MAXThermocouple::spiRead() { + uint32_t d = 0; + + for (int i = 31; i >= 0; i--) { + digitalWrite(pin_CLK, LOW); + delay(1); + d <<= 1; + d |= digitalRead(pin_DO); + + digitalWrite(pin_CLK, HIGH); + delay(1); + } + return d; +} + +}; // namespace Sensor +}; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/sensor/MAXThermocouple.h b/lib/SuplaDevice/src/supla/sensor/MAXThermocouple.h new file mode 100644 index 00000000..1f20260e --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/MAXThermocouple.h @@ -0,0 +1,43 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef _maxThermocouple_k_h +#define _maxThermocouple_k_h + +#include +#include + +namespace Supla { +namespace Sensor { +class MAXThermocouple : public Thermometer { + public: + MAXThermocouple(uint8_t pin_CLK, uint8_t pin_CS, uint8_t pin_DO); + double getValue(); + + private: + void onInit(); + uint32_t spiRead(void); + + protected: + int8_t pin_CLK; + int8_t pin_CS; + int8_t pin_DO; +}; + +}; // namespace Sensor +}; // namespace Supla + +#endif diff --git a/lib/SuplaDevice/src/supla/sensor/SHT3x.h b/lib/SuplaDevice/src/supla/sensor/SHT3x.h index 9f25c919..3406ac14 100644 --- a/lib/SuplaDevice/src/supla/sensor/SHT3x.h +++ b/lib/SuplaDevice/src/supla/sensor/SHT3x.h @@ -17,58 +17,73 @@ #ifndef _sht3x_h #define _sht3x_h -// Dependency: Risele SHT3x library - use library manager to install it +// Dependency: ClosedCube SHT3x library - use library manager to install it // https://github.com/closedcube/ClosedCube_SHT31D_Arduino -#include "ClosedCube_SHT31D.h" +#include #include "therm_hygro_meter.h" + namespace Supla { namespace Sensor { class SHT3x : public ThermHygroMeter { public: - SHT3x(int8_t address = 0x44) : address(address) { + SHT3x(int8_t address = 0x44) + : temperature(TEMPERATURE_NOT_AVAILABLE), + humidity(HUMIDITY_NOT_AVAILABLE), + address(address), + retryCount(0) { } double getTemp() { - float value = TEMPERATURE_NOT_AVAILABLE; + return temperature; + } - SHT31D result = sht.readTempAndHumidity( - SHT3XD_REPEATABILITY_LOW, SHT3XD_MODE_CLOCK_STRETCH, 50); + double getHumi() { + return humidity; + } - if (result.error == SHT3XD_NO_ERROR) { - value = result.t; - } else { - Serial.print("SHT [ERROR] Code #"); - Serial.println(result.error); + private: + void iterateAlways() { + if (millis() - lastReadTime > 10000) { + lastReadTime = millis(); + readValuesFromDevice(); + channel.setNewValue(getTemp(), getHumi()); } - return value; } - double getHumi() { - float value = HUMIDITY_NOT_AVAILABLE; + void onInit() { + sht.begin(address); + readValuesFromDevice(); + channel.setNewValue(getTemp(), getHumi()); + } + void readValuesFromDevice() { SHT31D result = sht.readTempAndHumidity( SHT3XD_REPEATABILITY_LOW, SHT3XD_MODE_CLOCK_STRETCH, 50); - if (result.error == SHT3XD_NO_ERROR) { - value = result.rh; - } else { - Serial.print("SHT [ERROR] Code #"); + if (result.error != SHT3XD_NO_ERROR) { + Serial.print(F("SHT [ERROR] Code #")); Serial.println(result.error); + retryCount++; + if (retryCount > 3) { + retryCount = 0; + temperature = TEMPERATURE_NOT_AVAILABLE; + humidity = HUMIDITY_NOT_AVAILABLE; + } + } else { + retryCount = 0; + temperature = result.t; + humidity = result.rh; } - return value; - } - - void onInit() { - sht.begin(address); - - channel.setNewValue(getTemp(), getHumi()); } protected: int8_t address; + double temperature; + double humidity; + int8_t retryCount; ::ClosedCube_SHT31D sht; // I2C }; diff --git a/lib/SuplaDevice/src/supla/sensor/Si7021.h b/lib/SuplaDevice/src/supla/sensor/Si7021.h index 0b6b70f6..055e3f6f 100644 --- a/lib/SuplaDevice/src/supla/sensor/Si7021.h +++ b/lib/SuplaDevice/src/supla/sensor/Si7021.h @@ -20,7 +20,7 @@ // Dependency: Adafruid Si7021 library - use library manager to install it // https://github.com/adafruit/Adafruit_Si7021 -#include "Adafruit_Si7021.h" +#include #include "therm_hygro_meter.h" namespace Supla { @@ -53,26 +53,25 @@ class Si7021 : public ThermHygroMeter { } void onInit() { - sensor = Adafruit_Si7021(); sensor.begin(); - Serial.print("Found model "); + Serial.print(F("Found model ")); switch (sensor.getModel()) { case SI_Engineering_Samples: - Serial.print("SI engineering samples"); + Serial.print(F("SI engineering samples")); break; case SI_7013: - Serial.print("Si7013"); + Serial.print(F("Si7013")); break; case SI_7020: - Serial.print("Si7020"); + Serial.print(F("Si7020")); break; case SI_7021: - Serial.print("Si7021"); + Serial.print(F("Si7021")); break; case SI_UNKNOWN: default: - Serial.print("Unknown"); + Serial.print(F("Unknown")); } channel.setNewValue(getTemp(), getHumi()); diff --git a/lib/SuplaDevice/src/supla/sensor/Si7021_sonoff.cpp b/lib/SuplaDevice/src/supla/sensor/Si7021_sonoff.cpp new file mode 100644 index 00000000..c3391e25 --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/Si7021_sonoff.cpp @@ -0,0 +1,118 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "Si7021_sonoff.h" + +namespace Supla { +namespace Sensor { +Si7021Sonoff::Si7021Sonoff(int pin) + : temperature(TEMPERATURE_NOT_AVAILABLE), + humidity(HUMIDITY_NOT_AVAILABLE), + pin(pin), + retryCount(0) { +} + +double Si7021Sonoff::getTemp() { + return temperature; +} + +double Si7021Sonoff::getHumi() { + return humidity; +} + +void Si7021Sonoff::iterateAlways() { + if (millis() - lastReadTime > 10000) { + lastReadTime = millis(); + read(); + channel.setNewValue(getTemp(), getHumi()); + } +} + +void Si7021Sonoff::onInit() { + pinMode(pin, INPUT); + + delay(100); + read(); + channel.setNewValue(getTemp(), getHumi()); +} + +double Si7021Sonoff::readTemp(uint8_t* data) { + double temp = (((data[2] & 0x7F) << 8) | data[3]) * 0.1; + if (data[2] & 0x80) { + temp *= -1; + } + return temp; +} + +double Si7021Sonoff::readHumi(uint8_t* data) { + double humi = ((data[0] << 8) | data[1]) * 0.1; + return humi; +} + +bool Si7021Sonoff::read() { + uint8_t data[5] = {0}; + + yield(); + + pinMode(pin, OUTPUT); + digitalWrite(pin, LOW); + delayMicroseconds(500); + digitalWrite(pin, HIGH); + delayMicroseconds(20); + pinMode(pin, INPUT); + + uint32_t i = 0; + if (waitState(0) && waitState(1) && waitState(0)) { + for (i = 0; i < 40; i++) { + if (!waitState(1)) { + break; + } + delayMicroseconds(35); + if (digitalRead(pin) == HIGH) { + data[i / 8] |= (1 << (7 - i % 8)); + } + if (!waitState(0)) { + break; + } + } + } + + uint8_t checksum = (data[0] + data[1] + data[2] + data[3]) & 0xFF; + if (i < 40 || data[4] != checksum) { + retryCount++; + if (retryCount > 3) { + retryCount = 0; + temperature = TEMPERATURE_NOT_AVAILABLE; + humidity = HUMIDITY_NOT_AVAILABLE; + } + } else { + retryCount = 0; + temperature = readTemp(data); + humidity = readHumi(data); + } +} + +bool Si7021Sonoff::waitState(bool state) { + unsigned long timeout = micros(); + while (micros() - timeout < 100) { + if (digitalRead(pin) == state) return true; + delayMicroseconds(1); + } + return false; +} + +}; // namespace Sensor +}; // namespace Supla \ No newline at end of file diff --git a/lib/SuplaDevice/src/supla/sensor/Si7021_sonoff.h b/lib/SuplaDevice/src/supla/sensor/Si7021_sonoff.h index b07ec66d..bac9667e 100644 --- a/lib/SuplaDevice/src/supla/sensor/Si7021_sonoff.h +++ b/lib/SuplaDevice/src/supla/sensor/Si7021_sonoff.h @@ -1,154 +1,50 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + #ifndef _si7021_sonoff_h #define _si7021_sonoff_h +#include #include + namespace Supla { namespace Sensor { class Si7021Sonoff : public ThermHygroMeter { public: - Si7021Sonoff(int pin) { - _pin = pin; - pinMode(_pin, INPUT); - delay(100); - retryCountTemp = 0; - retryCountHumi = 0; - lastValidTemp = TEMPERATURE_NOT_AVAILABLE; - lastValidHumi = HUMIDITY_NOT_AVAILABLE; - } - - double getTemp() { - double value = TEMPERATURE_NOT_AVAILABLE; - ReadTemp(); - value = _temp; - if (isnan(value)) { - value = TEMPERATURE_NOT_AVAILABLE; - } - - if (value == TEMPERATURE_NOT_AVAILABLE) { - retryCountTemp++; - if (retryCountTemp > 3) { - retryCountTemp = 0; - } - else { - value = lastValidTemp; - } - } - else { - retryCountTemp = 0; - } - lastValidTemp = value; - - return value; - } - - double getHumi() { - double value = HUMIDITY_NOT_AVAILABLE; - ReadTemp(); - value = _humidity; - if (isnan(value)) { - value = HUMIDITY_NOT_AVAILABLE; - } - - if (value == HUMIDITY_NOT_AVAILABLE) { - retryCountHumi++; - if (retryCountHumi > 3) { - retryCountHumi = 0; - } - else { - value = lastValidHumi; - } - } - else { - retryCountHumi = 0; - } - lastValidHumi = value; - - return value; - } - - void iterateAlways() { - if (lastReadTime + 10000 < millis()) { - lastReadTime = millis(); - channel.setNewValue(getTemp(), getHumi()); - } - } - - void onInit() { - channel.setNewValue(getTemp(), getHumi()); - } + Si7021Sonoff(int pin); + double getTemp(); + double getHumi(); private: - bool ReadTemp() { - _temp = NAN; - _humidity = NAN; - - uint8_t d[5]; - d[0] = d[1] = d[2] = d[3] = d[4] = 0; - - pinMode(_pin, OUTPUT); - digitalWrite(_pin, LOW); - delayMicroseconds(500); - digitalWrite(_pin, HIGH); - delayMicroseconds(20); - pinMode(_pin, INPUT); - - uint32_t i = 0; - if (WaitState(0) and WaitState(1) and WaitState(0)) { - for (i = 0; i < 40; i++) { - if (!WaitState(1)) { - break; - } - delayMicroseconds(35); - if (digitalRead(_pin) == HIGH) { - d[i / 8] |= (1 << (7 - i % 8)); - } - if (!WaitState(0)) { - break; - } - } - } - - if (i < 40) { - return false; - } - - uint8_t checksum = (d[0] + d[1] + d[2] + d[3]) & 0xFF; - if (d[4] == checksum) { - _temp = (((d[2] & 0x7F) << 8) | d[3]) * 0.1; - _humidity = ((d[0] << 8) | d[1]) * 0.1; - if (d[2] & 0x80) { - _temp *= -1; - } - } - - if (isnan(_temp) || isnan(_humidity)) { - Serial.println(F("Invalid NAN reading")); - return false; - } - return true; - } - - bool WaitState(bool state) { - unsigned long timeout = micros(); - while (micros() - timeout < 100) { - if (digitalRead(_pin) == state) - return true; - delayMicroseconds(1); - } - return false; - } + void iterateAlways(); + void onInit(); + double readTemp(uint8_t* data); + double readHumi(uint8_t* data); + bool read(); + bool waitState(bool state); protected: - int8_t _pin; - double _temp = NAN, _humidity = NAN; - double lastValidTemp; - double lastValidHumi; - int8_t retryCountTemp; - int8_t retryCountHumi; + int8_t pin; + double temperature; + double humidity; + int8_t retryCount; }; }; // namespace Sensor }; // namespace Supla -#endif +#endif \ No newline at end of file diff --git a/lib/SuplaDevice/src/supla/sensor/binary.cpp b/lib/SuplaDevice/src/supla/sensor/binary.cpp new file mode 100644 index 00000000..162b9d0c --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/binary.cpp @@ -0,0 +1,41 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "binary.h" +#include "../io.h" + +Supla::Sensor::Binary::Binary(int pin, bool pullUp = false) + : pin(pin), pullUp(pullUp), lastReadTime(0) { + channel.setType(SUPLA_CHANNELTYPE_SENSORNO); +} + +bool Supla::Sensor::Binary::getValue() { + return Supla::Io::digitalRead(channel.getChannelNumber(), pin) == LOW ? false + : true; +} + +void Supla::Sensor::Binary::iterateAlways() { + if (lastReadTime + 100 < millis()) { + lastReadTime = millis(); + channel.setNewValue(getValue()); + } +} + +void Supla::Sensor::Binary::onInit() { + Supla::Io::pinMode( + channel.getChannelNumber(), pin, pullUp ? INPUT_PULLUP : INPUT); + channel.setNewValue(getValue()); +} diff --git a/lib/SuplaDevice/src/supla/sensor/binary.h b/lib/SuplaDevice/src/supla/sensor/binary.h index 8bd7e52d..994ca7ae 100644 --- a/lib/SuplaDevice/src/supla/sensor/binary.h +++ b/lib/SuplaDevice/src/supla/sensor/binary.h @@ -19,41 +19,18 @@ #include -#include "../channel.h" -#include "../element.h" -#include "../io.h" +#include "../channel_element.h" namespace Supla { namespace Sensor { -class Binary : public Element { +class Binary : public ChannelElement { public: - Binary(int pin, bool pullUp = false) : pin(pin), pullUp(pullUp), lastReadTime(0) { - channel.setType(SUPLA_CHANNELTYPE_SENSORNO); - } - - bool getValue() { - return Supla::Io::digitalRead(channel.getChannelNumber(), pin) == LOW - ? false - : true; - } - - void iterateAlways() { - if (lastReadTime + 100 < millis()) { - lastReadTime = millis(); - channel.setNewValue(getValue()); - } - } - - void onInit() { - pinMode(pin, pullUp ? INPUT_PULLUP : INPUT); - channel.setNewValue(getValue()); - } + Binary(int pin, bool pullUp); + bool getValue(); + void iterateAlways(); + void onInit(); protected: - Channel *getChannel() { - return &channel; - } - Channel channel; int pin; bool pullUp; unsigned long lastReadTime; diff --git a/lib/SuplaDevice/src/supla/sensor/distance.h b/lib/SuplaDevice/src/supla/sensor/distance.h index 953078a1..ae13d184 100644 --- a/lib/SuplaDevice/src/supla/sensor/distance.h +++ b/lib/SuplaDevice/src/supla/sensor/distance.h @@ -17,14 +17,13 @@ #ifndef _distance_h #define _distance_h -#include "supla/channel.h" -#include "supla/element.h" +#include "supla/channel_element.h" -#define DISTANCE_NOT_AVAILABLE -1 +#define DISTANCE_NOT_AVAILABLE -1.0 namespace Supla { namespace Sensor { -class Distance : public Element { +class Distance : public ChannelElement { public: Distance() : lastReadTime(0) { channel.setType(SUPLA_CHANNELTYPE_DISTANCESENSOR); @@ -37,17 +36,13 @@ class Distance : public Element { } void iterateAlways() { - if (lastReadTime + 500 < millis()) { + if (lastReadTime + 100 < millis()) { lastReadTime = millis(); channel.setNewValue(getValue()); } } protected: - Channel *getChannel() { - return &channel; - } - Channel channel; unsigned long lastReadTime; }; diff --git a/lib/SuplaDevice/src/supla/sensor/electricity_meter.cpp b/lib/SuplaDevice/src/supla/sensor/electricity_meter.cpp new file mode 100644 index 00000000..13fbf36a --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/electricity_meter.cpp @@ -0,0 +1,260 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include "electricity_meter.h" + +Supla::Sensor::ElectricityMeter::ElectricityMeter() + : valueChanged(false), lastReadTime(0), refreshRateSec(5) { + extChannel.setType(SUPLA_CHANNELTYPE_ELECTRICITY_METER); + extChannel.setDefault(SUPLA_CHANNELFNC_ELECTRICITY_METER); + memset(&emValue, 0, sizeof(emValue)); + emValue.period = 5; + for (int i = 0; i < MAX_PHASES; i++) { + rawCurrent[i] = 0; + } + currentMeasurementAvailable = false; +} + +void Supla::Sensor::ElectricityMeter::updateChannelValues() { + if (!valueChanged) { + return; + } + valueChanged = false; + + emValue.m_count = 1; + + // Update current messurement precision based on last updates + if (currentMeasurementAvailable) { + bool over65A = false; + for (int i = 0; i < MAX_PHASES; i++) { + if (rawCurrent[i] > 65000) { + over65A = true; + } + } + + for (int i = 0; i < MAX_PHASES; i++) { + if (over65A) { + emValue.m[0].current[i] = rawCurrent[i] / 10; + } else { + emValue.m[0].current[i] = rawCurrent[i]; + } + } + + if (over65A) { + emValue.measured_values ^= (!EM_VAR_CURRENT); + emValue.measured_values |= EM_VAR_CURRENT_OVER_65A; + } else { + emValue.measured_values ^= (!EM_VAR_CURRENT_OVER_65A); + emValue.measured_values |= EM_VAR_CURRENT; + } + } + + // Prepare extended channel value + srpc_evtool_v2_emextended2extended(&emValue, extChannel.getExtValue()); + extChannel.setNewValue(emValue); +} + +// energy in 0.00001 kWh +void Supla::Sensor::ElectricityMeter::setFwdActEnergy( + int phase, unsigned _supla_int64_t energy) { + if (phase >= 0 && phase < MAX_PHASES) { + if (emValue.total_forward_active_energy[phase] != energy) { + valueChanged = true; + } + emValue.total_forward_active_energy[phase] = energy; + emValue.measured_values |= EM_VAR_FORWARD_ACTIVE_ENERGY; + } +} + +// energy in 0.00001 kWh +void Supla::Sensor::ElectricityMeter::setRvrActEnergy( + int phase, unsigned _supla_int64_t energy) { + if (phase >= 0 && phase < MAX_PHASES) { + if (emValue.total_reverse_active_energy[phase] != energy) { + valueChanged = true; + } + emValue.total_reverse_active_energy[phase] = energy; + emValue.measured_values |= EM_VAR_REVERSE_ACTIVE_ENERGY; + } +} + +// energy in 0.00001 kWh +void Supla::Sensor::ElectricityMeter::setFwdReactEnergy( + int phase, unsigned _supla_int64_t energy) { + if (phase >= 0 && phase < MAX_PHASES) { + if (emValue.total_forward_reactive_energy[phase] != energy) { + valueChanged = true; + } + emValue.total_forward_reactive_energy[phase] = energy; + emValue.measured_values |= EM_VAR_FORWARD_REACTIVE_ENERGY; + } +} + +// energy in 0.00001 kWh +void Supla::Sensor::ElectricityMeter::setRvrReactEnergy( + int phase, unsigned _supla_int64_t energy) { + if (phase >= 0 && phase < MAX_PHASES) { + if (emValue.total_reverse_reactive_energy[phase] != energy) { + valueChanged = true; + } + emValue.total_reverse_reactive_energy[phase] = energy; + emValue.measured_values |= EM_VAR_REVERSE_REACTIVE_ENERGY; + } +} + +// voltage in 0.01 V +void Supla::Sensor::ElectricityMeter::setVoltage( + int phase, unsigned _supla_int16_t voltage) { + if (phase >= 0 && phase < MAX_PHASES) { + if (emValue.m[0].voltage[phase] != voltage) { + valueChanged = true; + } + emValue.m[0].voltage[phase] = voltage; + emValue.measured_values |= EM_VAR_VOLTAGE; + } +} + +// current in 0.001 A +void Supla::Sensor::ElectricityMeter::setCurrent( + int phase, unsigned _supla_int_t current) { + if (phase >= 0 && phase < MAX_PHASES) { + if (rawCurrent[phase] != current) { + valueChanged = true; + } + rawCurrent[phase] = current; + currentMeasurementAvailable = true; + } +} + +// Frequency in 0.01 Hz +void Supla::Sensor::ElectricityMeter::setFreq(unsigned _supla_int16_t freq) { + if (emValue.m[0].freq != freq) { + valueChanged = true; + } + emValue.m[0].freq = freq; + emValue.measured_values |= EM_VAR_FREQ; +} + +// power in 0.00001 kW +void Supla::Sensor::ElectricityMeter::setPowerActive(int phase, + _supla_int_t power) { + if (phase >= 0 && phase < MAX_PHASES) { + if (emValue.m[0].power_active[phase] != power) { + valueChanged = true; + } + emValue.m[0].power_active[phase] = power; + emValue.measured_values |= EM_VAR_POWER_ACTIVE; + } +} + +// power in 0.00001 kvar +void Supla::Sensor::ElectricityMeter::setPowerReactive(int phase, + _supla_int_t power) { + if (phase >= 0 && phase < MAX_PHASES) { + if (emValue.m[0].power_reactive[phase] != power) { + valueChanged = true; + } + emValue.m[0].power_reactive[phase] = power; + emValue.measured_values |= EM_VAR_POWER_REACTIVE; + } +} + +// power in 0.00001 kVA +void Supla::Sensor::ElectricityMeter::setPowerApparent(int phase, + _supla_int_t power) { + if (phase >= 0 && phase < MAX_PHASES) { + if (emValue.m[0].power_apparent[phase] != power) { + valueChanged = true; + } + emValue.m[0].power_apparent[phase] = power; + emValue.measured_values |= EM_VAR_POWER_APPARENT; + } +} + +// power in 0.001 +void Supla::Sensor::ElectricityMeter::setPowerFactor(int phase, + _supla_int_t powerFactor) { + if (phase >= 0 && phase < MAX_PHASES) { + if (emValue.m[0].power_factor[phase] != powerFactor) { + valueChanged = true; + } + emValue.m[0].power_factor[phase] = powerFactor; + emValue.measured_values |= EM_VAR_POWER_FACTOR; + } +} + +// phase angle in 0.1 degree +void Supla::Sensor::ElectricityMeter::setPhaseAngle(int phase, + _supla_int_t phaseAngle) { + if (phase >= 0 && phase < MAX_PHASES) { + if (emValue.m[0].phase_angle[phase] != phaseAngle) { + valueChanged = true; + } + emValue.m[0].phase_angle[phase] = phaseAngle; + emValue.measured_values |= EM_VAR_PHASE_ANGLE; + } +} + +void Supla::Sensor::ElectricityMeter::resetReadParameters() { + if (emValue.measured_values != 0) { + emValue.measured_values = 0; + memset(&emValue.m[0], 0, sizeof(TElectricityMeter_Measurement)); + valueChanged = true; + } +} + +// Please implement this class for reading value from elecricity meter device. +// It will be called every 5 s. Use set methods defined above in order to +// set values on channel. Don't use any other method to modify channel values. +void Supla::Sensor::ElectricityMeter::readValuesFromDevice() { +} + +// Put here initialization code for electricity meter device. +// It will be called within SuplaDevce.begin method. +// It should also read first data set, so at the end it should call those two +// methods: +// readValuesFromDevice(); +// updateChannelValues(); +void Supla::Sensor::ElectricityMeter::onInit() { +} + +void Supla::Sensor::ElectricityMeter::iterateAlways() { + if (millis() - lastReadTime > refreshRateSec*1000) { + lastReadTime = millis(); + readValuesFromDevice(); + updateChannelValues(); + } +} + +// Implement this method to reset stored energy value (i.e. to set energy +// counter back to 0 kWh +void Supla::Sensor::ElectricityMeter::resetStorage() { +} + +Supla::Channel *Supla::Sensor::ElectricityMeter::getChannel() { + return &extChannel; +} + +void Supla::Sensor::ElectricityMeter::setResreshRate(unsigned int sec) { + refreshRateSec = sec; + if (refreshRateSec == 0) { + refreshRateSec = 1; + } +} + + diff --git a/lib/SuplaDevice/src/supla/sensor/electricity_meter.h b/lib/SuplaDevice/src/supla/sensor/electricity_meter.h index 3180e7e3..e67bd517 100644 --- a/lib/SuplaDevice/src/supla/sensor/electricity_meter.h +++ b/lib/SuplaDevice/src/supla/sensor/electricity_meter.h @@ -17,7 +17,6 @@ #ifndef _electricity_meter_h #define _electricity_meter_h -#include #include #include "../channel_extended.h" @@ -30,199 +29,52 @@ namespace Supla { namespace Sensor { class ElectricityMeter : public Element { public: - ElectricityMeter() : valueChanged(false), lastReadTime(0) { - extChannel.setType(SUPLA_CHANNELTYPE_ELECTRICITY_METER); - extChannel.setDefault(SUPLA_CHANNELFNC_ELECTRICITY_METER); - memset(&emValue, 0, sizeof(emValue)); - emValue.period = 5; - for (int i = 0; i < MAX_PHASES; i++) { - rawCurrent[i] = 0; - } - currentMeasurementAvailable = false; - } + ElectricityMeter(); - virtual void updateChannelValues() { - if (!valueChanged) { - return; - } - valueChanged = false; - - emValue.m_count = 1; - - // Update current messurement precision based on last updates - if (currentMeasurementAvailable) { - bool over65A = false; - for (int i = 0; i < MAX_PHASES; i++) { - if (rawCurrent[i] > 65000) { - over65A = true; - } - } - - for (int i = 0; i < MAX_PHASES; i++) { - if (over65A) { - emValue.m[0].current[i] = rawCurrent[i] / 10; - } else { - emValue.m[0].current[i] = rawCurrent[i]; - } - } - - if (over65A) { - emValue.measured_values ^= (!EM_VAR_CURRENT); - emValue.measured_values |= EM_VAR_CURRENT_OVER_65A; - } else { - emValue.measured_values ^= (!EM_VAR_CURRENT_OVER_65A); - emValue.measured_values |= EM_VAR_CURRENT; - } - } - - // Prepare extended channel value - srpc_evtool_v2_emextended2extended(&emValue, extChannel.getExtValue()); - extChannel.setNewValue(emValue); - } + virtual void updateChannelValues(); // energy in 0.00001 kWh - void setFwdActEnergy(int phase, unsigned _supla_int64_t energy) { - if (phase >= 0 && phase < MAX_PHASES) { - if (emValue.total_forward_active_energy[phase] != energy) { - valueChanged = true; - } - emValue.total_forward_active_energy[phase] = energy; - emValue.measured_values |= EM_VAR_FORWARD_ACTIVE_ENERGY; - } - } + void setFwdActEnergy(int phase, unsigned _supla_int64_t energy); // energy in 0.00001 kWh - void setRvrActEnergy(int phase, unsigned _supla_int64_t energy) { - if (phase >= 0 && phase < MAX_PHASES) { - if (emValue.total_reverse_active_energy[phase] != energy) { - valueChanged = true; - } - emValue.total_reverse_active_energy[phase] = energy; - emValue.measured_values |= EM_VAR_REVERSE_ACTIVE_ENERGY; - } - } + void setRvrActEnergy(int phase, unsigned _supla_int64_t energy); // energy in 0.00001 kWh - void setFwdReactEnergy(int phase, unsigned _supla_int64_t energy) { - if (phase >= 0 && phase < MAX_PHASES) { - if (emValue.total_forward_reactive_energy[phase] != energy) { - valueChanged = true; - } - emValue.total_forward_reactive_energy[phase] = energy; - emValue.measured_values |= EM_VAR_FORWARD_REACTIVE_ENERGY; - } - } + void setFwdReactEnergy(int phase, unsigned _supla_int64_t energy); // energy in 0.00001 kWh - void setRvrReactEnergy(int phase, unsigned _supla_int64_t energy) { - if (phase >= 0 && phase < MAX_PHASES) { - if (emValue.total_reverse_reactive_energy[phase] != energy) { - valueChanged = true; - } - emValue.total_reverse_reactive_energy[phase] = energy; - emValue.measured_values |= EM_VAR_REVERSE_REACTIVE_ENERGY; - } - } + void setRvrReactEnergy(int phase, unsigned _supla_int64_t energy); // voltage in 0.01 V - void setVoltage(int phase, unsigned _supla_int16_t voltage) { - if (phase >= 0 && phase < MAX_PHASES) { - if (emValue.m[0].voltage[phase] != voltage) { - valueChanged = true; - } - emValue.m[0].voltage[phase] = voltage; - emValue.measured_values |= EM_VAR_VOLTAGE; - } - } + void setVoltage(int phase, unsigned _supla_int16_t voltage); // current in 0.001 A - void setCurrent(int phase, unsigned _supla_int_t current) { - if (phase >= 0 && phase < MAX_PHASES) { - if (rawCurrent[phase] != current) { - valueChanged = true; - } - rawCurrent[phase] = current; - currentMeasurementAvailable = true; - } - } + void setCurrent(int phase, unsigned _supla_int_t current); // Frequency in 0.01 Hz - void setFreq(unsigned _supla_int16_t freq) { - if (emValue.m[0].freq != freq) { - valueChanged = true; - } - emValue.m[0].freq = freq; - emValue.measured_values |= EM_VAR_FREQ; - } + void setFreq(unsigned _supla_int16_t freq); // power in 0.00001 kW - void setPowerActive(int phase, _supla_int_t power) { - if (phase >= 0 && phase < MAX_PHASES) { - if (emValue.m[0].power_active[phase] != power) { - valueChanged = true; - } - emValue.m[0].power_active[phase] = power; - emValue.measured_values |= EM_VAR_POWER_ACTIVE; - } - } + void setPowerActive(int phase, _supla_int_t power); // power in 0.00001 kvar - void setPowerReactive(int phase, _supla_int_t power) { - if (phase >= 0 && phase < MAX_PHASES) { - if (emValue.m[0].power_reactive[phase] != power) { - valueChanged = true; - } - emValue.m[0].power_reactive[phase] = power; - emValue.measured_values |= EM_VAR_POWER_REACTIVE; - } - } + void setPowerReactive(int phase, _supla_int_t power); // power in 0.00001 kVA - void setPowerApparent(int phase, _supla_int_t power) { - if (phase >= 0 && phase < MAX_PHASES) { - if (emValue.m[0].power_apparent[phase] != power) { - valueChanged = true; - } - emValue.m[0].power_apparent[phase] = power; - emValue.measured_values |= EM_VAR_POWER_APPARENT; - } - } + void setPowerApparent(int phase, _supla_int_t power); // power in 0.001 - void setPowerFactor(int phase, _supla_int_t powerFactor) { - if (phase >= 0 && phase < MAX_PHASES) { - if (emValue.m[0].power_factor[phase] != powerFactor) { - valueChanged = true; - } - emValue.m[0].power_factor[phase] = powerFactor; - emValue.measured_values |= EM_VAR_POWER_FACTOR; - } - } + void setPowerFactor(int phase, _supla_int_t powerFactor); // phase angle in 0.1 degree - void setPhaseAngle(int phase, _supla_int_t phaseAngle) { - if (phase >= 0 && phase < MAX_PHASES) { - if (emValue.m[0].phase_angle[phase] != phaseAngle) { - valueChanged = true; - } - emValue.m[0].phase_angle[phase] = phaseAngle; - emValue.measured_values |= EM_VAR_PHASE_ANGLE; - } - } + void setPhaseAngle(int phase, _supla_int_t phaseAngle); - void resetReadParameters() { - if (emValue.measured_values != 0) { - emValue.measured_values = 0; - memset(&emValue.m[0], 0, sizeof(TElectricityMeter_Measurement)); - valueChanged = true; - } - } + void resetReadParameters(); // Please implement this class for reading value from elecricity meter device. // It will be called every 5 s. Use set methods defined above in order to // set values on channel. Don't use any other method to modify channel values. - virtual void readValuesFromDevice() { - } + virtual void readValuesFromDevice(); // Put here initialization code for electricity meter device. // It will be called within SuplaDevce.begin method. @@ -230,35 +82,30 @@ class ElectricityMeter : public Element { // methods: // readValuesFromDevice(); // updateChannelValues(); - void onInit() { - } + void onInit(); - void iterateAlways() { - if (lastReadTime + 5000 < millis()) { - lastReadTime = millis(); - readValuesFromDevice(); - updateChannelValues(); - } - } + void iterateAlways(); // Implement this method to reset stored energy value (i.e. to set energy // counter back to 0 kWh - virtual void resetStorage() { - } + virtual void resetStorage(); + + void setResreshRate(unsigned int sec); + + Channel *getChannel(); protected: - Channel *getChannel() { - return &extChannel; - } TElectricityMeter_ExtendedValue_V2 emValue; ChannelExtended extChannel; unsigned _supla_int_t rawCurrent[MAX_PHASES]; bool valueChanged; bool currentMeasurementAvailable; unsigned long lastReadTime; + unsigned int refreshRateSec; }; }; // namespace Sensor }; // namespace Supla #endif + diff --git a/lib/SuplaDevice/src/supla/sensor/general_purpose_measurement_base.h b/lib/SuplaDevice/src/supla/sensor/general_purpose_measurement_base.h index 6c494824..96afc2ec 100644 --- a/lib/SuplaDevice/src/supla/sensor/general_purpose_measurement_base.h +++ b/lib/SuplaDevice/src/supla/sensor/general_purpose_measurement_base.h @@ -17,12 +17,11 @@ #ifndef _general_purpose_measurement_h #define _general_purpose_measurement_h -#include "supla/element.h" -#include "supla/channel.h" +#include "supla/channel_element.h" namespace Supla { namespace Sensor { -class GeneralPurposeMeasurementBase : public Element { +class GeneralPurposeMeasurementBase : public ChannelElement { public: GeneralPurposeMeasurementBase() : lastReadTime(0) { channel.setType(SUPLA_CHANNELTYPE_GENERAL_PURPOSE_MEASUREMENT); @@ -38,10 +37,6 @@ class GeneralPurposeMeasurementBase : public Element { } protected: - Channel *getChannel() { - return &channel; - } - Channel channel; unsigned long lastReadTime; }; diff --git a/lib/SuplaDevice/src/supla/sensor/impulse_counter.cpp b/lib/SuplaDevice/src/supla/sensor/impulse_counter.cpp index d710eea3..9e641c4a 100644 --- a/lib/SuplaDevice/src/supla/sensor/impulse_counter.cpp +++ b/lib/SuplaDevice/src/supla/sensor/impulse_counter.cpp @@ -15,10 +15,10 @@ */ #include -#include #include #include #include +#include #include "impulse_counter.h" @@ -52,9 +52,9 @@ ImpulseCounter::ImpulseCounter(int _impulsePin, void ImpulseCounter::onInit() { if (inputPullup) { - pinMode(impulsePin, INPUT_PULLUP); + Supla::Io::pinMode(channel.getChannelNumber(), impulsePin, INPUT_PULLUP); } else { - pinMode(impulsePin, INPUT); + Supla::Io::pinMode(channel.getChannelNumber(), impulsePin, INPUT); } } @@ -88,7 +88,7 @@ void ImpulseCounter::incCounter() { } void ImpulseCounter::onFastTimer() { - int currentState = digitalRead(impulsePin); + int currentState = Supla::Io::digitalRead(channel.getChannelNumber(), impulsePin); if (prevState == (detectLowToHigh == true ? LOW : HIGH)) { if (millis() - lastImpulseMillis > debounceDelay) { if (currentState == (detectLowToHigh == true ? HIGH : LOW)) { @@ -100,11 +100,7 @@ void ImpulseCounter::onFastTimer() { prevState = currentState; } -Supla::Channel *ImpulseCounter::getChannel() { - return &channel; -} - -void ImpulseCounter::runAction(int event, int action) { +void ImpulseCounter::handleAction(int event, int action) { (void)(event); switch (action) { case RESET: { diff --git a/lib/SuplaDevice/src/supla/sensor/impulse_counter.h b/lib/SuplaDevice/src/supla/sensor/impulse_counter.h index 6d6da313..ef99708c 100644 --- a/lib/SuplaDevice/src/supla/sensor/impulse_counter.h +++ b/lib/SuplaDevice/src/supla/sensor/impulse_counter.h @@ -18,13 +18,12 @@ #define _supla_impulse_counter_h_ #include -#include -#include -#include +#include +#include namespace Supla { namespace Sensor { -class ImpulseCounter : public Element, public Triggerable { +class ImpulseCounter : public ChannelElement, public ActionHandler { public: ImpulseCounter(int _impulsePin, bool _detectLowToHigh = false, @@ -35,7 +34,7 @@ class ImpulseCounter : public Element, public Triggerable { void onLoadState(); void onSaveState(); void onFastTimer(); - void runAction(int event, int action); + void handleAction(int event, int action); // Returns value of a counter at given Supla channel _supla_int64_t getCounter(); @@ -60,10 +59,6 @@ class ImpulseCounter : public Element, public Triggerable { bool inputPullup; unsigned _supla_int64_t counter; // Actual count of impulses - - Channel *getChannel(); - Channel channel; - }; }; // namespace Sensor }; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/sensor/pressure.h b/lib/SuplaDevice/src/supla/sensor/pressure.h index d320fb33..b9c2653c 100644 --- a/lib/SuplaDevice/src/supla/sensor/pressure.h +++ b/lib/SuplaDevice/src/supla/sensor/pressure.h @@ -17,14 +17,13 @@ #ifndef _pressure_h #define _pressure_h -#include "supla/channel.h" -#include "supla/element.h" +#include "supla/channel_element.h" #define PRESSURE_NOT_AVAILABLE -1 namespace Supla { namespace Sensor { -class Pressure : public Element { +class Pressure : public ChannelElement { public: Pressure() : lastReadTime(0) { channel.setType(SUPLA_CHANNELTYPE_PRESSURESENSOR); @@ -43,12 +42,7 @@ class Pressure : public Element { } } - protected: - Channel *getChannel() { - return &channel; - } - Channel channel; unsigned long lastReadTime; }; diff --git a/lib/SuplaDevice/src/supla/sensor/rain.h b/lib/SuplaDevice/src/supla/sensor/rain.h index dd060d27..4fdbe177 100644 --- a/lib/SuplaDevice/src/supla/sensor/rain.h +++ b/lib/SuplaDevice/src/supla/sensor/rain.h @@ -17,14 +17,13 @@ #ifndef _rain_h #define _rain_h -#include "supla/channel.h" -#include "supla/element.h" +#include "supla/channel_element.h" #define RAIN_NOT_AVAILABLE -1 namespace Supla { namespace Sensor { -class Rain: public Element { +class Rain: public ChannelElement { public: Rain() : lastReadTime(0) { channel.setType(SUPLA_CHANNELTYPE_RAINSENSOR); @@ -43,12 +42,7 @@ class Rain: public Element { } } - protected: - Channel *getChannel() { - return &channel; - } - Channel channel; unsigned long lastReadTime; }; diff --git a/lib/SuplaDevice/src/supla/sensor/therm_hygro_meter.cpp b/lib/SuplaDevice/src/supla/sensor/therm_hygro_meter.cpp new file mode 100644 index 00000000..b9604cfd --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/therm_hygro_meter.cpp @@ -0,0 +1,37 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "therm_hygro_meter.h" + +Supla::Sensor::ThermHygroMeter::ThermHygroMeter() { + channel.setType(SUPLA_CHANNELTYPE_HUMIDITYANDTEMPSENSOR); + channel.setDefault(SUPLA_CHANNELFNC_HUMIDITYANDTEMPERATURE); +} + +double Supla::Sensor::ThermHygroMeter::getTemp() { + return TEMPERATURE_NOT_AVAILABLE; +} + +double Supla::Sensor::ThermHygroMeter::getHumi() { + return HUMIDITY_NOT_AVAILABLE; +} + +void Supla::Sensor::ThermHygroMeter::iterateAlways() { + if (millis() - lastReadTime > 10000) { + lastReadTime = millis(); + channel.setNewValue(getTemp(), getHumi()); + } +} diff --git a/lib/SuplaDevice/src/supla/sensor/therm_hygro_meter.h b/lib/SuplaDevice/src/supla/sensor/therm_hygro_meter.h index 3e9f019a..ab79fea1 100644 --- a/lib/SuplaDevice/src/supla/sensor/therm_hygro_meter.h +++ b/lib/SuplaDevice/src/supla/sensor/therm_hygro_meter.h @@ -25,27 +25,11 @@ namespace Supla { namespace Sensor { class ThermHygroMeter : public Thermometer { public: - ThermHygroMeter() { - channel.setType(SUPLA_CHANNELTYPE_HUMIDITYANDTEMPSENSOR); - channel.setDefault(SUPLA_CHANNELFNC_HUMIDITYANDTEMPERATURE); - } - - virtual double getTemp() { - return TEMPERATURE_NOT_AVAILABLE; - } - - virtual double getHumi() { - return HUMIDITY_NOT_AVAILABLE; - } - - void iterateAlways() { - if (millis() - lastReadTime > 10000) { - lastReadTime = millis(); - channel.setNewValue(getTemp(), getHumi()); - } - } - - protected: + ThermHygroMeter(); + virtual double getTemp(); + virtual double getHumi(); + void iterateAlways(); + }; }; // namespace Sensor diff --git a/lib/SuplaDevice/src/supla/sensor/therm_hygro_press_meter.cpp b/lib/SuplaDevice/src/supla/sensor/therm_hygro_press_meter.cpp new file mode 100644 index 00000000..4e93b638 --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/therm_hygro_press_meter.cpp @@ -0,0 +1,79 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include "therm_hygro_press_meter.h" + +Supla::Sensor::ThermHygroPressMeter::ThermHygroPressMeter() { + pressureChannel.setType(SUPLA_CHANNELTYPE_PRESSURESENSOR); + pressureChannel.setDefault(SUPLA_CHANNELFNC_PRESSURESENSOR); +} + +double Supla::Sensor::ThermHygroPressMeter::getPressure() { + return PRESSURE_NOT_AVAILABLE; +} + +void Supla::Sensor::ThermHygroPressMeter::iterateAlways() { + if (millis() - lastReadTime > 10000) { + pressureChannel.setNewValue(getPressure()); + } + ThermHygroMeter::iterateAlways(); +} + +bool Supla::Sensor::ThermHygroPressMeter::iterateConnected(void *srpc) { + bool response = true; + if (pressureChannel.isUpdateReady() && + millis() - pressureChannel.lastCommunicationTimeMs > 100) { + pressureChannel.lastCommunicationTimeMs = millis(); + pressureChannel.sendUpdate(srpc); + response = false; + } + + if (!Element::iterateConnected(srpc)) { + response = false; + } + return response; +} + +Supla::Element &Supla::Sensor::ThermHygroPressMeter::disableChannelState() { + pressureChannel.unsetFlag(SUPLA_CHANNEL_FLAG_CHANNELSTATE); + return ThermHygroMeter::disableChannelState(); +} + +Supla::Channel *Supla::Sensor::ThermHygroPressMeter::getSecondaryChannel() { + return &pressureChannel; +} + +void Supla::Sensor::ThermHygroPressMeter::addAction(int action, + ActionHandler &client, + int event) { + // delegate secondary channel event registration to secondary channel + switch (event) { + case Supla::ON_SECONDARY_CHANNEL_CHANGE: { + getSecondaryChannel()->addAction(action, client, event); + return; + } + } + // delegate all other events to primary channel + channel.addAction(action, client, event); +} + +void Supla::Sensor::ThermHygroPressMeter::addAction(int action, + ActionHandler *client, + int event) { + addAction(action, *client, event); +} diff --git a/lib/SuplaDevice/src/supla/sensor/therm_hygro_press_meter.h b/lib/SuplaDevice/src/supla/sensor/therm_hygro_press_meter.h index 55441650..a75fcbd7 100644 --- a/lib/SuplaDevice/src/supla/sensor/therm_hygro_press_meter.h +++ b/lib/SuplaDevice/src/supla/sensor/therm_hygro_press_meter.h @@ -25,41 +25,17 @@ namespace Supla { namespace Sensor { class ThermHygroPressMeter : public ThermHygroMeter { public: - ThermHygroPressMeter() { - pressureChannel.setType(SUPLA_CHANNELTYPE_PRESSURESENSOR); - pressureChannel.setDefault(SUPLA_CHANNELFNC_PRESSURESENSOR); - } - - virtual double getPressure() { - return PRESSURE_NOT_AVAILABLE; - } - - void iterateAlways() { - if (millis() - lastReadTime > 10000) { - pressureChannel.setNewValue(getPressure()); - } - ThermHygroMeter::iterateAlways(); - } - - bool iterateConnected(void *srpc) { - bool response = true; - if (pressureChannel.isUpdateReady() && - millis() - pressureChannel.lastCommunicationTimeMs > 100) { - pressureChannel.lastCommunicationTimeMs = millis(); - pressureChannel.sendUpdate(srpc); - response = false; - } - - if (!Element::iterateConnected(srpc)) { - response = false; - } - return response; - } - - Element &disableChannelState() { - pressureChannel.unsetFlag(SUPLA_CHANNEL_FLAG_CHANNELSTATE); - return ThermHygroMeter::disableChannelState(); - } + ThermHygroPressMeter(); + virtual double getPressure(); + void iterateAlways(); + bool iterateConnected(void *srpc); + Element &disableChannelState(); + Channel *getSecondaryChannel(); + + // Override local action methods in order to delegate execution to Channel and + // Secondary Channel + void addAction(int action, ActionHandler &client, int event); + void addAction(int action, ActionHandler *client, int event); protected: Channel pressureChannel; diff --git a/lib/SuplaDevice/src/supla/sensor/thermometer.cpp b/lib/SuplaDevice/src/supla/sensor/thermometer.cpp new file mode 100644 index 00000000..e9e8ccbb --- /dev/null +++ b/lib/SuplaDevice/src/supla/sensor/thermometer.cpp @@ -0,0 +1,34 @@ +/* + Copyright (C) AC SOFTWARE SP. Z O.O. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "thermometer.h" + +Supla::Sensor::Thermometer::Thermometer() : lastReadTime(0) { + channel.setType(SUPLA_CHANNELTYPE_THERMOMETER); + channel.setDefault(SUPLA_CHANNELFNC_THERMOMETER); +} + +double Supla::Sensor::Thermometer::getValue() { + return TEMPERATURE_NOT_AVAILABLE; +} + +void Supla::Sensor::Thermometer::iterateAlways() { + if (millis() - lastReadTime > 10000) { + lastReadTime = millis(); + channel.setNewValue(getValue()); + } +} + diff --git a/lib/SuplaDevice/src/supla/sensor/thermometer.h b/lib/SuplaDevice/src/supla/sensor/thermometer.h index 8dd11942..8be2f28a 100644 --- a/lib/SuplaDevice/src/supla/sensor/thermometer.h +++ b/lib/SuplaDevice/src/supla/sensor/thermometer.h @@ -17,36 +17,20 @@ #ifndef _thermometer_h #define _thermometer_h -#include "supla/channel.h" -#include "supla/element.h" +#include +#include "supla/channel_element.h" #define TEMPERATURE_NOT_AVAILABLE -275 namespace Supla { namespace Sensor { -class Thermometer : public Element { +class Thermometer : public ChannelElement { public: - Thermometer() : lastReadTime(0) { - channel.setType(SUPLA_CHANNELTYPE_THERMOMETER); - channel.setDefault(SUPLA_CHANNELFNC_THERMOMETER); - } - - virtual double getValue() { - return TEMPERATURE_NOT_AVAILABLE; - } - - void iterateAlways() { - if (lastReadTime + 10000 < millis()) { - lastReadTime = millis(); - channel.setNewValue(getValue()); - } - } + Thermometer(); + virtual double getValue(); + void iterateAlways(); protected: - Channel *getChannel() { - return &channel; - } - Channel channel; unsigned long lastReadTime; }; diff --git a/lib/SuplaDevice/src/supla/sensor/virtual_binary.cpp b/lib/SuplaDevice/src/supla/sensor/virtual_binary.cpp index c0a66b5f..04240e0d 100644 --- a/lib/SuplaDevice/src/supla/sensor/virtual_binary.cpp +++ b/lib/SuplaDevice/src/supla/sensor/virtual_binary.cpp @@ -38,7 +38,7 @@ void VirtualBinary::onInit() { channel.setNewValue(getValue()); } -void VirtualBinary::runAction(int event, int action) { +void VirtualBinary::handleAction(int event, int action) { (void)(event); switch (action) { case SET: { @@ -56,9 +56,5 @@ void VirtualBinary::runAction(int event, int action) { } } -Channel *VirtualBinary::getChannel() { - return &channel; -} - }; // namespace Sensor }; // namespace Supla diff --git a/lib/SuplaDevice/src/supla/sensor/virtual_binary.h b/lib/SuplaDevice/src/supla/sensor/virtual_binary.h index 5c30c5f6..94521b89 100644 --- a/lib/SuplaDevice/src/supla/sensor/virtual_binary.h +++ b/lib/SuplaDevice/src/supla/sensor/virtual_binary.h @@ -19,24 +19,21 @@ #include -#include "../channel.h" -#include "../element.h" -#include "../triggerable.h" +#include "../channel_element.h" +#include "../action_handler.h" #include "../actions.h" namespace Supla { namespace Sensor { -class VirtualBinary : public Element, public Triggerable { +class VirtualBinary : public ChannelElement, public ActionHandler { public: VirtualBinary(); bool getValue(); void iterateAlways(); void onInit(); - void runAction(int event, int action); + void handleAction(int event, int action); protected: - Channel *getChannel(); - Channel channel; bool state; unsigned long lastReadTime; }; diff --git a/lib/SuplaDevice/src/supla/sensor/weight.h b/lib/SuplaDevice/src/supla/sensor/weight.h index 8c318cbd..89a8efb6 100644 --- a/lib/SuplaDevice/src/supla/sensor/weight.h +++ b/lib/SuplaDevice/src/supla/sensor/weight.h @@ -17,14 +17,14 @@ #ifndef _weight_h #define _weight_h -#include "supla/channel.h" +#include "supla/channel_element.h" #include "supla/element.h" #define WEIGHT_NOT_AVAILABLE -1 namespace Supla { namespace Sensor { -class Weight : public Element { +class Weight : public ChannelElement { public: Weight() : lastReadTime(0) { channel.setType(SUPLA_CHANNELTYPE_WEIGHTSENSOR); @@ -43,12 +43,7 @@ class Weight : public Element { } } - protected: - Channel *getChannel() { - return &channel; - } - Channel channel; unsigned long lastReadTime; }; diff --git a/lib/SuplaDevice/src/supla/sensor/wind.h b/lib/SuplaDevice/src/supla/sensor/wind.h index 6869bc83..c8c147a7 100644 --- a/lib/SuplaDevice/src/supla/sensor/wind.h +++ b/lib/SuplaDevice/src/supla/sensor/wind.h @@ -17,14 +17,13 @@ #ifndef _wind_h #define _wind_h -#include "supla/channel.h" -#include "supla/element.h" +#include "supla/channel_element.h" #define WIND_NOT_AVAILABLE -1 namespace Supla { namespace Sensor { -class Wind: public Element { +class Wind: public ChannelElement { public: Wind() : lastReadTime(0) { channel.setType(SUPLA_CHANNELTYPE_WINDSENSOR); @@ -43,12 +42,7 @@ class Wind: public Element { } } - protected: - Channel *getChannel() { - return &channel; - } - Channel channel; unsigned long lastReadTime; }; diff --git a/lib/SuplaDevice/src/supla/storage/eeprom.cpp b/lib/SuplaDevice/src/supla/storage/eeprom.cpp index 79cd9f7b..8bba0afb 100644 --- a/lib/SuplaDevice/src/supla/storage/eeprom.cpp +++ b/lib/SuplaDevice/src/supla/storage/eeprom.cpp @@ -24,19 +24,25 @@ using namespace Supla; // By default, write to EEPROM every 3 min #define SUPLA_EEPROM_WRITING_PERIOD 3*60*1000 -Eeprom::Eeprom(unsigned int storageStartingOffset) +Eeprom::Eeprom(unsigned int storageStartingOffset, int reservedSize) : Storage(storageStartingOffset), - dataChanged(false) { + dataChanged(false), reservedSize(reservedSize) { setStateSavePeriod((unsigned long)SUPLA_EEPROM_WRITING_PERIOD); } bool Eeprom::init() { +#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + if (reservedSize <= 0) { #if defined(ARDUINO_ARCH_ESP8266) EEPROM.begin(1024); #elif defined(ARDUINO_ARCH_ESP32) EEPROM.begin(512); #endif + } else { + EEPROM.begin(reservedSize); + } delay(15); +#endif return Storage::init(); } diff --git a/lib/SuplaDevice/src/supla/storage/eeprom.h b/lib/SuplaDevice/src/supla/storage/eeprom.h index b9699ada..2dc40bfe 100644 --- a/lib/SuplaDevice/src/supla/storage/eeprom.h +++ b/lib/SuplaDevice/src/supla/storage/eeprom.h @@ -23,7 +23,7 @@ namespace Supla { class Eeprom : public Storage { public: - Eeprom(unsigned int storageStartingOffset = 0); + Eeprom(unsigned int storageStartingOffset = 0, int reservedSize = -1); bool init(); void commit(); @@ -31,6 +31,7 @@ class Eeprom : public Storage { int readStorage(unsigned int, unsigned char *, int, bool); int writeStorage(unsigned int, const unsigned char *, int); + int reservedSize; bool dataChanged; }; diff --git a/lib/SuplaDevice/src/supla/storage/storage.cpp b/lib/SuplaDevice/src/supla/storage/storage.cpp index 52dfaf8e..9f78faf0 100644 --- a/lib/SuplaDevice/src/supla/storage/storage.cpp +++ b/lib/SuplaDevice/src/supla/storage/storage.cpp @@ -15,6 +15,7 @@ */ #include +#include #include "storage.h" @@ -83,6 +84,12 @@ bool Storage::SaveStateAllowed(unsigned long ms) { return false; } +void Storage::ScheduleSave(unsigned long delayMs) { + if (Instance()) { + Instance()->scheduleSave(delayMs); + } +} + Storage::Storage(unsigned int storageStartingOffset) : storageStartingOffset(storageStartingOffset), deviceConfigOffset(0), @@ -100,6 +107,10 @@ Storage::Storage(unsigned int storageStartingOffset) instance = this; } +Storage::~Storage() { + instance = nullptr; +} + void Storage::prepareState(bool performDryRun) { dryRun = performDryRun; newSectionSize = 0; @@ -319,3 +330,11 @@ bool Storage::saveStateAllowed(unsigned long ms) { return false; } +void Storage::scheduleSave(unsigned long delayMs) { + unsigned long currentMs = millis(); + unsigned long newTimestamp = currentMs - saveStatePeriod - 1 + delayMs; + + if (currentMs - lastWriteTimestamp < currentMs - newTimestamp) { + lastWriteTimestamp = newTimestamp; + } +} diff --git a/lib/SuplaDevice/src/supla/storage/storage.h b/lib/SuplaDevice/src/supla/storage/storage.h index 66284302..0537749c 100644 --- a/lib/SuplaDevice/src/supla/storage/storage.h +++ b/lib/SuplaDevice/src/supla/storage/storage.h @@ -36,8 +36,10 @@ class Storage { static void PrepareState(bool dryRun = false); static bool FinalizeSaveState(); static bool SaveStateAllowed(unsigned long); + static void ScheduleSave(unsigned long delayMs); Storage(unsigned int storageStartingOffset = 0); + virtual ~Storage(); // Changes default state save period time virtual void setStateSavePeriod(unsigned long periodMs); @@ -51,6 +53,7 @@ class Storage { virtual void prepareState(bool performDryRun); virtual bool finalizeSaveState(); virtual bool saveStateAllowed(unsigned long); + virtual void scheduleSave(unsigned long delayMs); virtual void commit() = 0; diff --git a/lib/SuplaDevice/src/supla/timer.cpp b/lib/SuplaDevice/src/supla/timer.cpp index b77eb54a..8a43039f 100644 --- a/lib/SuplaDevice/src/supla/timer.cpp +++ b/lib/SuplaDevice/src/supla/timer.cpp @@ -20,7 +20,11 @@ #include "timer.h" #if defined(ARDUINO_ARCH_ESP32) -#include +#include +#endif + +#ifdef ARDUINO_ARCH_ESP8266 +#include #endif namespace { @@ -38,14 +42,14 @@ void esp_fastTimer_cb(void *timer_arg) { SuplaDevice.onFastTimer(); } #elif defined(ARDUINO_ARCH_ESP32) -hw_timer_t *supla_esp_timer = NULL; -hw_timer_t *supla_esp_fastTimer = NULL; +Ticker supla_esp_timer; +Ticker supla_esp_fastTimer; -void IRAM_ATTR esp_timer_cb() { +void esp_timer_cb() { SuplaDevice.onTimer(); } -void IRAM_ATTR esp_fastTimer_cb() { +void esp_fastTimer_cb() { SuplaDevice.onFastTimer(); } #else @@ -71,17 +75,8 @@ void initTimers() { os_timer_arm(&supla_esp_fastTimer, 1, 1); #elif defined(ARDUINO_ARCH_ESP32) - supla_esp_timer = timerBegin(0, 80, true); // timer 0, div 80 - timerAttachInterrupt(supla_esp_timer, &esp_timer_cb, true); // attach callback - timerAlarmWrite(supla_esp_timer, 10 * 1000, false); // set time in us - timerAlarmEnable(supla_esp_timer); // enable interrupt - - supla_esp_fastTimer = timerBegin(1, 80, true); - timerAttachInterrupt(supla_esp_fastTimer, - &esp_fastTimer_cb, - true); // attach callback - timerAlarmWrite(supla_esp_fastTimer, 1 * 1000, false); // set time in us - timerAlarmEnable(supla_esp_fastTimer); // enable interrupt + supla_esp_timer.attach_ms(10, esp_timer_cb); + supla_esp_fastTimer.attach_ms(1, esp_fastTimer_cb); #else // Timer 1 for interrupt frequency 100 Hz (10 ms) TCCR1A = 0; // set entire TCCR1A register to 0 diff --git a/lib/SuplaDevice/src/supla/tools.cpp b/lib/SuplaDevice/src/supla/tools.cpp index a4c9038c..f053ad41 100644 --- a/lib/SuplaDevice/src/supla/tools.cpp +++ b/lib/SuplaDevice/src/supla/tools.cpp @@ -41,3 +41,16 @@ void float2DoublePacked(float number, uint8_t *bar, int byteOrder) { } #endif } + +float doublePacked2float(uint8_t *bar) { + _FLOATCONV fl; + _DBLCONV dbl; + for (int i = 0; i < 8; i++) { + dbl.b[i] = bar[i]; + } + fl.p.s = dbl.p.s; + fl.p.m = dbl.p.m; + fl.p.e = dbl.p.e + 127 - 1023; // exponent adjust + + return fl.f; +} diff --git a/lib/SuplaDevice/src/supla/tools.h b/lib/SuplaDevice/src/supla/tools.h index 0a2c2a6d..ab2ac31f 100644 --- a/lib/SuplaDevice/src/supla/tools.h +++ b/lib/SuplaDevice/src/supla/tools.h @@ -19,9 +19,11 @@ #ifndef _tools_H_ #define _tools_H_ +#include #include "supla-common/IEEE754tools.h" void float2DoublePacked(float number, uint8_t *bar, int byteOrder = LSBFIRST); +float doublePacked2float(uint8_t *bar); #endif diff --git a/lib/esp8266-oled-ssd1306-master/.editorconfig b/lib/esp8266-oled-ssd1306-master/.editorconfig new file mode 100644 index 00000000..79b24ae3 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/.editorconfig @@ -0,0 +1,14 @@ +# This file is for unifying the coding style for different editors and IDEs +# editorconfig.org + +root = true + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 2 +max_line_length = 120 +curly_bracket_next_line = false diff --git a/lib/esp8266-oled-ssd1306-master/.gitignore b/lib/esp8266-oled-ssd1306-master/.gitignore new file mode 100644 index 00000000..80e74646 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/.gitignore @@ -0,0 +1,2 @@ +.vscode +.pio diff --git a/lib/esp8266-oled-ssd1306-master/.travis.yml b/lib/esp8266-oled-ssd1306-master/.travis.yml new file mode 100644 index 00000000..49ea5b62 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/.travis.yml @@ -0,0 +1,29 @@ +# documentation at https://docs.platformio.org/en/latest/integration/ci/travis.html + +language: python +python: + - "3.7" + +# Cache PlatformIO packages using Travis CI container-based infrastructure +sudo: false +cache: + directories: + - "~/.platformio" + - $HOME/.cache/pip + +env: + - PLATFORMIO_CI_SRC=examples/SSD1306UiDemo + - PLATFORMIO_CI_SRC=examples/SSD1306SimpleDemo + - PLATFORMIO_CI_SRC=examples/SSD1306DrawingDemo + - PLATFORMIO_CI_SRC=examples/SSD1306OTADemo + - PLATFORMIO_CI_SRC=examples/SSD1306ClockDemo + - PLATFORMIO_CI_SRC=examples/SSD1306TwoScreenDemo + + +install: + - pip install -U platformio + - pio update + - platformio lib -g install "paulstoffregen/Time@^1.6" + +script: + - platformio ci --lib="." --board=nodemcuv2 diff --git a/lib/esp8266-oled-ssd1306-master/CMakeLists.txt b/lib/esp8266-oled-ssd1306-master/CMakeLists.txt new file mode 100644 index 00000000..705d9a07 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/CMakeLists.txt @@ -0,0 +1,4 @@ +set(COMPONENT_ADD_INCLUDEDIRS src) +set(COMPONENT_PRIV_REQUIRES arduino-esp32) +set(COMPONENT_SRCDIRS src) +register_component() diff --git a/lib/esp8266-oled-ssd1306-master/CONTRIBUTING.md b/lib/esp8266-oled-ssd1306-master/CONTRIBUTING.md new file mode 100644 index 00000000..6ed01ad3 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/CONTRIBUTING.md @@ -0,0 +1,70 @@ +# Contributing to ThingPulse OLED SSD1306 + +:+1::tada: First off, thanks for taking the time to contribute! :tada::+1: + +The following is a set of guidelines for contributing to the ThingPulse OLED SSD1306 library on GitHub. These are just guidelines, not rules, use your best judgment and feel free to propose changes to this document in a pull request. + +It is appreciated if you raise an issue _before_ you start changing the code, discussing the proposed change; emphasizing that you are proposing to develop the patch yourself, and outlining the strategy for implementation. This type of discussion is what we should be doing on the issues list and it is better to do this before or in parallel to developing the patch rather than having "you should have done it this way" type of feedback on the PR itself. + +### Table Of Contents +* [General remarks](#general-remarks) +* [Writing Documentation](#writing-documentation) +* [Working with Git and GitHub](#working-with-git-and-github) + * [General flow](#general-flow) + * [Keeping your fork in sync](#keeping-your-fork-in-sync) + * [Commit messages](#commit-messages) + +## General remarks +We are a friendly and welcoming community and look forward to your contributions. Once your contribution is integrated into this repository we feel responsible for it. Therefore, be prepared for constructive feedback. Before we merge anything we need to ensure that it fits in and is consistent with the rest of code. +If you made something really cool but won't spend the time to integrate it into this upstream project please still share it in your fork on GitHub. If you mention it in an issue we'll take a look at it anyway. + +## Writing Documentation +ThingPulse maintains documentation for its products at [https://github.com/thingpulse/docs/](https://github.com/thingpulse/docs/). If you contribute features for this project that require altering the respective product guide then we ask you to prepare a pull request with the necessary documentation changes as well. + +## Working with Git and GitHub + +Avoid intermediate merge commits. [Rebase](https://www.atlassian.com/git/tutorials/merging-vs-rebasing) your feature branch onto `master` to pull updates and verify your local changes against them before placing the pull request. + +### General flow +1. [Fork](https://help.github.com/articles/fork-a-repo) this repository on GitHub. +1. [Create a branch](https://help.github.com/articles/creating-and-deleting-branches-within-your-repository/#creating-a-branch) in your fork on GitHub **based on the `master` branch**. +1. Clone the fork on your machine with `git clone https://github.com//.git` +1. `cd ` then run `git remote add upstream https://github.com/ThingPulse/esp8266-oled-ssd1306` +1. `git checkout ` +1. Make changes to the code base and commit them using e.g. `git commit -a -m 'Look ma, I did it'` +1. When you're done bring your fork up-to-date with the upstream repo ([see below](#keeping-your-fork-in-sync)). Then rebase your branch on `master` running `git rebase master`. +1. `git push` +1. [Create a pull request](https://help.github.com/articles/creating-a-pull-request/) (PR) on GitHub. + +This is just one way of doing things. If you're proficient in Git matters you're free to choose your own. If you want to read more then the [GitHub chapter in the Git book](http://git-scm.com/book/en/v2/GitHub-Contributing-to-a-Project#The-GitHub-Flow) is a way to start. [GitHub's own documentation](https://help.github.com/categories/collaborating/) contains a wealth of information as well. + +### Keeping your fork in sync +You need to sync your fork with the upstream repository from time to time, latest before you rebase (see flow above). + +1. `git fetch upstream` +1. `git checkout master` +1. `git merge upstream/master` + +### Commit messages + +From: [http://git-scm.com/book/ch5-2.html](http://git-scm.com/book/ch5-2.html) +
+Short (50 chars or less) summary of changes
+
+More detailed explanatory text, if necessary.  Wrap it to about 72
+characters or so.  In some contexts, the first line is treated as the
+subject of an email and the rest of the text as the body.  The blank
+line separating the summary from the body is critical (unless you omit
+the body entirely); tools like rebase can get confused if you run the
+two together.
+
+Further paragraphs come after blank lines.
+
+- Bullet points are okay, too
+- Typically a hyphen or asterisk is used for the bullet, preceded by a
+   single space, with blank lines in between, but conventions vary here
+
+ +Don't forget to [reference affected issues](https://help.github.com/articles/closing-issues-via-commit-messages/) in the commit message to have them closed automatically on GitHub. + +[Amend](https://help.github.com/articles/changing-a-commit-message/) your commit messages if necessary to make sure what the world sees on GitHub is as expressive and meaningful as possible. diff --git a/lib/esp8266-oled-ssd1306-master/README.md b/lib/esp8266-oled-ssd1306-master/README.md new file mode 100644 index 00000000..ed2d8b13 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/README.md @@ -0,0 +1,431 @@ +[![Build Status](https://travis-ci.org/ThingPulse/esp8266-oled-ssd1306.svg?branch=master)](https://travis-ci.org/ThingPulse/esp8266-oled-ssd1306) + +# ThingPulse OLED SSD1306 (ESP8266/ESP32/Mbed-OS) + +This is a driver for SSD1306 128x64, 128x32, 64x48 and 64x32 OLED displays running on the Arduino/ESP8266 & ESP32 and mbed-os platforms. +Can be used with either the I2C or SPI version of the display. + +This library drives the OLED display included in the [ThingPulse IoT starter kit](https://thingpulse.com/product/esp8266-iot-electronics-starter-kit-weatherstation-planespotter-worldclock/) aka classic kit aka weather station kit. + +[![ThingPulse ESP8266 WeatherStation Classic Kit](https://github.com/ThingPulse/esp8266-weather-station/blob/master/resources/ThingPulse-ESP8266-Weather-Station.jpeg?raw=true)](https://thingpulse.com/product/esp8266-iot-electronics-starter-kit-weatherstation-planespotter-worldclock/) + +You can either download this library as a zip file and unpack it to your Arduino/libraries folder or find it in the Arduino library manager under "ESP8266 and ESP32 Oled Driver for SSD1306 display". For mbed-os a copy of the files are available as an mbed-os library. + +It is also available as a [PlatformIO library](https://platformio.org/lib/show/562/ESP8266%20and%20ESP32%20OLED%20driver%20for%20SSD1306%20displays/examples). Just execute the following command: +``` +platformio lib install 562 +``` + +## Service level promise + +
+This is a ThingPulse prime project. See our open-source commitment declaration for what this means.
+ +## Credits + +This library has initially been written by Daniel Eichhorn ([@squix78](https://github.com/squix78)). Many thanks go to Fabrice Weinberg ([@FWeinb](https://github.com/FWeinb)) for optimizing and refactoring many aspects of the library. Also many thanks to the many committers who helped to add new features and who fixed many bugs. Mbed-OS support and other improvements were contributed by Helmut Tschemernjak ([@helmut64](https://github.com/helmut64)). + +The init sequence for the SSD1306 was inspired by Adafruit's library for the same display. + +## mbed-os +This library has been adopted to support the ARM mbed-os environment. A copy of this library is available in mbed-os under the name OLED_SSD1306 by Helmut Tschemernjak. An alternate installation option is to copy the following files into your mbed-os project: OLEDDisplay.cpp OLEDDisplay.h OLEDDisplayFonts.h OLEDDisplayUi.cpp OLEDDisplayUi.h SSD1306I2C.h + +## Usage + +Check out the examples folder for a few comprehensive demonstrations how to use the library. Also check out the [ESP8266 Weather Station](https://github.com/ThingPulse/esp8266-weather-station) library which uses the OLED library to display beautiful weather information. + +## Upgrade + +The API changed a lot with the 3.0 release. If you were using this library with older versions please have a look at the [Upgrade Guide](UPGRADE-3.0.md). + +Going from 3.x version to 4.0 a lot of internals changed and compatibility for more displays was added. Please read the [Upgrade Guide](UPGRADE-4.0.md). + +## Features + +* Draw pixels at given coordinates +* Draw lines from given coordinates to given coordinates +* Draw or fill a rectangle with given dimensions +* Draw Text at given coordinates: + * Define Alignment: Left, Right and Center + * Set the Fontface you want to use (see section Fonts below) + * Limit the width of the text by an amount of pixels. Before this widths will be reached, the renderer will wrap the text to a new line if possible +* Display content in automatically side scrolling carousel + * Define transition cycles + * Define how long one frame will be displayed + * Draw the different frames in callback methods + * One indicator per frame will be automatically displayed. The active frame will be displayed from inactive once + +## Fonts + +Fonts are defined in a proprietary but open format. You can create new font files by choosing from a given list +of open sourced Fonts from this web app: http://oleddisplay.squix.ch +Choose the font family, style and size, check the preview image and if you like what you see click the "Create" button. This will create the font array in a text area form where you can copy and paste it into a new or existing header file. + + +![FontTool](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/FontTool.png) + +## Hardware Abstraction + +The library supports different protocols to access the OLED display. Currently there is support for I2C using the built in Wire.h library, I2C by using the much faster [BRZO I2C library](https://github.com/pasko-zh/brzo_i2c) written in assembler and it also supports displays which come with the SPI interface. + +### I2C with Wire.h + +```C++ +#include +#include "SSD1306Wire.h" + +// for 128x64 displays: +SSD1306Wire display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL +// for 128x32 displays: +// SSD1306Wire display(0x3c, SDA, SCL, GEOMETRY_128_32); // ADDRESS, SDA, SCL, GEOMETRY_128_32 (or 128_64) +// for using 2nd Hardware I2C (if available) +// SSD1306Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_TWO); //default value is I2C_ONE if not mentioned +// By default SD1306Wire set I2C frequency to 700000, you can use set either another frequency or skip setting the frequency by providing -1 value +// SSD1306Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_ONE, 400000); //set I2C frequency to 400kHz +// SSD1306Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_ONE, -1); //skip setting the I2C bus frequency +``` + +for a SH1106: +```C++ +#include +#include "SH1106Wire.h" + +SH1106Wire display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL +// By default SH1106Wire set I2C frequency to 700000, you can use set either another frequency or skip setting the frequency by providing -1 value +// SH1106Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_ONE, 400000); //set I2C frequency to 400kHz +// SH1106Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_ONE, -1); //skip setting the I2C bus frequency +``` + +### I2C with brzo_i2c + +```C++ +#include +#include "SSD1306Brzo.h" + +SSD1306Brzo display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL +``` +or for the SH1106: +```C++ +#include +#include "SH1106Brzo.h" + +SH1106Brzo display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL +``` + +### SPI + +```C++ +#include +#include "SSD1306Spi.h" + +SSD1306Spi display(D0, D2, D8); // RES, DC, CS +``` +or for the SH1106: +```C++ +#include +#include "SH1106Spi.h" + +SH1106Spi display(D0, D2); // RES, DC +``` + +## API + +### Display Control + +```C++ +// Initialize the display +void init(); + +// Free the memory used by the display +void end(); + +// Cycle through the initialization +void resetDisplay(void); + +// Connect again to the display through I2C +void reconnect(void); + +// Turn the display on +void displayOn(void); + +// Turn the display offs +void displayOff(void); + +// Clear the local pixel buffer +void clear(void); + +// Write the buffer to the display memory +void display(void); + +// Inverted display mode +void invertDisplay(void); + +// Normal display mode +void normalDisplay(void); + +// Set display contrast +// really low brightness & contrast: contrast = 10, precharge = 5, comdetect = 0 +// normal brightness & contrast: contrast = 100 +void setContrast(uint8_t contrast, uint8_t precharge = 241, uint8_t comdetect = 64); + +// Convenience method to access +void setBrightness(uint8_t); + +// Turn the display upside down +void flipScreenVertically(); + +// Draw the screen mirrored +void mirrorScreen(); +``` + +## Pixel drawing + +```C++ + +/* Drawing functions */ +// Sets the color of all pixel operations +// color : BLACK, WHITE, INVERSE +void setColor(OLEDDISPLAY_COLOR color); + +// Draw a pixel at given position +void setPixel(int16_t x, int16_t y); + +// Draw a line from position 0 to position 1 +void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1); + +// Draw the border of a rectangle at the given location +void drawRect(int16_t x, int16_t y, int16_t width, int16_t height); + +// Fill the rectangle +void fillRect(int16_t x, int16_t y, int16_t width, int16_t height); + +// Draw the border of a circle +void drawCircle(int16_t x, int16_t y, int16_t radius); + +// Fill circle +void fillCircle(int16_t x, int16_t y, int16_t radius); + +// Draw a line horizontally +void drawHorizontalLine(int16_t x, int16_t y, int16_t length); + +// Draw a lin vertically +void drawVerticalLine(int16_t x, int16_t y, int16_t length); + +// Draws a rounded progress bar with the outer dimensions given by width and height. Progress is +// a unsigned byte value between 0 and 100 +void drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress); + +// Draw a bitmap in the internal image format +void drawFastImage(int16_t x, int16_t y, int16_t width, int16_t height, const uint8_t *image); + +// Draw a XBM +void drawXbm(int16_t x, int16_t y, int16_t width, int16_t height, const char* xbm); +``` + +## Text operations + +``` C++ +void drawString(int16_t x, int16_t y, String text); + +// Draws a String with a maximum width at the given location. +// If the given String is wider than the specified width +// The text will be wrapped to the next line at a space or dash +void drawStringMaxWidth(int16_t x, int16_t y, int16_t maxLineWidth, String text); + +// Returns the width of the const char* with the current +// font settings +uint16_t getStringWidth(const char* text, uint16_t length); + +// Convencience method for the const char version +uint16_t getStringWidth(String text); + +// Specifies relative to which anchor point +// the text is rendered. Available constants: +// TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER_BOTH +void setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment); + +// Sets the current font. Available default fonts +// ArialMT_Plain_10, ArialMT_Plain_16, ArialMT_Plain_24 +// Or create one with the font tool at http://oleddisplay.squix.ch +void setFont(const uint8_t* fontData); +``` + +## Ui Library (OLEDDisplayUi) + +The Ui Library is used to provide a basic set of Ui elements called, `Frames` and `Overlays`. A `Frame` is used to provide +information the default behaviour is to display a `Frame` for a defined time and than move to the next. The library also provides an `Indicator` that will be updated accordingly. An `Overlay` on the other hand is a pieces of information (e.g. a clock) that is displayed always at the same position. + + +```C++ +/** + * Initialise the display + */ +void init(); + +/** + * Configure the internal used target FPS + */ +void setTargetFPS(uint8_t fps); + +/** + * Enable automatic transition to next frame after the some time can be configured with + * `setTimePerFrame` and `setTimePerTransition`. + */ +void enableAutoTransition(); + +/** + * Disable automatic transition to next frame. + */ +void disableAutoTransition(); + +/** + * Set the direction if the automatic transitioning + */ +void setAutoTransitionForwards(); +void setAutoTransitionBackwards(); + +/** + * Set the approx. time a frame is displayed + */ +void setTimePerFrame(uint16_t time); + +/** + * Set the approx. time a transition will take + */ +void setTimePerTransition(uint16_t time); + +/** + * Draw the indicator. + * This is the default state for all frames if + * the indicator was hidden on the previous frame + * it will be slided in. + */ +void enableIndicator(); + +/** + * Don't draw the indicator. + * This will slide out the indicator + * when transitioning to the next frame. + */ +void disableIndicator(); + +/** + * Enable drawing of all indicators. + */ +void enableAllIndicators(); + +/** + * Disable drawing of all indicators. + */ +void disableAllIndicators(); + +/** + * Set the position of the indicator bar. + */ +void setIndicatorPosition(IndicatorPosition pos); + +/** + * Set the direction of the indicator bar. Defining the order of frames ASCENDING / DESCENDING + */ +void setIndicatorDirection(IndicatorDirection dir); + +/** + * Set the symbol to indicate an active frame in the indicator bar. + */ +void setActiveSymbol(const char* symbol); + +/** + * Set the symbol to indicate an inactive frame in the indicator bar. + */ +void setInactiveSymbol(const char* symbol); + +/** + * Configure what animation is used to transition from one frame to another + */ +void setFrameAnimation(AnimationDirection dir); + +/** + * Add frame drawing functions + */ +void setFrames(FrameCallback* frameFunctions, uint8_t frameCount); + +/** + * Add overlays drawing functions that are draw independent of the Frames + */ +void setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount); + +/** + * Set the function that will draw each step + * in the loading animation + */ +void setLoadingDrawFunction(LoadingDrawFunction loadingDrawFunction); + +/** + * Run the loading process + */ +void runLoadingProcess(LoadingStage* stages, uint8_t stagesCount); + +// Manuell Controll +void nextFrame(); +void previousFrame(); + +/** + * Switch without transition to frame `frame`. + */ +void switchToFrame(uint8_t frame); + +/** + * Transition to frame `frame`, when the `frame` number is bigger than the current + * frame the forward animation will be used, otherwise the backwards animation is used. + */ +void transitionToFrame(uint8_t frame); + +// State Info +OLEDDisplayUiState* getUiState(); + +// This needs to be called in the main loop +// the returned value is the remaining time (in ms) +// you have to draw after drawing to keep the frame budget. +int8_t update(); +``` + +## Example: SSD1306Demo + +### Frame 1 +![DemoFrame1](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/DemoFrame1.jpg) + +This frame shows three things: + * How to draw an xbm image + * How to draw a static text which is not moved by the frame transition + * The active/inactive frame indicators + +### Frame 2 +![DemoFrame2](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/DemoFrame2.jpg) + +Currently there are one fontface with three sizes included in the library: Arial 10, 16 and 24. Once the converter is published you will be able to convert any ttf font into the used format. + +### Frame 3 + +![DemoFrame3](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/DemoFrame3.jpg) + +This frame demonstrates the text alignment. The coordinates in the frame show relative to which position the texts have been rendered. + +### Frame 4 + +![DemoFrame4](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/DemoFrame4.jpg) + +This shows how to use define a maximum width after which the driver automatically wraps a word to the next line. This comes in very handy if you have longer texts to display. + +### SPI version + +![SPIVersion](https://github.com/neptune2/esp8266-oled-ssd1306/raw/master/resources/SPI_version.jpg) + +This shows the code working on the SPI version of the display. See demo code for ESP8266 pins used. + +## Selection of projects using this library + + * [QRCode ESP8266](https://github.com/anunpanya/ESP8266_QRcode) (by @anunpanya) + * [Scan I2C](https://github.com/hallard/Scan-I2C-WiFi) (by @hallard) + * [ThingPulse Weather Station](https://github.com/ThingPulse/esp8266-weather-station) + * [Meshtastic](https://www.meshtastic.org/) - an open source GPS communicator mesh radio + * Yours? diff --git a/lib/esp8266-oled-ssd1306-master/README_GEOMETRY_64_48.md b/lib/esp8266-oled-ssd1306-master/README_GEOMETRY_64_48.md new file mode 100644 index 00000000..9b9bf912 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/README_GEOMETRY_64_48.md @@ -0,0 +1,20 @@ +# GEOMETRY_64_48 + +The 64x48 geometry setting are working with the `Wire.h` and `brzo_i2c` libraries. + +I've tested it successfully with a WEMOS D1 mini Lite and a WEMOS OLED shield + +Initialization code: + +- Wire +``` +#include +#include +SSD1306Wire display(0x3c, D2, D1, GEOMETRY_64_48 ); // WEMOS OLED shield +``` + +- BRZO i2c +``` +#include +SSD1306Brzo display(0x3c, D2, D1, GEOMETRY_64_48 ); // WEMOS OLED Shield +``` diff --git a/lib/esp8266-oled-ssd1306-master/UPGRADE-3.0.md b/lib/esp8266-oled-ssd1306-master/UPGRADE-3.0.md new file mode 100644 index 00000000..e7a315bc --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/UPGRADE-3.0.md @@ -0,0 +1,125 @@ +# Upgrade from 2.0 to 3.0 + +While developing version 3.0 we made some breaking changes to the public +API of this library. This document will help you update your code to work with +version 3.0 + +## Font Definitions + +To get better performance and a smaller font definition format, we change the memory +layout of the font definition format. If you are using custom fonts not included in +this library we updated the font generator [here](http://oleddisplay.squix.ch/#/home). +Please update your fonts to be working with 3.0 by selecting the respective version in the dropdown. + + +## Architectural Changes + +To become a more versatile library for the SSD1306 chipset we abstracted the +hardware connection into subclasses of the base display class now called `OLEDDisplay`. +This library is currently shipping with three implementations: + + * `SSD1306Wire` implementing the I2C protocol using the Wire Library. + * `SSD1306Brzo` implementing the I2C protocol using the faster [`brzo_i2c`](https://github.com/pasko-zh/brzo_i2c) library. + * `SSD1306Spi` implementing the SPI protocol. + +To keep backwards compatiblity with the old API `SSD1306` is an alias of `SSD1306Wire`. +If you are not using the UI components you don't have to change anything to keep your code working. + +## Name Changes + +[Naming things is hard](http://martinfowler.com/bliki/TwoHardThings.html), to better reflect our intention with this library +we changed the name of the base class to `OLEDDisplay` and the UI library accordingly to `OLEDDisplayUi`. +As a consequence the type definitions of all frame and overlay related functions changed. +This means that you have to update all your frame drawing callbacks from: + +```c +bool frame1(SSD1306 *display, SSD1306UiState* state, int x, int y); +``` + +too + +```c +void frame1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y); +``` + +And your overlay drawing functions from: + +```c +bool overlay1(SSD1306 *display, SSD1306UiState* state); +``` + +too + +```c +void overlay1(OLEDDisplay *display, OLEDDisplayUiState* state); +``` + +## New Features + +### Loading Animation + +While using this library ourself we noticed a pattern emerging. We want to drawing +a loading progress while connecting to WiFi and updating weather data etc. + +The simplest thing was to add the function `drawProgressBar(x, y, width, height, progress)` +,where `progress` is between `0` and `100`, right to the `OLEDDisplay` class. + +But we didn't stop there. We added a new feature to the `OLEDDisplayUi` called `LoadingStages`. +You can define your loading process like this: + +```c++ +LoadingStage loadingStages[] = { + { + .process = "Connect to WiFi", + .callback = []() { + // Connect to WiFi + } + }, + { + .process = "Get time from NTP", + .callback = []() { + // Get current time via NTP + } + } + // more steps +}; + +int LOADING_STAGES_COUNT = sizeof(loadingStages) / sizeof(LoadingStage); +``` + +After defining your array of `LoadingStages` you can then run the loading process by using +`ui.runLoadingProcess(loadingStages, LOADING_STAGES_COUNT)`. This will give you a +nice little loading animation you can see in the beginning of [this](https://vimeo.com/168362918) +video. + +To further customize this you are free to define your own `LoadingDrawFunction` like this: + +```c +void myLoadingDraw(OLEDDisplay *display, LoadingStage* stage, uint8_t progress) { + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(ArialMT_Plain_10); + // stage->process contains the text of the current progress e.q. "Connect to WiFi" + display->drawString(64, 18, stage->process); + // you could just print the current process without the progress bar + display->drawString(64, 28, progress); +} +``` + +After defining a function like that, you can pass it to the Ui library by use +`ui.setLoadingDrawFunction(myLoadingDraw)`. + + +### Text Logging + +It is always useful to display some text on the display without worrying to much +where it goes and managing it. In 3.0 we made the `OLEDDisplay` class implement +[`Print`](https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/Print.h) +so you can use it like you would use `Serial`. We calls this feature `LogBuffer` +and the only thing you have to do is to define how many lines you want to display +and how many characters there are on average on each. This is done by calling +`setLogBuffer(lines, chars);`. If there is not enough memory the function will +return false. + +After that you can draw the `LogBuffer` anywhere you want by calling `drawLogBuffer(x, y)`. +(Note: You have to call `display()` to update the screen) +We made a [video](https://www.youtube.com/watch?v=8Fiss77A3TE) showing this feature in action. diff --git a/lib/esp8266-oled-ssd1306-master/UPGRADE-4.0.md b/lib/esp8266-oled-ssd1306-master/UPGRADE-4.0.md new file mode 100644 index 00000000..4b17693f --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/UPGRADE-4.0.md @@ -0,0 +1,27 @@ +# Upgrade from 3.x to 4.0 + +There are changes that breaks compatibility with older versions. + +1. You'll have to change data type for all your binary resources such as images and fonts from + + ```c + const char MySymbol[] PROGMEM = { + ``` + + to + + ```c + const uint8_t MySymbol[] PROGMEM = { + ``` + +1. Arguments of `setContrast` from `char` to `uint8_t` + + ```c++ + void OLEDDisplay::setContrast(char contrast, char precharge, char comdetect); + ``` + + to + + ```c++ + void OLEDDisplay::setContrast(uint8_t contrast, uint8_t precharge, uint8_t comdetect); + ``` diff --git a/lib/esp8266-oled-ssd1306-master/component.mk b/lib/esp8266-oled-ssd1306-master/component.mk new file mode 100644 index 00000000..23a01a07 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/component.mk @@ -0,0 +1,3 @@ +COMPONENT_ADD_INCLUDEDIRS := src +COMPONENT_SRCDIRS := src +CXXFLAGS += -Wno-ignored-qualifiers diff --git a/lib/esp8266-oled-ssd1306-master/examples/SSD1306ClockDemo/SSD1306ClockDemo.ino b/lib/esp8266-oled-ssd1306-master/examples/SSD1306ClockDemo/SSD1306ClockDemo.ino new file mode 100644 index 00000000..63102cf0 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/examples/SSD1306ClockDemo/SSD1306ClockDemo.ino @@ -0,0 +1,214 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * + * 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. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#include + +// Include the correct display library +// For a connection via I2C using Wire include +#include // Only needed for Arduino 1.6.5 and earlier +#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"` +// or #include "SH1106Wire.h", legacy include: `#include "SH1106.h"` +// For a connection via I2C using brzo_i2c (must be installed) include +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Brzo.h" +// #include "SH1106Brzo.h" +// For a connection via SPI include +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Spi.h" +// #include "SH1106SPi.h" + +// Include the UI lib +#include "OLEDDisplayUi.h" + +// Include custom images +#include "images.h" + +// Use the corresponding display class: + +// Initialize the OLED display using SPI +// D5 -> CLK +// D7 -> MOSI (DOUT) +// D0 -> RES +// D2 -> DC +// D8 -> CS +// SSD1306Spi display(D0, D2, D8); +// or +// SH1106Spi display(D0, D2); + +// Initialize the OLED display using brzo_i2c +// D3 -> SDA +// D5 -> SCL +// SSD1306Brzo display(0x3c, D3, D5); +// or +// SH1106Brzo display(0x3c, D3, D5); + +// Initialize the OLED display using Wire library +SSD1306Wire display(0x3c, D3, D5); +// SH1106 display(0x3c, D3, D5); + +OLEDDisplayUi ui ( &display ); + +int screenW = 128; +int screenH = 64; +int clockCenterX = screenW/2; +int clockCenterY = ((screenH-16)/2)+16; // top yellow part is 16 px height +int clockRadius = 23; + +// utility function for digital clock display: prints leading 0 +String twoDigits(int digits){ + if(digits < 10) { + String i = '0'+String(digits); + return i; + } + else { + return String(digits); + } +} + +void clockOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) { + +} + +void analogClockFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { +// ui.disableIndicator(); + + // Draw the clock face +// display->drawCircle(clockCenterX + x, clockCenterY + y, clockRadius); + display->drawCircle(clockCenterX + x, clockCenterY + y, 2); + // + //hour ticks + for( int z=0; z < 360;z= z + 30 ){ + //Begin at 0° and stop at 360° + float angle = z ; + angle = ( angle / 57.29577951 ) ; //Convert degrees to radians + int x2 = ( clockCenterX + ( sin(angle) * clockRadius ) ); + int y2 = ( clockCenterY - ( cos(angle) * clockRadius ) ); + int x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 8 ) ) ) ); + int y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 8 ) ) ) ); + display->drawLine( x2 + x , y2 + y , x3 + x , y3 + y); + } + + // display second hand + float angle = second() * 6 ; + angle = ( angle / 57.29577951 ) ; //Convert degrees to radians + int x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 5 ) ) ) ); + int y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 5 ) ) ) ); + display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y); + // + // display minute hand + angle = minute() * 6 ; + angle = ( angle / 57.29577951 ) ; //Convert degrees to radians + x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 4 ) ) ) ); + y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 4 ) ) ) ); + display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y); + // + // display hour hand + angle = hour() * 30 + int( ( minute() / 12 ) * 6 ) ; + angle = ( angle / 57.29577951 ) ; //Convert degrees to radians + x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 2 ) ) ) ); + y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 2 ) ) ) ); + display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y); +} + +void digitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + String timenow = String(hour())+":"+twoDigits(minute())+":"+twoDigits(second()); + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(ArialMT_Plain_24); + display->drawString(clockCenterX + x , clockCenterY + y, timenow ); +} + +// This array keeps function pointers to all frames +// frames are the single views that slide in +FrameCallback frames[] = { analogClockFrame, digitalClockFrame }; + +// how many frames are there? +int frameCount = 2; + +// Overlays are statically drawn on top of a frame eg. a clock +OverlayCallback overlays[] = { clockOverlay }; +int overlaysCount = 1; + +void setup() { + Serial.begin(9600); + Serial.println(); + + // The ESP is capable of rendering 60fps in 80Mhz mode + // but that won't give you much time for anything else + // run it in 160Mhz mode or just set it to 30 fps + ui.setTargetFPS(60); + + // Customize the active and inactive symbol + ui.setActiveSymbol(activeSymbol); + ui.setInactiveSymbol(inactiveSymbol); + + // You can change this to + // TOP, LEFT, BOTTOM, RIGHT + ui.setIndicatorPosition(TOP); + + // Defines where the first frame is located in the bar. + ui.setIndicatorDirection(LEFT_RIGHT); + + // You can change the transition that is used + // SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN + ui.setFrameAnimation(SLIDE_LEFT); + + // Add frames + ui.setFrames(frames, frameCount); + + // Add overlays + ui.setOverlays(overlays, overlaysCount); + + // Initialising the UI will init the display too. + ui.init(); + + display.flipScreenVertically(); + + unsigned long secsSinceStart = millis(); + // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: + const unsigned long seventyYears = 2208988800UL; + // subtract seventy years: + unsigned long epoch = secsSinceStart - seventyYears * SECS_PER_HOUR; + setTime(epoch); + +} + + +void loop() { + int remainingTimeBudget = ui.update(); + + if (remainingTimeBudget > 0) { + // You can do some work here + // Don't do stuff if you are below your + // time budget. + delay(remainingTimeBudget); + + } + + +} diff --git a/lib/esp8266-oled-ssd1306-master/examples/SSD1306ClockDemo/images.h b/lib/esp8266-oled-ssd1306-master/examples/SSD1306ClockDemo/images.h new file mode 100644 index 00000000..1889188f --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/examples/SSD1306ClockDemo/images.h @@ -0,0 +1,21 @@ +const unsigned char activeSymbol[] PROGMEM = { + B00000000, + B00000000, + B00011000, + B00100100, + B01000010, + B01000010, + B00100100, + B00011000 +}; + +const unsigned char inactiveSymbol[] PROGMEM = { + B00000000, + B00000000, + B00000000, + B00000000, + B00011000, + B00011000, + B00000000, + B00000000 +}; diff --git a/lib/esp8266-oled-ssd1306-master/examples/SSD1306DrawingDemo/SSD1306DrawingDemo.ino b/lib/esp8266-oled-ssd1306-master/examples/SSD1306DrawingDemo/SSD1306DrawingDemo.ino new file mode 100644 index 00000000..43bd9747 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/examples/SSD1306DrawingDemo/SSD1306DrawingDemo.ino @@ -0,0 +1,233 @@ + /** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * 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. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + + // Include the correct display library + // For a connection via I2C using Wire include + #include // Only needed for Arduino 1.6.5 and earlier +#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"` + // or #include "SH1106Wire.h", legacy include: `#include "SH1106.h"` + // For a connection via I2C using brzo_i2c (must be installed) include + // #include // Only needed for Arduino 1.6.5 and earlier + // #include "SSD1306Brzo.h" + // #include "SH1106Brzo.h" + // For a connection via SPI include + // #include // Only needed for Arduino 1.6.5 and earlier + // #include "SSD1306Spi.h" + // #include "SH1106SPi.h" + + // Use the corresponding display class: + + // Initialize the OLED display using SPI + // D5 -> CLK + // D7 -> MOSI (DOUT) + // D0 -> RES + // D2 -> DC + // D8 -> CS + // SSD1306Spi display(D0, D2, D8); + // or + // SH1106Spi display(D0, D2); + + // Initialize the OLED display using brzo_i2c + // D3 -> SDA + // D5 -> SCL + // SSD1306Brzo display(0x3c, D3, D5); + // or + // SH1106Brzo display(0x3c, D3, D5); + + // Initialize the OLED display using Wire library + SSD1306Wire display(0x3c, D3, D5); + // SH1106 display(0x3c, D3, D5); + +// Adapted from Adafruit_SSD1306 +void drawLines() { + for (int16_t i=0; i=0; i-=4) { + display.drawLine(0, display.getHeight()-1, display.getWidth()-1, i); + display.display(); + delay(10); + } + delay(250); + + display.clear(); + for (int16_t i=display.getWidth()-1; i>=0; i-=4) { + display.drawLine(display.getWidth()-1, display.getHeight()-1, i, 0); + display.display(); + delay(10); + } + for (int16_t i=display.getHeight()-1; i>=0; i-=4) { + display.drawLine(display.getWidth()-1, display.getHeight()-1, 0, i); + display.display(); + delay(10); + } + delay(250); + display.clear(); + for (int16_t i=0; i + + // OTA Includes + #include + #include + + const char *ssid = "[Your SSID]"; + const char *password = "[Your Password]"; + + +// Include the correct display library +// For a connection via I2C using Wire include +#include // Only needed for Arduino 1.6.5 and earlier +#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"` +// or #include "SH1106Wire.h", legacy include: `#include "SH1106.h"` +// For a connection via I2C using brzo_i2c (must be installed) include +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Brzo.h" +// #include "SH1106Brzo.h" +// For a connection via SPI include +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Spi.h" +// #include "SH1106SPi.h" + +// Use the corresponding display class: + +// Initialize the OLED display using SPI +// D5 -> CLK +// D7 -> MOSI (DOUT) +// D0 -> RES +// D2 -> DC +// D8 -> CS +// SSD1306Spi display(D0, D2, D8); +// or +// SH1106Spi display(D0, D2); + +// Initialize the OLED display using brzo_i2c +// D3 -> SDA +// D5 -> SCL +// SSD1306Brzo display(0x3c, D3, D5); +// or +// SH1106Brzo display(0x3c, D3, D5); + +// Initialize the OLED display using Wire library +SSD1306Wire display(0x3c, D3, D5); +// SH1106 display(0x3c, D3, D5); + + +void setup() { + WiFi.begin ( ssid, password ); + + // Wait for connection + while ( WiFi.status() != WL_CONNECTED ) { + delay ( 10 ); + } + + display.init(); + display.flipScreenVertically(); + display.setContrast(255); + + ArduinoOTA.begin(); + ArduinoOTA.onStart([]() { + display.clear(); + display.setFont(ArialMT_Plain_10); + display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH); + display.drawString(display.getWidth()/2, display.getHeight()/2 - 10, "OTA Update"); + display.display(); + }); + + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { + display.drawProgressBar(4, 32, 120, 8, progress / (total / 100) ); + display.display(); + }); + + ArduinoOTA.onEnd([]() { + display.clear(); + display.setFont(ArialMT_Plain_10); + display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH); + display.drawString(display.getWidth()/2, display.getHeight()/2, "Restart"); + display.display(); + }); + + // Align text vertical/horizontal center + display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH); + display.setFont(ArialMT_Plain_10); + display.drawString(display.getWidth()/2, display.getHeight()/2, "Ready for OTA:\n" + WiFi.localIP().toString()); + display.display(); +} + +void loop() { + ArduinoOTA.handle(); +} diff --git a/lib/esp8266-oled-ssd1306-master/examples/SSD1306SimpleDemo/SSD1306SimpleDemo.ino b/lib/esp8266-oled-ssd1306-master/examples/SSD1306SimpleDemo/SSD1306SimpleDemo.ino new file mode 100644 index 00000000..8d715e44 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/examples/SSD1306SimpleDemo/SSD1306SimpleDemo.ino @@ -0,0 +1,197 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * 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. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +// Include the correct display library + +// For a connection via I2C using the Arduino Wire include: +#include // Only needed for Arduino 1.6.5 and earlier +#include "SSD1306Wire.h" // legacy: #include "SSD1306.h" +// OR #include "SH1106Wire.h" // legacy: #include "SH1106.h" + +// For a connection via I2C using brzo_i2c (must be installed) include: +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Brzo.h" +// OR #include "SH1106Brzo.h" + +// For a connection via SPI include: +// #include // Only needed for Arduino 1.6.5 and earlier +// #include "SSD1306Spi.h" +// OR #include "SH1106SPi.h" + + +// Optionally include custom images +#include "images.h" + + +// Initialize the OLED display using Arduino Wire: +SSD1306Wire display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL - SDA and SCL usually populate automatically based on your board's pins_arduino.h +// SSD1306Wire display(0x3c, D3, D5); // ADDRESS, SDA, SCL - If not, they can be specified manually. +// SSD1306Wire display(0x3c, SDA, SCL, GEOMETRY_128_32); // ADDRESS, SDA, SCL, OLEDDISPLAY_GEOMETRY - Extra param required for 128x32 displays. +// SH1106 display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL + +// Initialize the OLED display using brzo_i2c: +// SSD1306Brzo display(0x3c, D3, D5); // ADDRESS, SDA, SCL +// or +// SH1106Brzo display(0x3c, D3, D5); // ADDRESS, SDA, SCL + +// Initialize the OLED display using SPI: +// D5 -> CLK +// D7 -> MOSI (DOUT) +// D0 -> RES +// D2 -> DC +// D8 -> CS +// SSD1306Spi display(D0, D2, D8); // RES, DC, CS +// or +// SH1106Spi display(D0, D2); // RES, DC + + +#define DEMO_DURATION 3000 +typedef void (*Demo)(void); + +int demoMode = 0; +int counter = 1; + +void setup() { + Serial.begin(115200); + Serial.println(); + Serial.println(); + + + // Initialising the UI will init the display too. + display.init(); + + display.flipScreenVertically(); + display.setFont(ArialMT_Plain_10); + +} + +void drawFontFaceDemo() { + // Font Demo1 + // create more fonts at http://oleddisplay.squix.ch/ + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.setFont(ArialMT_Plain_10); + display.drawString(0, 0, "Hello world"); + display.setFont(ArialMT_Plain_16); + display.drawString(0, 10, "Hello world"); + display.setFont(ArialMT_Plain_24); + display.drawString(0, 26, "Hello world"); +} + +void drawTextFlowDemo() { + display.setFont(ArialMT_Plain_10); + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.drawStringMaxWidth(0, 0, 128, + "Lorem ipsum\n dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore." ); +} + +void drawTextAlignmentDemo() { + // Text alignment demo + display.setFont(ArialMT_Plain_10); + + // The coordinates define the left starting point of the text + display.setTextAlignment(TEXT_ALIGN_LEFT); + display.drawString(0, 10, "Left aligned (0,10)"); + + // The coordinates define the center of the text + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.drawString(64, 22, "Center aligned (64,22)"); + + // The coordinates define the right end of the text + display.setTextAlignment(TEXT_ALIGN_RIGHT); + display.drawString(128, 33, "Right aligned (128,33)"); +} + +void drawRectDemo() { + // Draw a pixel at given position + for (int i = 0; i < 10; i++) { + display.setPixel(i, i); + display.setPixel(10 - i, i); + } + display.drawRect(12, 12, 20, 20); + + // Fill the rectangle + display.fillRect(14, 14, 17, 17); + + // Draw a line horizontally + display.drawHorizontalLine(0, 40, 20); + + // Draw a line horizontally + display.drawVerticalLine(40, 0, 20); +} + +void drawCircleDemo() { + for (int i=1; i < 8; i++) { + display.setColor(WHITE); + display.drawCircle(32, 32, i*3); + if (i % 2 == 0) { + display.setColor(BLACK); + } + display.fillCircle(96, 32, 32 - i* 3); + } +} + +void drawProgressBarDemo() { + int progress = (counter / 5) % 100; + // draw the progress bar + display.drawProgressBar(0, 32, 120, 10, progress); + + // draw the percentage as String + display.setTextAlignment(TEXT_ALIGN_CENTER); + display.drawString(64, 15, String(progress) + "%"); +} + +void drawImageDemo() { + // see http://blog.squix.org/2015/05/esp8266-nodemcu-how-to-create-xbm.html + // on how to create xbm files + display.drawXbm(34, 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits); +} + +Demo demos[] = {drawFontFaceDemo, drawTextFlowDemo, drawTextAlignmentDemo, drawRectDemo, drawCircleDemo, drawProgressBarDemo, drawImageDemo}; +int demoLength = (sizeof(demos) / sizeof(Demo)); +long timeSinceLastModeSwitch = 0; + +void loop() { + // clear the display + display.clear(); + // draw the current demo method + demos[demoMode](); + + display.setTextAlignment(TEXT_ALIGN_RIGHT); + display.drawString(10, 128, String(millis())); + // write the buffer to the display + display.display(); + + if (millis() - timeSinceLastModeSwitch > DEMO_DURATION) { + demoMode = (demoMode + 1) % demoLength; + timeSinceLastModeSwitch = millis(); + } + counter++; + delay(10); +} diff --git a/lib/esp8266-oled-ssd1306-master/examples/SSD1306SimpleDemo/images.h b/lib/esp8266-oled-ssd1306-master/examples/SSD1306SimpleDemo/images.h new file mode 100644 index 00000000..50417990 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/examples/SSD1306SimpleDemo/images.h @@ -0,0 +1,28 @@ +#define WiFi_Logo_width 60 +#define WiFi_Logo_height 36 +const uint8_t WiFi_Logo_bits[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF, + 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, + 0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C, + 0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00, + 0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, + 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00, + 0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F, + 0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF, + 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; diff --git a/lib/esp8266-oled-ssd1306-master/examples/SSD1306TwoScreenDemo/SSD1306TwoScreenDemo.ino b/lib/esp8266-oled-ssd1306-master/examples/SSD1306TwoScreenDemo/SSD1306TwoScreenDemo.ino new file mode 100644 index 00000000..b9595d8d --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/examples/SSD1306TwoScreenDemo/SSD1306TwoScreenDemo.ino @@ -0,0 +1,75 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * + * 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. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +// Include the correct display library +// For a connection via I2C using Wire include +#include // Only needed for Arduino 1.6.5 and earlier +#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"` +#include "images.h" + +// Initialize the OLED display using Wire library +SSD1306Wire display(0x3c, D3, D5); +SSD1306Wire display2(0x3c, D1, D2); + +void setup() { + Serial.begin(115200); + Serial.println(); + Serial.println(); + + + // Initialising the UI will init the display too. + display.init(); + display2.init(); + + // This will make sure that multiple instances of a display driver + // running on different ports will work together transparently + display.setI2cAutoInit(true); + display2.setI2cAutoInit(true); + + display.flipScreenVertically(); + display.setFont(ArialMT_Plain_10); + display.setTextAlignment(TEXT_ALIGN_LEFT); + + display2.flipScreenVertically(); + display2.setFont(ArialMT_Plain_10); + display2.setTextAlignment(TEXT_ALIGN_LEFT); + +} + +void loop() { + display.clear(); + display.drawString(0, 0, "Hello world: " + String(millis())); + display.display(); + + display2.clear(); + display2.drawString(0, 0, "Hello world: " + String(millis())); + display2.display(); + + delay(10); +} diff --git a/lib/esp8266-oled-ssd1306-master/examples/SSD1306TwoScreenDemo/images.h b/lib/esp8266-oled-ssd1306-master/examples/SSD1306TwoScreenDemo/images.h new file mode 100644 index 00000000..50417990 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/examples/SSD1306TwoScreenDemo/images.h @@ -0,0 +1,28 @@ +#define WiFi_Logo_width 60 +#define WiFi_Logo_height 36 +const uint8_t WiFi_Logo_bits[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF, + 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, + 0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C, + 0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00, + 0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, + 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00, + 0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F, + 0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF, + 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; diff --git a/lib/esp8266-oled-ssd1306-master/examples/SSD1306UiDemo/SSD1306UiDemo.ino b/lib/esp8266-oled-ssd1306-master/examples/SSD1306UiDemo/SSD1306UiDemo.ino new file mode 100644 index 00000000..72c836bf --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/examples/SSD1306UiDemo/SSD1306UiDemo.ino @@ -0,0 +1,194 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * 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. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + + // Include the correct display library + // For a connection via I2C using Wire include + #include // Only needed for Arduino 1.6.5 and earlier + #include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"` + // or #include "SH1106Wire.h", legacy include: `#include "SH1106.h"` + // For a connection via I2C using brzo_i2c (must be installed) include + // #include // Only needed for Arduino 1.6.5 and earlier + // #include "SSD1306Brzo.h" + // #include "SH1106Brzo.h" + // For a connection via SPI include + // #include // Only needed for Arduino 1.6.5 and earlier + // #include "SSD1306Spi.h" + // #include "SH1106SPi.h" + +// Include the UI lib +#include "OLEDDisplayUi.h" + +// Include custom images + +#include "images.h" + +// Use the corresponding display class: + +// Initialize the OLED display using SPI +// D5 -> CLK +// D7 -> MOSI (DOUT) +// D0 -> RES +// D2 -> DC +// D8 -> CS +// SSD1306Spi display(D0, D2, D8); +// or +// SH1106Spi display(D0, D2); + +// Initialize the OLED display using brzo_i2c +// D3 -> SDA +// D5 -> SCL +// SSD1306Brzo display(0x3c, D3, D5); +// or +// SH1106Brzo display(0x3c, D3, D5); + +// Initialize the OLED display using Wire library +SSD1306Wire display(0x3c, D3, D5); +// SH1106Wire display(0x3c, D3, D5); + +OLEDDisplayUi ui ( &display ); + +void msOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) { + display->setTextAlignment(TEXT_ALIGN_RIGHT); + display->setFont(ArialMT_Plain_10); + display->drawString(128, 0, String(millis())); +} + +void drawFrame1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + // draw an xbm image. + // Please note that everything that should be transitioned + // needs to be drawn relative to x and y + + display->drawXbm(x + 34, y + 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits); +} + +void drawFrame2(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + // Demonstrates the 3 included default sizes. The fonts come from SSD1306Fonts.h file + // Besides the default fonts there will be a program to convert TrueType fonts into this format + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(ArialMT_Plain_10); + display->drawString(0 + x, 10 + y, "Arial 10"); + + display->setFont(ArialMT_Plain_16); + display->drawString(0 + x, 20 + y, "Arial 16"); + + display->setFont(ArialMT_Plain_24); + display->drawString(0 + x, 34 + y, "Arial 24"); +} + +void drawFrame3(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + // Text alignment demo + display->setFont(ArialMT_Plain_10); + + // The coordinates define the left starting point of the text + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->drawString(0 + x, 11 + y, "Left aligned (0,10)"); + + // The coordinates define the center of the text + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->drawString(64 + x, 22 + y, "Center aligned (64,22)"); + + // The coordinates define the right end of the text + display->setTextAlignment(TEXT_ALIGN_RIGHT); + display->drawString(128 + x, 33 + y, "Right aligned (128,33)"); +} + +void drawFrame4(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + // Demo for drawStringMaxWidth: + // with the third parameter you can define the width after which words will be wrapped. + // Currently only spaces and "-" are allowed for wrapping + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(ArialMT_Plain_10); + display->drawStringMaxWidth(0 + x, 10 + y, 128, "Lorem ipsum\n dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore."); +} + +void drawFrame5(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { + +} + +// This array keeps function pointers to all frames +// frames are the single views that slide in +FrameCallback frames[] = { drawFrame1, drawFrame2, drawFrame3, drawFrame4, drawFrame5 }; + +// how many frames are there? +int frameCount = 5; + +// Overlays are statically drawn on top of a frame eg. a clock +OverlayCallback overlays[] = { msOverlay }; +int overlaysCount = 1; + +void setup() { + Serial.begin(115200); + Serial.println(); + Serial.println(); + + // The ESP is capable of rendering 60fps in 80Mhz mode + // but that won't give you much time for anything else + // run it in 160Mhz mode or just set it to 30 fps + ui.setTargetFPS(60); + + // Customize the active and inactive symbol + ui.setActiveSymbol(activeSymbol); + ui.setInactiveSymbol(inactiveSymbol); + + // You can change this to + // TOP, LEFT, BOTTOM, RIGHT + ui.setIndicatorPosition(BOTTOM); + + // Defines where the first frame is located in the bar. + ui.setIndicatorDirection(LEFT_RIGHT); + + // You can change the transition that is used + // SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN + ui.setFrameAnimation(SLIDE_LEFT); + + // Add frames + ui.setFrames(frames, frameCount); + + // Add overlays + ui.setOverlays(overlays, overlaysCount); + + // Initialising the UI will init the display too. + ui.init(); + + display.flipScreenVertically(); + +} + + +void loop() { + int remainingTimeBudget = ui.update(); + + if (remainingTimeBudget > 0) { + // You can do some work here + // Don't do stuff if you are below your + // time budget. + delay(remainingTimeBudget); + } +} diff --git a/lib/esp8266-oled-ssd1306-master/examples/SSD1306UiDemo/images.h b/lib/esp8266-oled-ssd1306-master/examples/SSD1306UiDemo/images.h new file mode 100644 index 00000000..2489d1e5 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/examples/SSD1306UiDemo/images.h @@ -0,0 +1,50 @@ +#define WiFi_Logo_width 60 +#define WiFi_Logo_height 36 +const uint8_t WiFi_Logo_bits[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF, + 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, + 0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C, + 0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00, + 0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, + 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, + 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C, + 0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00, + 0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F, + 0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00, + 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF, + 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + +const uint8_t activeSymbol[] PROGMEM = { + B00000000, + B00000000, + B00011000, + B00100100, + B01000010, + B01000010, + B00100100, + B00011000 +}; + +const uint8_t inactiveSymbol[] PROGMEM = { + B00000000, + B00000000, + B00000000, + B00000000, + B00011000, + B00011000, + B00000000, + B00000000 +}; diff --git a/lib/esp8266-oled-ssd1306-master/keywords.txt b/lib/esp8266-oled-ssd1306-master/keywords.txt new file mode 100644 index 00000000..db59d6e8 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/keywords.txt @@ -0,0 +1,100 @@ +####################################### +# Syntax Coloring Map List +####################################### + + +####################################### +# Constants (LITERAL1) +####################################### +INVERSE LITERAL1 + +TEXT_ALIGN_LEFT LITERAL1 +TEXT_ALIGN_RIGHT LITERAL1 +TEXT_ALIGN_CENTER LITERAL1 +TEXT_ALIGN_CENTER_BOTH LITERAL1 + +GEOMETRY_128_64 LITERAL1 +GEOMETRY_128_32 LITERAL1 +GEOMETRY_RAWMODE LITERAL1 + +ArialMT_Plain_10 LITERAL1 +ArialMT_Plain_16 LITERAL1 +ArialMT_Plain_24 LITERAL1 + +SLIDE_UP LITERAL1 +SLIDE_DOWN LITERAL1 +SLIDE_LEFT LITERAL1 +SLIDE_RIGHT LITERAL1 + +TOP LITERAL1 +RIGHT LITERAL1 +BOTTOM LITERAL1 +LEFT LITERAL1 + +LEFT_RIGHT LITERAL1 +RIGHT_LEFT LITERAL1 + +IN_TRANSITION LITERAL1 +FIXED LITERAL1 + + +####################################### +# Datatypes (KEYWORD1) +####################################### +OLEDDisplay KEYWORD1 +OLEDDisplayUi KEYWORD1 + +SH1106Wire KEYWORD1 +SH1106Brzo KEYWORD1 +SH1106Spi KEYWORD1 + +SSD1306Wire KEYWORD1 +SSD1306Brzo KEYWORD1 +SSD1306I2C KEYWORD1 +SSD1306Spi KEYWORD1 + + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +allocateBuffer KEYWORD2 +init KEYWORD2 +resetDisplay KEYWORD2 +setColor KEYWORD2 +getColor KEYWORD2 +setPixel KEYWORD2 +setPixelColor KEYWORD2 +clearPixel KEYWORD2 +drawLine KEYWORD2 +drawRect KEYWORD2 +fillRect KEYWORD2 +drawCircle KEYWORD2 +drawCircleQuads KEYWORD2 +fillCircle KEYWORD2 +fillRing KEYWORD2 +drawHorizontalLine KEYWORD2 +drawVerticalLine KEYWORD2 +drawProgressBar KEYWORD2 +drawFastImage KEYWORD2 +drawXbm KEYWORD2 +drawIco16x16 KEYWORD2 +drawString KEYWORD2 +drawStringMaxWidth KEYWORD2 +getStringWidth KEYWORD2 +setTextAlignment KEYWORD2 +setFont KEYWORD2 +setFontTableLookupFunction KEYWORD2 +displayOn KEYWORD2 +displayOff KEYWORD2 +invertDisplay KEYWORD2 +normalDisplay KEYWORD2 +setContrast KEYWORD2 +setBrightness KEYWORD2 +resetOrientation KEYWORD2 +flipScreenVertically KEYWORD2 +mirrorScreen KEYWORD2 +display KEYWORD2 +setLogBuffer KEYWORD2 +drawLogBuffer KEYWORD2 +getWidth KEYWORD2 +getHeight KEYWORD2 diff --git a/lib/esp8266-oled-ssd1306-master/library.json b/lib/esp8266-oled-ssd1306-master/library.json new file mode 100644 index 00000000..acd11ce9 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/library.json @@ -0,0 +1,30 @@ +{ + "name": "ESP8266 and ESP32 OLED driver for SSD1306 displays", + "version": "4.2.0", + "keywords": "ssd1306, oled, display, i2c", + "description": "I2C display driver for SSD1306 OLED displays connected to ESP8266, ESP32, Mbed-OS", + "license": "MIT", + "repository": + { + "type": "git", + "url": "https://github.com/ThingPulse/esp8266-oled-ssd1306" + }, + "authors": + [ + { + "name": "Daniel Eichhorn, ThingPulse", + "email": "squix78@gmail.com", + "url": "https://thingpulse.com" + }, + { + "name": "Fabrice Weinberg", + "email": "fabrice@weinberg.me" + } + ], + "frameworks": "arduino", + "platforms": [ + "espressif8266", + "espressif32", + "nordicnrf52" + ] +} diff --git a/lib/esp8266-oled-ssd1306-master/library.properties b/lib/esp8266-oled-ssd1306-master/library.properties new file mode 100644 index 00000000..e931a3f6 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/library.properties @@ -0,0 +1,10 @@ +name=ESP8266 and ESP32 OLED driver for SSD1306 displays +version=4.2.0 +author=ThingPulse, Fabrice Weinberg +maintainer=ThingPulse +sentence=I2C display driver for SSD1306 OLED displays connected to ESP8266, ESP32, Mbed-OS +paragraph=The following geometries are currently supported: 128x64, 128x32, 64x48. The init sequence was inspired by Adafruit's library for the same display. +category=Display +url=https://github.com/ThingPulse/esp8266-oled-ssd1306 +architectures=esp8266,esp32 +license=MIT diff --git a/lib/esp8266-oled-ssd1306-master/license b/lib/esp8266-oled-ssd1306-master/license new file mode 100644 index 00000000..706c10fe --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/license @@ -0,0 +1,24 @@ +The MIT License (MIT) + +Copyright (c) 2016 by Daniel Eichhorn +Copyright (c) 2016 by Fabrice Weinberg + +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. + +See more at http://blog.squix.ch diff --git a/lib/esp8266-oled-ssd1306-master/platformio.ini b/lib/esp8266-oled-ssd1306-master/platformio.ini new file mode 100644 index 00000000..530c046e --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/platformio.ini @@ -0,0 +1,19 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; http://docs.platformio.org/page/projectconf.html + +[env:d1_mini] +platform = espressif8266 +board = d1_mini +framework = arduino +upload_speed = 921600 +board_build.f_cpu = 160000000L +upload_port = /dev/cu.SLAB_USBtoUART +monitor_port = /dev/cu.SLAB_USBtoUART +lib_deps = diff --git a/lib/esp8266-oled-ssd1306-master/src/OLEDDisplay.cpp b/lib/esp8266-oled-ssd1306-master/src/OLEDDisplay.cpp new file mode 100644 index 00000000..89a80108 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/src/OLEDDisplay.cpp @@ -0,0 +1,1045 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de + * + * 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. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + + /* + * TODO Helmut + * - test/finish dislplay.printf() on mbed-os + * - Finish _putc with drawLogBuffer when running display + */ + +#include "OLEDDisplay.h" + +OLEDDisplay::OLEDDisplay() { + + displayWidth = 128; + displayHeight = 64; + displayBufferSize = displayWidth * displayHeight / 8; + color = WHITE; + geometry = GEOMETRY_128_64; + textAlignment = TEXT_ALIGN_LEFT; + fontData = ArialMT_Plain_10; + fontTableLookupFunction = DefaultFontTableLookup; + buffer = NULL; +#ifdef OLEDDISPLAY_DOUBLE_BUFFER + buffer_back = NULL; +#endif +} + +OLEDDisplay::~OLEDDisplay() { + end(); +} + +bool OLEDDisplay::allocateBuffer() { + + logBufferSize = 0; + logBufferFilled = 0; + logBufferLine = 0; + logBufferMaxLines = 0; + logBuffer = NULL; + + if (!connect()) { + DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Can't establish connection to display\n"); + return false; + } + + if(this->buffer==NULL) { + this->buffer = (uint8_t*) malloc((sizeof(uint8_t) * displayBufferSize) + BufferOffset); + this->buffer += BufferOffset; + + if(!this->buffer) { + DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Not enough memory to create display\n"); + return false; + } + } + + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + if(this->buffer_back==NULL) { + this->buffer_back = (uint8_t*) malloc((sizeof(uint8_t) * displayBufferSize) + BufferOffset); + this->buffer_back += BufferOffset; + + if(!this->buffer_back) { + DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Not enough memory to create back buffer\n"); + free(this->buffer - BufferOffset); + return false; + } + } + #endif + + return true; +} + +bool OLEDDisplay::init() { + + BufferOffset = getBufferOffset(); + + if(!allocateBuffer()) { + return false; + } + + sendInitCommands(); + resetDisplay(); + + return true; +} + +void OLEDDisplay::end() { + if (this->buffer) { free(this->buffer - BufferOffset); this->buffer = NULL; } + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + if (this->buffer_back) { free(this->buffer_back - BufferOffset); this->buffer_back = NULL; } + #endif + if (this->logBuffer != NULL) { free(this->logBuffer); this->logBuffer = NULL; } +} + +void OLEDDisplay::resetDisplay(void) { + clear(); + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + memset(buffer_back, 1, displayBufferSize); + #endif + display(); +} + +void OLEDDisplay::setColor(OLEDDISPLAY_COLOR color) { + this->color = color; +} + +OLEDDISPLAY_COLOR OLEDDisplay::getColor() { + return this->color; +} + +void OLEDDisplay::setPixel(int16_t x, int16_t y) { + if (x >= 0 && x < this->width() && y >= 0 && y < this->height()) { + switch (color) { + case WHITE: buffer[x + (y / 8) * this->width()] |= (1 << (y & 7)); break; + case BLACK: buffer[x + (y / 8) * this->width()] &= ~(1 << (y & 7)); break; + case INVERSE: buffer[x + (y / 8) * this->width()] ^= (1 << (y & 7)); break; + } + } +} + +void OLEDDisplay::setPixelColor(int16_t x, int16_t y, OLEDDISPLAY_COLOR color) { + if (x >= 0 && x < this->width() && y >= 0 && y < this->height()) { + switch (color) { + case WHITE: buffer[x + (y / 8) * this->width()] |= (1 << (y & 7)); break; + case BLACK: buffer[x + (y / 8) * this->width()] &= ~(1 << (y & 7)); break; + case INVERSE: buffer[x + (y / 8) * this->width()] ^= (1 << (y & 7)); break; + } + } +} + +void OLEDDisplay::clearPixel(int16_t x, int16_t y) { + if (x >= 0 && x < this->width() && y >= 0 && y < this->height()) { + switch (color) { + case BLACK: buffer[x + (y >> 3) * this->width()] |= (1 << (y & 7)); break; + case WHITE: buffer[x + (y >> 3) * this->width()] &= ~(1 << (y & 7)); break; + case INVERSE: buffer[x + (y >> 3) * this->width()] ^= (1 << (y & 7)); break; + } + } +} + + +// Bresenham's algorithm - thx wikipedia and Adafruit_GFX +void OLEDDisplay::drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1) { + int16_t steep = abs(y1 - y0) > abs(x1 - x0); + if (steep) { + _swap_int16_t(x0, y0); + _swap_int16_t(x1, y1); + } + + if (x0 > x1) { + _swap_int16_t(x0, x1); + _swap_int16_t(y0, y1); + } + + int16_t dx, dy; + dx = x1 - x0; + dy = abs(y1 - y0); + + int16_t err = dx / 2; + int16_t ystep; + + if (y0 < y1) { + ystep = 1; + } else { + ystep = -1; + } + + for (; x0<=x1; x0++) { + if (steep) { + setPixel(y0, x0); + } else { + setPixel(x0, y0); + } + err -= dy; + if (err < 0) { + y0 += ystep; + err += dx; + } + } +} + +void OLEDDisplay::drawRect(int16_t x, int16_t y, int16_t width, int16_t height) { + drawHorizontalLine(x, y, width); + drawVerticalLine(x, y, height); + drawVerticalLine(x + width - 1, y, height); + drawHorizontalLine(x, y + height - 1, width); +} + +void OLEDDisplay::fillRect(int16_t xMove, int16_t yMove, int16_t width, int16_t height) { + for (int16_t x = xMove; x < xMove + width; x++) { + drawVerticalLine(x, yMove, height); + } +} + +void OLEDDisplay::drawCircle(int16_t x0, int16_t y0, int16_t radius) { + int16_t x = 0, y = radius; + int16_t dp = 1 - radius; + do { + if (dp < 0) + dp = dp + (x++) * 2 + 3; + else + dp = dp + (x++) * 2 - (y--) * 2 + 5; + + setPixel(x0 + x, y0 + y); //For the 8 octants + setPixel(x0 - x, y0 + y); + setPixel(x0 + x, y0 - y); + setPixel(x0 - x, y0 - y); + setPixel(x0 + y, y0 + x); + setPixel(x0 - y, y0 + x); + setPixel(x0 + y, y0 - x); + setPixel(x0 - y, y0 - x); + + } while (x < y); + + setPixel(x0 + radius, y0); + setPixel(x0, y0 + radius); + setPixel(x0 - radius, y0); + setPixel(x0, y0 - radius); +} + +void OLEDDisplay::drawCircleQuads(int16_t x0, int16_t y0, int16_t radius, uint8_t quads) { + int16_t x = 0, y = radius; + int16_t dp = 1 - radius; + while (x < y) { + if (dp < 0) + dp = dp + (x++) * 2 + 3; + else + dp = dp + (x++) * 2 - (y--) * 2 + 5; + if (quads & 0x1) { + setPixel(x0 + x, y0 - y); + setPixel(x0 + y, y0 - x); + } + if (quads & 0x2) { + setPixel(x0 - y, y0 - x); + setPixel(x0 - x, y0 - y); + } + if (quads & 0x4) { + setPixel(x0 - y, y0 + x); + setPixel(x0 - x, y0 + y); + } + if (quads & 0x8) { + setPixel(x0 + x, y0 + y); + setPixel(x0 + y, y0 + x); + } + } + if (quads & 0x1 && quads & 0x8) { + setPixel(x0 + radius, y0); + } + if (quads & 0x4 && quads & 0x8) { + setPixel(x0, y0 + radius); + } + if (quads & 0x2 && quads & 0x4) { + setPixel(x0 - radius, y0); + } + if (quads & 0x1 && quads & 0x2) { + setPixel(x0, y0 - radius); + } +} + + +void OLEDDisplay::fillCircle(int16_t x0, int16_t y0, int16_t radius) { + int16_t x = 0, y = radius; + int16_t dp = 1 - radius; + do { + if (dp < 0) + dp = dp + (x++) * 2 + 3; + else + dp = dp + (x++) * 2 - (y--) * 2 + 5; + + drawHorizontalLine(x0 - x, y0 - y, 2*x); + drawHorizontalLine(x0 - x, y0 + y, 2*x); + drawHorizontalLine(x0 - y, y0 - x, 2*y); + drawHorizontalLine(x0 - y, y0 + x, 2*y); + + + } while (x < y); + drawHorizontalLine(x0 - radius, y0, 2 * radius); + +} + +void OLEDDisplay::drawHorizontalLine(int16_t x, int16_t y, int16_t length) { + if (y < 0 || y >= this->height()) { return; } + + if (x < 0) { + length += x; + x = 0; + } + + if ( (x + length) > this->width()) { + length = (this->width() - x); + } + + if (length <= 0) { return; } + + uint8_t * bufferPtr = buffer; + bufferPtr += (y >> 3) * this->width(); + bufferPtr += x; + + uint8_t drawBit = 1 << (y & 7); + + switch (color) { + case WHITE: while (length--) { + *bufferPtr++ |= drawBit; + }; break; + case BLACK: drawBit = ~drawBit; while (length--) { + *bufferPtr++ &= drawBit; + }; break; + case INVERSE: while (length--) { + *bufferPtr++ ^= drawBit; + }; break; + } +} + +void OLEDDisplay::drawVerticalLine(int16_t x, int16_t y, int16_t length) { + if (x < 0 || x >= this->width()) return; + + if (y < 0) { + length += y; + y = 0; + } + + if ( (y + length) > this->height()) { + length = (this->height() - y); + } + + if (length <= 0) return; + + + uint8_t yOffset = y & 7; + uint8_t drawBit; + uint8_t *bufferPtr = buffer; + + bufferPtr += (y >> 3) * this->width(); + bufferPtr += x; + + if (yOffset) { + yOffset = 8 - yOffset; + drawBit = ~(0xFF >> (yOffset)); + + if (length < yOffset) { + drawBit &= (0xFF >> (yOffset - length)); + } + + switch (color) { + case WHITE: *bufferPtr |= drawBit; break; + case BLACK: *bufferPtr &= ~drawBit; break; + case INVERSE: *bufferPtr ^= drawBit; break; + } + + if (length < yOffset) return; + + length -= yOffset; + bufferPtr += this->width(); + } + + if (length >= 8) { + switch (color) { + case WHITE: + case BLACK: + drawBit = (color == WHITE) ? 0xFF : 0x00; + do { + *bufferPtr = drawBit; + bufferPtr += this->width(); + length -= 8; + } while (length >= 8); + break; + case INVERSE: + do { + *bufferPtr = ~(*bufferPtr); + bufferPtr += this->width(); + length -= 8; + } while (length >= 8); + break; + } + } + + if (length > 0) { + drawBit = (1 << (length & 7)) - 1; + switch (color) { + case WHITE: *bufferPtr |= drawBit; break; + case BLACK: *bufferPtr &= ~drawBit; break; + case INVERSE: *bufferPtr ^= drawBit; break; + } + } +} + +void OLEDDisplay::drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress) { + uint16_t radius = height / 2; + uint16_t xRadius = x + radius; + uint16_t yRadius = y + radius; + uint16_t doubleRadius = 2 * radius; + uint16_t innerRadius = radius - 2; + + setColor(WHITE); + drawCircleQuads(xRadius, yRadius, radius, 0b00000110); + drawHorizontalLine(xRadius, y, width - doubleRadius + 1); + drawHorizontalLine(xRadius, y + height, width - doubleRadius + 1); + drawCircleQuads(x + width - radius, yRadius, radius, 0b00001001); + + uint16_t maxProgressWidth = (width - doubleRadius + 1) * progress / 100; + + fillCircle(xRadius, yRadius, innerRadius); + fillRect(xRadius + 1, y + 2, maxProgressWidth, height - 3); + fillCircle(xRadius + maxProgressWidth, yRadius, innerRadius); +} + +void OLEDDisplay::drawFastImage(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *image) { + drawInternal(xMove, yMove, width, height, image, 0, 0); +} + +void OLEDDisplay::drawXbm(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *xbm) { + int16_t widthInXbm = (width + 7) / 8; + uint8_t data = 0; + + for(int16_t y = 0; y < height; y++) { + for(int16_t x = 0; x < width; x++ ) { + if (x & 7) { + data >>= 1; // Move a bit + } else { // Read new data every 8 bit + data = pgm_read_byte(xbm + (x / 8) + y * widthInXbm); + } + // if there is a bit draw it + if (data & 0x01) { + setPixel(xMove + x, yMove + y); + } + } + } +} + +void OLEDDisplay::drawIco16x16(int16_t xMove, int16_t yMove, const char *ico, bool inverse) { + uint16_t data; + + for(int16_t y = 0; y < 16; y++) { + data = pgm_read_byte(ico + (y << 1)) + (pgm_read_byte(ico + (y << 1) + 1) << 8); + for(int16_t x = 0; x < 16; x++ ) { + if ((data & 0x01) ^ inverse) { + setPixelColor(xMove + x, yMove + y, WHITE); + } else { + setPixelColor(xMove + x, yMove + y, BLACK); + } + data >>= 1; // Move a bit + } + } +} + +void OLEDDisplay::drawStringInternal(int16_t xMove, int16_t yMove, char* text, uint16_t textLength, uint16_t textWidth) { + uint8_t textHeight = pgm_read_byte(fontData + HEIGHT_POS); + uint8_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS); + uint16_t sizeOfJumpTable = pgm_read_byte(fontData + CHAR_NUM_POS) * JUMPTABLE_BYTES; + + uint16_t cursorX = 0; + uint16_t cursorY = 0; + + switch (textAlignment) { + case TEXT_ALIGN_CENTER_BOTH: + yMove -= textHeight >> 1; + // Fallthrough + case TEXT_ALIGN_CENTER: + xMove -= textWidth >> 1; // divide by 2 + break; + case TEXT_ALIGN_RIGHT: + xMove -= textWidth; + break; + case TEXT_ALIGN_LEFT: + break; + } + + // Don't draw anything if it is not on the screen. + if (xMove + textWidth < 0 || xMove > this->width() ) {return;} + if (yMove + textHeight < 0 || yMove > this->width() ) {return;} + + for (uint16_t j = 0; j < textLength; j++) { + int16_t xPos = xMove + cursorX; + int16_t yPos = yMove + cursorY; + + uint8_t code = text[j]; + if (code >= firstChar) { + uint8_t charCode = code - firstChar; + + // 4 Bytes per char code + uint8_t msbJumpToChar = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES ); // MSB \ JumpAddress + uint8_t lsbJumpToChar = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_LSB); // LSB / + uint8_t charByteSize = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_SIZE); // Size + uint8_t currentCharWidth = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_WIDTH); // Width + + // Test if the char is drawable + if (!(msbJumpToChar == 255 && lsbJumpToChar == 255)) { + // Get the position of the char data + uint16_t charDataPosition = JUMPTABLE_START + sizeOfJumpTable + ((msbJumpToChar << 8) + lsbJumpToChar); + drawInternal(xPos, yPos, currentCharWidth, textHeight, fontData, charDataPosition, charByteSize); + } + + cursorX += currentCharWidth; + } + } +} + + +void OLEDDisplay::drawString(int16_t xMove, int16_t yMove, String strUser) { + uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS); + + // char* text must be freed! + char* text = utf8ascii(strUser); + + uint16_t yOffset = 0; + // If the string should be centered vertically too + // we need to now how heigh the string is. + if (textAlignment == TEXT_ALIGN_CENTER_BOTH) { + uint16_t lb = 0; + // Find number of linebreaks in text + for (uint16_t i=0;text[i] != 0; i++) { + lb += (text[i] == 10); + } + // Calculate center + yOffset = (lb * lineHeight) / 2; + } + + uint16_t line = 0; + char* textPart = strtok(text,"\n"); + while (textPart != NULL) { + uint16_t length = strlen(textPart); + drawStringInternal(xMove, yMove - yOffset + (line++) * lineHeight, textPart, length, getStringWidth(textPart, length)); + textPart = strtok(NULL, "\n"); + } + free(text); +} + +void OLEDDisplay::drawStringf( int16_t x, int16_t y, char* buffer, String format, ... ) +{ + va_list myargs; + va_start(myargs, format); + vsprintf(buffer, format.c_str(), myargs); + va_end(myargs); + drawString( x, y, buffer ); +} + +void OLEDDisplay::drawStringMaxWidth(int16_t xMove, int16_t yMove, uint16_t maxLineWidth, String strUser) { + uint16_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS); + uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS); + + char* text = utf8ascii(strUser); + + uint16_t length = strlen(text); + uint16_t lastDrawnPos = 0; + uint16_t lineNumber = 0; + uint16_t strWidth = 0; + + uint16_t preferredBreakpoint = 0; + uint16_t widthAtBreakpoint = 0; + + for (uint16_t i = 0; i < length; i++) { + strWidth += pgm_read_byte(fontData + JUMPTABLE_START + (text[i] - firstChar) * JUMPTABLE_BYTES + JUMPTABLE_WIDTH); + + // Always try to break on a space or dash + if (text[i] == ' ' || text[i]== '-') { + preferredBreakpoint = i; + widthAtBreakpoint = strWidth; + } + + if (strWidth >= maxLineWidth) { + if (preferredBreakpoint == 0) { + preferredBreakpoint = i; + widthAtBreakpoint = strWidth; + } + drawStringInternal(xMove, yMove + (lineNumber++) * lineHeight , &text[lastDrawnPos], preferredBreakpoint - lastDrawnPos, widthAtBreakpoint); + lastDrawnPos = preferredBreakpoint + 1; + // It is possible that we did not draw all letters to i so we need + // to account for the width of the chars from `i - preferredBreakpoint` + // by calculating the width we did not draw yet. + strWidth = strWidth - widthAtBreakpoint; + preferredBreakpoint = 0; + } + } + + // Draw last part if needed + if (lastDrawnPos < length) { + drawStringInternal(xMove, yMove + lineNumber * lineHeight , &text[lastDrawnPos], length - lastDrawnPos, getStringWidth(&text[lastDrawnPos], length - lastDrawnPos)); + } + + free(text); +} + +uint16_t OLEDDisplay::getStringWidth(const char* text, uint16_t length) { + uint16_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS); + + uint16_t stringWidth = 0; + uint16_t maxWidth = 0; + + while (length--) { + stringWidth += pgm_read_byte(fontData + JUMPTABLE_START + (text[length] - firstChar) * JUMPTABLE_BYTES + JUMPTABLE_WIDTH); + if (text[length] == 10) { + maxWidth = max(maxWidth, stringWidth); + stringWidth = 0; + } + } + + return max(maxWidth, stringWidth); +} + +uint16_t OLEDDisplay::getStringWidth(String strUser) { + char* text = utf8ascii(strUser); + uint16_t length = strlen(text); + uint16_t width = getStringWidth(text, length); + free(text); + return width; +} + +void OLEDDisplay::setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment) { + this->textAlignment = textAlignment; +} + +void OLEDDisplay::setFont(const uint8_t *fontData) { + this->fontData = fontData; +} + +void OLEDDisplay::displayOn(void) { + sendCommand(DISPLAYON); +} + +void OLEDDisplay::displayOff(void) { + sendCommand(DISPLAYOFF); +} + +void OLEDDisplay::invertDisplay(void) { + sendCommand(INVERTDISPLAY); +} + +void OLEDDisplay::normalDisplay(void) { + sendCommand(NORMALDISPLAY); +} + +void OLEDDisplay::setContrast(uint8_t contrast, uint8_t precharge, uint8_t comdetect) { + sendCommand(SETPRECHARGE); //0xD9 + sendCommand(precharge); //0xF1 default, to lower the contrast, put 1-1F + sendCommand(SETCONTRAST); + sendCommand(contrast); // 0-255 + sendCommand(SETVCOMDETECT); //0xDB, (additionally needed to lower the contrast) + sendCommand(comdetect); //0x40 default, to lower the contrast, put 0 + sendCommand(DISPLAYALLON_RESUME); + sendCommand(NORMALDISPLAY); + sendCommand(DISPLAYON); +} + +void OLEDDisplay::setBrightness(uint8_t brightness) { + uint8_t contrast = brightness; + if (brightness < 128) { + // Magic values to get a smooth/ step-free transition + contrast = brightness * 1.171; + } else { + contrast = brightness * 1.171 - 43; + } + + uint8_t precharge = 241; + if (brightness == 0) { + precharge = 0; + } + uint8_t comdetect = brightness / 8; + + setContrast(contrast, precharge, comdetect); +} + +void OLEDDisplay::resetOrientation() { + sendCommand(SEGREMAP); + sendCommand(COMSCANINC); //Reset screen rotation or mirroring +} + +void OLEDDisplay::flipScreenVertically() { + sendCommand(SEGREMAP | 0x01); + sendCommand(COMSCANDEC); //Rotate screen 180 Deg +} + +void OLEDDisplay::mirrorScreen() { + sendCommand(SEGREMAP); + sendCommand(COMSCANDEC); //Mirror screen +} + +void OLEDDisplay::clear(void) { + memset(buffer, 0, displayBufferSize); +} + +void OLEDDisplay::drawLogBuffer(uint16_t xMove, uint16_t yMove) { + uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS); + // Always align left + setTextAlignment(TEXT_ALIGN_LEFT); + + // State values + uint16_t length = 0; + uint16_t line = 0; + uint16_t lastPos = 0; + + for (uint16_t i=0;ilogBufferFilled;i++){ + // Everytime we have a \n print + if (this->logBuffer[i] == 10) { + length++; + // Draw string on line `line` from lastPos to length + // Passing 0 as the lenght because we are in TEXT_ALIGN_LEFT + drawStringInternal(xMove, yMove + (line++) * lineHeight, &this->logBuffer[lastPos], length, 0); + // Remember last pos + lastPos = i; + // Reset length + length = 0; + } else { + // Count chars until next linebreak + length++; + } + } + // Draw the remaining string + if (length > 0) { + drawStringInternal(xMove, yMove + line * lineHeight, &this->logBuffer[lastPos], length, 0); + } +} + +uint16_t OLEDDisplay::getWidth(void) { + return displayWidth; +} + +uint16_t OLEDDisplay::getHeight(void) { + return displayHeight; +} + +bool OLEDDisplay::setLogBuffer(uint16_t lines, uint16_t chars){ + if (logBuffer != NULL) free(logBuffer); + uint16_t size = lines * chars; + if (size > 0) { + this->logBufferLine = 0; // Lines printed + this->logBufferFilled = 0; // Nothing stored yet + this->logBufferMaxLines = lines; // Lines max printable + this->logBufferSize = size; // Total number of characters the buffer can hold + this->logBuffer = (char *) malloc(size * sizeof(uint8_t)); + if(!this->logBuffer) { + DEBUG_OLEDDISPLAY("[OLEDDISPLAY][setLogBuffer] Not enough memory to create log buffer\n"); + return false; + } + } + return true; +} + +size_t OLEDDisplay::write(uint8_t c) { + if (this->logBufferSize > 0) { + // Don't waste space on \r\n line endings, dropping \r + if (c == 13) return 1; + + // convert UTF-8 character to font table index + c = (this->fontTableLookupFunction)(c); + // drop unknown character + if (c == 0) return 1; + + bool maxLineNotReached = this->logBufferLine < this->logBufferMaxLines; + bool bufferNotFull = this->logBufferFilled < this->logBufferSize; + + // Can we write to the buffer? + if (bufferNotFull && maxLineNotReached) { + this->logBuffer[logBufferFilled] = c; + this->logBufferFilled++; + // Keep track of lines written + if (c == 10) this->logBufferLine++; + } else { + // Max line number is reached + if (!maxLineNotReached) this->logBufferLine--; + + // Find the end of the first line + uint16_t firstLineEnd = 0; + for (uint16_t i=0;ilogBufferFilled;i++) { + if (this->logBuffer[i] == 10){ + // Include last char too + firstLineEnd = i + 1; + break; + } + } + // If there was a line ending + if (firstLineEnd > 0) { + // Calculate the new logBufferFilled value + this->logBufferFilled = logBufferFilled - firstLineEnd; + // Now we move the lines infront of the buffer + memcpy(this->logBuffer, &this->logBuffer[firstLineEnd], logBufferFilled); + } else { + // Let's reuse the buffer if it was full + if (!bufferNotFull) { + this->logBufferFilled = 0; + }// else { + // Nothing to do here + //} + } + write(c); + } + } + // We are always writing all uint8_t to the buffer + return 1; +} + +size_t OLEDDisplay::write(const char* str) { + if (str == NULL) return 0; + size_t length = strlen(str); + for (size_t i = 0; i < length; i++) { + write(str[i]); + } + return length; +} + +#ifdef __MBED__ +int OLEDDisplay::_putc(int c) { + + if (!fontData) + return 1; + if (!logBufferSize) { + uint8_t textHeight = pgm_read_byte(fontData + HEIGHT_POS); + uint16_t lines = this->displayHeight / textHeight; + uint16_t chars = 2 * (this->displayWidth / textHeight); + + if (this->displayHeight % textHeight) + lines++; + if (this->displayWidth % textHeight) + chars++; + setLogBuffer(lines, chars); + } + + return this->write((uint8_t)c); +} +#endif + +// Private functions +void OLEDDisplay::setGeometry(OLEDDISPLAY_GEOMETRY g, uint16_t width, uint16_t height) { + this->geometry = g; + + switch (g) { + case GEOMETRY_128_64: + this->displayWidth = 128; + this->displayHeight = 64; + break; + case GEOMETRY_128_32: + this->displayWidth = 128; + this->displayHeight = 32; + break; + case GEOMETRY_64_48: + this->displayWidth = 64; + this->displayHeight = 48; + break; + case GEOMETRY_64_32: + this->displayWidth = 64; + this->displayHeight = 32; + break; + case GEOMETRY_RAWMODE: + this->displayWidth = width > 0 ? width : 128; + this->displayHeight = height > 0 ? height : 64; + break; + } + this->displayBufferSize = displayWidth * displayHeight / 8; +} + +void OLEDDisplay::sendInitCommands(void) { + if (geometry == GEOMETRY_RAWMODE) + return; + sendCommand(DISPLAYOFF); + sendCommand(SETDISPLAYCLOCKDIV); + sendCommand(0xF0); // Increase speed of the display max ~96Hz + sendCommand(SETMULTIPLEX); + sendCommand(this->height() - 1); + sendCommand(SETDISPLAYOFFSET); + sendCommand(0x00); + if(geometry == GEOMETRY_64_32) + sendCommand(0x00); + else + sendCommand(SETSTARTLINE); + sendCommand(CHARGEPUMP); + sendCommand(0x14); + sendCommand(MEMORYMODE); + sendCommand(0x00); + sendCommand(SEGREMAP); + sendCommand(COMSCANINC); + sendCommand(SETCOMPINS); + + if (geometry == GEOMETRY_128_64 || geometry == GEOMETRY_64_48 || geometry == GEOMETRY_64_32) { + sendCommand(0x12); + } else if (geometry == GEOMETRY_128_32) { + sendCommand(0x02); + } + + sendCommand(SETCONTRAST); + + if (geometry == GEOMETRY_128_64 || geometry == GEOMETRY_64_48 || geometry == GEOMETRY_64_32) { + sendCommand(0xCF); + } else if (geometry == GEOMETRY_128_32) { + sendCommand(0x8F); + } + + sendCommand(SETPRECHARGE); + sendCommand(0xF1); + sendCommand(SETVCOMDETECT); //0xDB, (additionally needed to lower the contrast) + sendCommand(0x40); //0x40 default, to lower the contrast, put 0 + sendCommand(DISPLAYALLON_RESUME); + sendCommand(NORMALDISPLAY); + sendCommand(0x2e); // stop scroll + sendCommand(DISPLAYON); +} + +void inline OLEDDisplay::drawInternal(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *data, uint16_t offset, uint16_t bytesInData) { + if (width < 0 || height < 0) return; + if (yMove + height < 0 || yMove > this->height()) return; + if (xMove + width < 0 || xMove > this->width()) return; + + uint8_t rasterHeight = 1 + ((height - 1) >> 3); // fast ceil(height / 8.0) + int8_t yOffset = yMove & 7; + + bytesInData = bytesInData == 0 ? width * rasterHeight : bytesInData; + + int16_t initYMove = yMove; + int8_t initYOffset = yOffset; + + + for (uint16_t i = 0; i < bytesInData; i++) { + + // Reset if next horizontal drawing phase is started. + if ( i % rasterHeight == 0) { + yMove = initYMove; + yOffset = initYOffset; + } + + uint8_t currentByte = pgm_read_byte(data + offset + i); + + int16_t xPos = xMove + (i / rasterHeight); + int16_t yPos = ((yMove >> 3) + (i % rasterHeight)) * this->width(); + +// int16_t yScreenPos = yMove + yOffset; + int16_t dataPos = xPos + yPos; + + if (dataPos >= 0 && dataPos < displayBufferSize && + xPos >= 0 && xPos < this->width() ) { + + if (yOffset >= 0) { + switch (this->color) { + case WHITE: buffer[dataPos] |= currentByte << yOffset; break; + case BLACK: buffer[dataPos] &= ~(currentByte << yOffset); break; + case INVERSE: buffer[dataPos] ^= currentByte << yOffset; break; + } + + if (dataPos < (displayBufferSize - this->width())) { + switch (this->color) { + case WHITE: buffer[dataPos + this->width()] |= currentByte >> (8 - yOffset); break; + case BLACK: buffer[dataPos + this->width()] &= ~(currentByte >> (8 - yOffset)); break; + case INVERSE: buffer[dataPos + this->width()] ^= currentByte >> (8 - yOffset); break; + } + } + } else { + // Make new offset position + yOffset = -yOffset; + + switch (this->color) { + case WHITE: buffer[dataPos] |= currentByte >> yOffset; break; + case BLACK: buffer[dataPos] &= ~(currentByte >> yOffset); break; + case INVERSE: buffer[dataPos] ^= currentByte >> yOffset; break; + } + + // Prepare for next iteration by moving one block up + yMove -= 8; + + // and setting the new yOffset + yOffset = 8 - yOffset; + } +#ifndef __MBED__ + yield(); +#endif + } + } +} + +// You need to free the char! +char* OLEDDisplay::utf8ascii(String str) { + uint16_t k = 0; + uint16_t length = str.length() + 1; + + // Copy the string into a char array + char* s = (char*) malloc(length * sizeof(char)); + if(!s) { + DEBUG_OLEDDISPLAY("[OLEDDISPLAY][utf8ascii] Can't allocate another char array. Drop support for UTF-8.\n"); + return (char*) str.c_str(); + } + str.toCharArray(s, length); + + length--; + + for (uint16_t i=0; i < length; i++) { + char c = (this->fontTableLookupFunction)(s[i]); + if (c!=0) { + s[k++]=c; + } + } + + s[k]=0; + + // This will leak 's' be sure to free it in the calling function. + return s; +} + +void OLEDDisplay::setFontTableLookupFunction(FontTableLookupFunction function) { + this->fontTableLookupFunction = function; +} + + +char DefaultFontTableLookup(const uint8_t ch) { + // UTF-8 to font table index converter + // Code form http://playground.arduino.cc/Main/Utf8ascii + static uint8_t LASTCHAR; + + if (ch < 128) { // Standard ASCII-set 0..0x7F handling + LASTCHAR = 0; + return ch; + } + + uint8_t last = LASTCHAR; // get last char + LASTCHAR = ch; + + switch (last) { // conversion depnding on first UTF8-character + case 0xC2: return (uint8_t) ch; + case 0xC3: return (uint8_t) (ch | 0xC0); + case 0x82: if (ch == 0xAC) return (uint8_t) 0x80; // special case Euro-symbol + } + + return (uint8_t) 0; // otherwise: return zero, if character has to be ignored +} diff --git a/lib/esp8266-oled-ssd1306-master/src/OLEDDisplay.h b/lib/esp8266-oled-ssd1306-master/src/OLEDDisplay.h new file mode 100644 index 00000000..d43ee162 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/src/OLEDDisplay.h @@ -0,0 +1,384 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de + * + * 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. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef OLEDDISPLAY_h +#define OLEDDISPLAY_h + +#ifdef ARDUINO +#include +#elif __MBED__ +#define pgm_read_byte(addr) (*(const unsigned char *)(addr)) + +#include +#define delay(x) wait_ms(x) +#define yield() void() + +/* + * This is a little Arduino String emulation to keep the OLEDDisplay + * library code in common between Arduino and mbed-os + */ +class String { +public: + String(const char *s) { _str = s; }; + int length() { return strlen(_str); }; + const char *c_str() { return _str; }; + void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const { + memcpy(buf, _str + index, std::min(bufsize, strlen(_str))); + }; +private: + const char *_str; +}; + +#else +#error "Unkown operating system" +#endif + +#include "OLEDDisplayFonts.h" + +//#define DEBUG_OLEDDISPLAY(...) Serial.printf( __VA_ARGS__ ) +//#define DEBUG_OLEDDISPLAY(...) dprintf("%s", __VA_ARGS__ ) + +#ifndef DEBUG_OLEDDISPLAY +#define DEBUG_OLEDDISPLAY(...) +#endif + +// Use DOUBLE BUFFERING by default +#ifndef OLEDDISPLAY_REDUCE_MEMORY +#define OLEDDISPLAY_DOUBLE_BUFFER +#endif + +// Header Values +#define JUMPTABLE_BYTES 4 + +#define JUMPTABLE_LSB 1 +#define JUMPTABLE_SIZE 2 +#define JUMPTABLE_WIDTH 3 +#define JUMPTABLE_START 4 + +#define WIDTH_POS 0 +#define HEIGHT_POS 1 +#define FIRST_CHAR_POS 2 +#define CHAR_NUM_POS 3 + + +// Display commands +#define CHARGEPUMP 0x8D +#define COLUMNADDR 0x21 +#define COMSCANDEC 0xC8 +#define COMSCANINC 0xC0 +#define DISPLAYALLON 0xA5 +#define DISPLAYALLON_RESUME 0xA4 +#define DISPLAYOFF 0xAE +#define DISPLAYON 0xAF +#define EXTERNALVCC 0x1 +#define INVERTDISPLAY 0xA7 +#define MEMORYMODE 0x20 +#define NORMALDISPLAY 0xA6 +#define PAGEADDR 0x22 +#define SEGREMAP 0xA0 +#define SETCOMPINS 0xDA +#define SETCONTRAST 0x81 +#define SETDISPLAYCLOCKDIV 0xD5 +#define SETDISPLAYOFFSET 0xD3 +#define SETHIGHCOLUMN 0x10 +#define SETLOWCOLUMN 0x00 +#define SETMULTIPLEX 0xA8 +#define SETPRECHARGE 0xD9 +#define SETSEGMENTREMAP 0xA1 +#define SETSTARTLINE 0x40 +#define SETVCOMDETECT 0xDB +#define SWITCHCAPVCC 0x2 + +#ifndef _swap_int16_t +#define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; } +#endif + +enum OLEDDISPLAY_COLOR { + BLACK = 0, + WHITE = 1, + INVERSE = 2 +}; + +enum OLEDDISPLAY_TEXT_ALIGNMENT { + TEXT_ALIGN_LEFT = 0, + TEXT_ALIGN_RIGHT = 1, + TEXT_ALIGN_CENTER = 2, + TEXT_ALIGN_CENTER_BOTH = 3 +}; + + +enum OLEDDISPLAY_GEOMETRY { + GEOMETRY_128_64 = 0, + GEOMETRY_128_32 = 1, + GEOMETRY_64_48 = 2, + GEOMETRY_64_32 = 3, + GEOMETRY_RAWMODE = 4 +}; + +enum HW_I2C { + I2C_ONE, + I2C_TWO +}; + +typedef char (*FontTableLookupFunction)(const uint8_t ch); +char DefaultFontTableLookup(const uint8_t ch); + + +#ifdef ARDUINO +class OLEDDisplay : public Print { +#elif __MBED__ +class OLEDDisplay : public Stream { +#else +#error "Unkown operating system" +#endif + + public: + OLEDDisplay(); + virtual ~OLEDDisplay(); + + uint16_t width(void) const { return displayWidth; }; + uint16_t height(void) const { return displayHeight; }; + + // Use this to resume after a deep sleep without resetting the display (what init() would do). + // Returns true if connection to the display was established and the buffer allocated, false otherwise. + bool allocateBuffer(); + + // Allocates the buffer and initializes the driver & display. Resets the display! + // Returns false if buffer allocation failed, true otherwise. + bool init(); + + // Free the memory used by the display + void end(); + + // Cycle through the initialization + void resetDisplay(void); + + /* Drawing functions */ + // Sets the color of all pixel operations + void setColor(OLEDDISPLAY_COLOR color); + + // Returns the current color. + OLEDDISPLAY_COLOR getColor(); + + // Draw a pixel at given position + void setPixel(int16_t x, int16_t y); + + // Draw a pixel at given position and color + void setPixelColor(int16_t x, int16_t y, OLEDDISPLAY_COLOR color); + + // Clear a pixel at given position FIXME: INVERSE is untested with this function + void clearPixel(int16_t x, int16_t y); + + // Draw a line from position 0 to position 1 + void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1); + + // Draw the border of a rectangle at the given location + void drawRect(int16_t x, int16_t y, int16_t width, int16_t height); + + // Fill the rectangle + void fillRect(int16_t x, int16_t y, int16_t width, int16_t height); + + // Draw the border of a circle + void drawCircle(int16_t x, int16_t y, int16_t radius); + + // Draw all Quadrants specified in the quads bit mask + void drawCircleQuads(int16_t x0, int16_t y0, int16_t radius, uint8_t quads); + + // Fill circle + void fillCircle(int16_t x, int16_t y, int16_t radius); + + // Draw a line horizontally + void drawHorizontalLine(int16_t x, int16_t y, int16_t length); + + // Draw a line vertically + void drawVerticalLine(int16_t x, int16_t y, int16_t length); + + // Draws a rounded progress bar with the outer dimensions given by width and height. Progress is + // a unsigned byte value between 0 and 100 + void drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress); + + // Draw a bitmap in the internal image format + void drawFastImage(int16_t x, int16_t y, int16_t width, int16_t height, const uint8_t *image); + + // Draw a XBM + void drawXbm(int16_t x, int16_t y, int16_t width, int16_t height, const uint8_t *xbm); + + // Draw icon 16x16 xbm format + void drawIco16x16(int16_t x, int16_t y, const char *ico, bool inverse = false); + + /* Text functions */ + + // Draws a string at the given location + void drawString(int16_t x, int16_t y, String text); + + // Draws a formatted string (like printf) at the given location + void drawStringf(int16_t x, int16_t y, char* buffer, String format, ... ); + + // Draws a String with a maximum width at the given location. + // If the given String is wider than the specified width + // The text will be wrapped to the next line at a space or dash + void drawStringMaxWidth(int16_t x, int16_t y, uint16_t maxLineWidth, String text); + + // Returns the width of the const char* with the current + // font settings + uint16_t getStringWidth(const char* text, uint16_t length); + + // Convencience method for the const char version + uint16_t getStringWidth(String text); + + // Specifies relative to which anchor point + // the text is rendered. Available constants: + // TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER_BOTH + void setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment); + + // Sets the current font. Available default fonts + // ArialMT_Plain_10, ArialMT_Plain_16, ArialMT_Plain_24 + void setFont(const uint8_t *fontData); + + // Set the function that will convert utf-8 to font table index + void setFontTableLookupFunction(FontTableLookupFunction function); + + /* Display functions */ + + // Turn the display on + void displayOn(void); + + // Turn the display offs + void displayOff(void); + + // Inverted display mode + void invertDisplay(void); + + // Normal display mode + void normalDisplay(void); + + // Set display contrast + // really low brightness & contrast: contrast = 10, precharge = 5, comdetect = 0 + // normal brightness & contrast: contrast = 100 + void setContrast(uint8_t contrast, uint8_t precharge = 241, uint8_t comdetect = 64); + + // Convenience method to access + void setBrightness(uint8_t); + + // Reset display rotation or mirroring + void resetOrientation(); + + // Turn the display upside down + void flipScreenVertically(); + + // Mirror the display (to be used in a mirror or as a projector) + void mirrorScreen(); + + // Write the buffer to the display memory + virtual void display(void) = 0; + + // Clear the local pixel buffer + void clear(void); + + // Log buffer implementation + + // This will define the lines and characters you can + // print to the screen. When you exeed the buffer size (lines * chars) + // the output may be truncated due to the size constraint. + bool setLogBuffer(uint16_t lines, uint16_t chars); + + // Draw the log buffer at position (x, y) + void drawLogBuffer(uint16_t x, uint16_t y); + + // Get screen geometry + uint16_t getWidth(void); + uint16_t getHeight(void); + + // Implement needed function to be compatible with Print class + size_t write(uint8_t c); + size_t write(const char* s); + + // Implement needed function to be compatible with Stream class +#ifdef __MBED__ + int _putc(int c); + int _getc() { return -1; }; +#endif + + + uint8_t *buffer; + + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t *buffer_back; + #endif + + protected: + + OLEDDISPLAY_GEOMETRY geometry; + + uint16_t displayWidth; + uint16_t displayHeight; + uint16_t displayBufferSize; + + // Set the correct height, width and buffer for the geometry + void setGeometry(OLEDDISPLAY_GEOMETRY g, uint16_t width = 0, uint16_t height = 0); + + OLEDDISPLAY_TEXT_ALIGNMENT textAlignment; + OLEDDISPLAY_COLOR color; + + const uint8_t *fontData; + + // State values for logBuffer + uint16_t logBufferSize; + uint16_t logBufferFilled; + uint16_t logBufferLine; + uint16_t logBufferMaxLines; + char *logBuffer; + + + // the header size of the buffer used, e.g. for the SPI command header + int BufferOffset; + virtual int getBufferOffset(void) = 0; + + // Send a command to the display (low level function) + virtual void sendCommand(uint8_t com) {(void)com;}; + + // Connect to the display + virtual bool connect() { return false; }; + + // Send all the init commands + void sendInitCommands(); + + // converts utf8 characters to extended ascii + char* utf8ascii(String s); + + void inline drawInternal(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *data, uint16_t offset, uint16_t bytesInData) __attribute__((always_inline)); + + void drawStringInternal(int16_t xMove, int16_t yMove, char* text, uint16_t textLength, uint16_t textWidth); + + FontTableLookupFunction fontTableLookupFunction; +}; + +#endif diff --git a/lib/esp8266-oled-ssd1306-master/src/OLEDDisplayFonts.h b/lib/esp8266-oled-ssd1306-master/src/OLEDDisplayFonts.h new file mode 100644 index 00000000..abc61ba1 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/src/OLEDDisplayFonts.h @@ -0,0 +1,1278 @@ +#ifndef OLEDDISPLAYFONTS_h +#define OLEDDISPLAYFONTS_h + +#ifdef __MBED__ +#define PROGMEM +#endif + +const uint8_t ArialMT_Plain_10[] PROGMEM = { + 0x0A, // Width: 10 + 0x0D, // Height: 13 + 0x20, // First Char: 32 + 0xE0, // Numbers of Chars: 224 + + // Jump Table: + 0xFF, 0xFF, 0x00, 0x03, // 32:65535 + 0x00, 0x00, 0x04, 0x03, // 33:0 + 0x00, 0x04, 0x05, 0x04, // 34:4 + 0x00, 0x09, 0x09, 0x06, // 35:9 + 0x00, 0x12, 0x0A, 0x06, // 36:18 + 0x00, 0x1C, 0x10, 0x09, // 37:28 + 0x00, 0x2C, 0x0E, 0x07, // 38:44 + 0x00, 0x3A, 0x01, 0x02, // 39:58 + 0x00, 0x3B, 0x06, 0x03, // 40:59 + 0x00, 0x41, 0x06, 0x03, // 41:65 + 0x00, 0x47, 0x05, 0x04, // 42:71 + 0x00, 0x4C, 0x09, 0x06, // 43:76 + 0x00, 0x55, 0x04, 0x03, // 44:85 + 0x00, 0x59, 0x03, 0x03, // 45:89 + 0x00, 0x5C, 0x04, 0x03, // 46:92 + 0x00, 0x60, 0x05, 0x03, // 47:96 + 0x00, 0x65, 0x0A, 0x06, // 48:101 + 0x00, 0x6F, 0x08, 0x06, // 49:111 + 0x00, 0x77, 0x0A, 0x06, // 50:119 + 0x00, 0x81, 0x0A, 0x06, // 51:129 + 0x00, 0x8B, 0x0B, 0x06, // 52:139 + 0x00, 0x96, 0x0A, 0x06, // 53:150 + 0x00, 0xA0, 0x0A, 0x06, // 54:160 + 0x00, 0xAA, 0x09, 0x06, // 55:170 + 0x00, 0xB3, 0x0A, 0x06, // 56:179 + 0x00, 0xBD, 0x0A, 0x06, // 57:189 + 0x00, 0xC7, 0x04, 0x03, // 58:199 + 0x00, 0xCB, 0x04, 0x03, // 59:203 + 0x00, 0xCF, 0x0A, 0x06, // 60:207 + 0x00, 0xD9, 0x09, 0x06, // 61:217 + 0x00, 0xE2, 0x09, 0x06, // 62:226 + 0x00, 0xEB, 0x0B, 0x06, // 63:235 + 0x00, 0xF6, 0x14, 0x0A, // 64:246 + 0x01, 0x0A, 0x0E, 0x07, // 65:266 + 0x01, 0x18, 0x0C, 0x07, // 66:280 + 0x01, 0x24, 0x0C, 0x07, // 67:292 + 0x01, 0x30, 0x0B, 0x07, // 68:304 + 0x01, 0x3B, 0x0C, 0x07, // 69:315 + 0x01, 0x47, 0x09, 0x06, // 70:327 + 0x01, 0x50, 0x0D, 0x08, // 71:336 + 0x01, 0x5D, 0x0C, 0x07, // 72:349 + 0x01, 0x69, 0x04, 0x03, // 73:361 + 0x01, 0x6D, 0x08, 0x05, // 74:365 + 0x01, 0x75, 0x0E, 0x07, // 75:373 + 0x01, 0x83, 0x0C, 0x06, // 76:387 + 0x01, 0x8F, 0x10, 0x08, // 77:399 + 0x01, 0x9F, 0x0C, 0x07, // 78:415 + 0x01, 0xAB, 0x0E, 0x08, // 79:427 + 0x01, 0xB9, 0x0B, 0x07, // 80:441 + 0x01, 0xC4, 0x0E, 0x08, // 81:452 + 0x01, 0xD2, 0x0C, 0x07, // 82:466 + 0x01, 0xDE, 0x0C, 0x07, // 83:478 + 0x01, 0xEA, 0x0B, 0x06, // 84:490 + 0x01, 0xF5, 0x0C, 0x07, // 85:501 + 0x02, 0x01, 0x0D, 0x07, // 86:513 + 0x02, 0x0E, 0x11, 0x09, // 87:526 + 0x02, 0x1F, 0x0E, 0x07, // 88:543 + 0x02, 0x2D, 0x0D, 0x07, // 89:557 + 0x02, 0x3A, 0x0C, 0x06, // 90:570 + 0x02, 0x46, 0x06, 0x03, // 91:582 + 0x02, 0x4C, 0x06, 0x03, // 92:588 + 0x02, 0x52, 0x04, 0x03, // 93:594 + 0x02, 0x56, 0x09, 0x05, // 94:598 + 0x02, 0x5F, 0x0C, 0x06, // 95:607 + 0x02, 0x6B, 0x03, 0x03, // 96:619 + 0x02, 0x6E, 0x0A, 0x06, // 97:622 + 0x02, 0x78, 0x0A, 0x06, // 98:632 + 0x02, 0x82, 0x0A, 0x05, // 99:642 + 0x02, 0x8C, 0x0A, 0x06, // 100:652 + 0x02, 0x96, 0x0A, 0x06, // 101:662 + 0x02, 0xA0, 0x05, 0x03, // 102:672 + 0x02, 0xA5, 0x0A, 0x06, // 103:677 + 0x02, 0xAF, 0x0A, 0x06, // 104:687 + 0x02, 0xB9, 0x04, 0x02, // 105:697 + 0x02, 0xBD, 0x04, 0x02, // 106:701 + 0x02, 0xC1, 0x08, 0x05, // 107:705 + 0x02, 0xC9, 0x04, 0x02, // 108:713 + 0x02, 0xCD, 0x10, 0x08, // 109:717 + 0x02, 0xDD, 0x0A, 0x06, // 110:733 + 0x02, 0xE7, 0x0A, 0x06, // 111:743 + 0x02, 0xF1, 0x0A, 0x06, // 112:753 + 0x02, 0xFB, 0x0A, 0x06, // 113:763 + 0x03, 0x05, 0x05, 0x03, // 114:773 + 0x03, 0x0A, 0x08, 0x05, // 115:778 + 0x03, 0x12, 0x06, 0x03, // 116:786 + 0x03, 0x18, 0x0A, 0x06, // 117:792 + 0x03, 0x22, 0x09, 0x05, // 118:802 + 0x03, 0x2B, 0x0E, 0x07, // 119:811 + 0x03, 0x39, 0x0A, 0x05, // 120:825 + 0x03, 0x43, 0x09, 0x05, // 121:835 + 0x03, 0x4C, 0x0A, 0x05, // 122:844 + 0x03, 0x56, 0x06, 0x03, // 123:854 + 0x03, 0x5C, 0x04, 0x03, // 124:860 + 0x03, 0x60, 0x05, 0x03, // 125:864 + 0x03, 0x65, 0x09, 0x06, // 126:869 + 0xFF, 0xFF, 0x00, 0x00, // 127:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 128:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 129:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 130:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 131:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 132:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 133:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 134:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 135:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 136:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 137:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 138:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 139:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 140:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 141:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 142:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 143:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 144:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 145:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 146:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 147:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 148:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 149:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 150:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 151:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 152:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 153:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 154:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 155:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 156:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 157:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 158:65535 + 0xFF, 0xFF, 0x00, 0x0A, // 159:65535 + 0xFF, 0xFF, 0x00, 0x03, // 160:65535 + 0x03, 0x6E, 0x04, 0x03, // 161:878 + 0x03, 0x72, 0x0A, 0x06, // 162:882 + 0x03, 0x7C, 0x0C, 0x06, // 163:892 + 0x03, 0x88, 0x0A, 0x06, // 164:904 + 0x03, 0x92, 0x0A, 0x06, // 165:914 + 0x03, 0x9C, 0x04, 0x03, // 166:924 + 0x03, 0xA0, 0x0A, 0x06, // 167:928 + 0x03, 0xAA, 0x05, 0x03, // 168:938 + 0x03, 0xAF, 0x0D, 0x07, // 169:943 + 0x03, 0xBC, 0x07, 0x04, // 170:956 + 0x03, 0xC3, 0x0A, 0x06, // 171:963 + 0x03, 0xCD, 0x09, 0x06, // 172:973 + 0x03, 0xD6, 0x03, 0x03, // 173:982 + 0x03, 0xD9, 0x0D, 0x07, // 174:985 + 0x03, 0xE6, 0x0B, 0x06, // 175:998 + 0x03, 0xF1, 0x07, 0x04, // 176:1009 + 0x03, 0xF8, 0x0A, 0x05, // 177:1016 + 0x04, 0x02, 0x05, 0x03, // 178:1026 + 0x04, 0x07, 0x05, 0x03, // 179:1031 + 0x04, 0x0C, 0x05, 0x03, // 180:1036 + 0x04, 0x11, 0x0A, 0x06, // 181:1041 + 0x04, 0x1B, 0x09, 0x05, // 182:1051 + 0x04, 0x24, 0x03, 0x03, // 183:1060 + 0x04, 0x27, 0x06, 0x03, // 184:1063 + 0x04, 0x2D, 0x05, 0x03, // 185:1069 + 0x04, 0x32, 0x07, 0x04, // 186:1074 + 0x04, 0x39, 0x0A, 0x06, // 187:1081 + 0x04, 0x43, 0x10, 0x08, // 188:1091 + 0x04, 0x53, 0x10, 0x08, // 189:1107 + 0x04, 0x63, 0x10, 0x08, // 190:1123 + 0x04, 0x73, 0x0A, 0x06, // 191:1139 + 0x04, 0x7D, 0x0E, 0x07, // 192:1149 + 0x04, 0x8B, 0x0E, 0x07, // 193:1163 + 0x04, 0x99, 0x0E, 0x07, // 194:1177 + 0x04, 0xA7, 0x0E, 0x07, // 195:1191 + 0x04, 0xB5, 0x0E, 0x07, // 196:1205 + 0x04, 0xC3, 0x0E, 0x07, // 197:1219 + 0x04, 0xD1, 0x12, 0x0A, // 198:1233 + 0x04, 0xE3, 0x0C, 0x07, // 199:1251 + 0x04, 0xEF, 0x0C, 0x07, // 200:1263 + 0x04, 0xFB, 0x0C, 0x07, // 201:1275 + 0x05, 0x07, 0x0C, 0x07, // 202:1287 + 0x05, 0x13, 0x0C, 0x07, // 203:1299 + 0x05, 0x1F, 0x05, 0x03, // 204:1311 + 0x05, 0x24, 0x04, 0x03, // 205:1316 + 0x05, 0x28, 0x04, 0x03, // 206:1320 + 0x05, 0x2C, 0x05, 0x03, // 207:1324 + 0x05, 0x31, 0x0B, 0x07, // 208:1329 + 0x05, 0x3C, 0x0C, 0x07, // 209:1340 + 0x05, 0x48, 0x0E, 0x08, // 210:1352 + 0x05, 0x56, 0x0E, 0x08, // 211:1366 + 0x05, 0x64, 0x0E, 0x08, // 212:1380 + 0x05, 0x72, 0x0E, 0x08, // 213:1394 + 0x05, 0x80, 0x0E, 0x08, // 214:1408 + 0x05, 0x8E, 0x0A, 0x06, // 215:1422 + 0x05, 0x98, 0x0D, 0x08, // 216:1432 + 0x05, 0xA5, 0x0C, 0x07, // 217:1445 + 0x05, 0xB1, 0x0C, 0x07, // 218:1457 + 0x05, 0xBD, 0x0C, 0x07, // 219:1469 + 0x05, 0xC9, 0x0C, 0x07, // 220:1481 + 0x05, 0xD5, 0x0D, 0x07, // 221:1493 + 0x05, 0xE2, 0x0B, 0x07, // 222:1506 + 0x05, 0xED, 0x0C, 0x06, // 223:1517 + 0x05, 0xF9, 0x0A, 0x06, // 224:1529 + 0x06, 0x03, 0x0A, 0x06, // 225:1539 + 0x06, 0x0D, 0x0A, 0x06, // 226:1549 + 0x06, 0x17, 0x0A, 0x06, // 227:1559 + 0x06, 0x21, 0x0A, 0x06, // 228:1569 + 0x06, 0x2B, 0x0A, 0x06, // 229:1579 + 0x06, 0x35, 0x10, 0x09, // 230:1589 + 0x06, 0x45, 0x0A, 0x05, // 231:1605 + 0x06, 0x4F, 0x0A, 0x06, // 232:1615 + 0x06, 0x59, 0x0A, 0x06, // 233:1625 + 0x06, 0x63, 0x0A, 0x06, // 234:1635 + 0x06, 0x6D, 0x0A, 0x06, // 235:1645 + 0x06, 0x77, 0x05, 0x03, // 236:1655 + 0x06, 0x7C, 0x04, 0x03, // 237:1660 + 0x06, 0x80, 0x05, 0x03, // 238:1664 + 0x06, 0x85, 0x05, 0x03, // 239:1669 + 0x06, 0x8A, 0x0A, 0x06, // 240:1674 + 0x06, 0x94, 0x0A, 0x06, // 241:1684 + 0x06, 0x9E, 0x0A, 0x06, // 242:1694 + 0x06, 0xA8, 0x0A, 0x06, // 243:1704 + 0x06, 0xB2, 0x0A, 0x06, // 244:1714 + 0x06, 0xBC, 0x0A, 0x06, // 245:1724 + 0x06, 0xC6, 0x0A, 0x06, // 246:1734 + 0x06, 0xD0, 0x09, 0x05, // 247:1744 + 0x06, 0xD9, 0x0A, 0x06, // 248:1753 + 0x06, 0xE3, 0x0A, 0x06, // 249:1763 + 0x06, 0xED, 0x0A, 0x06, // 250:1773 + 0x06, 0xF7, 0x0A, 0x06, // 251:1783 + 0x07, 0x01, 0x0A, 0x06, // 252:1793 + 0x07, 0x0B, 0x09, 0x05, // 253:1803 + 0x07, 0x14, 0x0A, 0x06, // 254:1812 + 0x07, 0x1E, 0x09, 0x05, // 255:1822 + + // Font Data: + 0x00,0x00,0xF8,0x02, // 33 + 0x38,0x00,0x00,0x00,0x38, // 34 + 0xA0,0x03,0xE0,0x00,0xB8,0x03,0xE0,0x00,0xB8, // 35 + 0x30,0x01,0x28,0x02,0xF8,0x07,0x48,0x02,0x90,0x01, // 36 + 0x00,0x00,0x30,0x00,0x48,0x00,0x30,0x03,0xC0,0x00,0xB0,0x01,0x48,0x02,0x80,0x01, // 37 + 0x80,0x01,0x50,0x02,0x68,0x02,0xA8,0x02,0x18,0x01,0x80,0x03,0x80,0x02, // 38 + 0x38, // 39 + 0xE0,0x03,0x10,0x04,0x08,0x08, // 40 + 0x08,0x08,0x10,0x04,0xE0,0x03, // 41 + 0x28,0x00,0x18,0x00,0x28, // 42 + 0x40,0x00,0x40,0x00,0xF0,0x01,0x40,0x00,0x40, // 43 + 0x00,0x00,0x00,0x06, // 44 + 0x80,0x00,0x80, // 45 + 0x00,0x00,0x00,0x02, // 46 + 0x00,0x03,0xE0,0x00,0x18, // 47 + 0xF0,0x01,0x08,0x02,0x08,0x02,0x08,0x02,0xF0,0x01, // 48 + 0x00,0x00,0x20,0x00,0x10,0x00,0xF8,0x03, // 49 + 0x10,0x02,0x08,0x03,0x88,0x02,0x48,0x02,0x30,0x02, // 50 + 0x10,0x01,0x08,0x02,0x48,0x02,0x48,0x02,0xB0,0x01, // 51 + 0xC0,0x00,0xA0,0x00,0x90,0x00,0x88,0x00,0xF8,0x03,0x80, // 52 + 0x60,0x01,0x38,0x02,0x28,0x02,0x28,0x02,0xC8,0x01, // 53 + 0xF0,0x01,0x28,0x02,0x28,0x02,0x28,0x02,0xD0,0x01, // 54 + 0x08,0x00,0x08,0x03,0xC8,0x00,0x38,0x00,0x08, // 55 + 0xB0,0x01,0x48,0x02,0x48,0x02,0x48,0x02,0xB0,0x01, // 56 + 0x70,0x01,0x88,0x02,0x88,0x02,0x88,0x02,0xF0,0x01, // 57 + 0x00,0x00,0x20,0x02, // 58 + 0x00,0x00,0x20,0x06, // 59 + 0x00,0x00,0x40,0x00,0xA0,0x00,0xA0,0x00,0x10,0x01, // 60 + 0xA0,0x00,0xA0,0x00,0xA0,0x00,0xA0,0x00,0xA0, // 61 + 0x00,0x00,0x10,0x01,0xA0,0x00,0xA0,0x00,0x40, // 62 + 0x10,0x00,0x08,0x00,0x08,0x00,0xC8,0x02,0x48,0x00,0x30, // 63 + 0x00,0x00,0xC0,0x03,0x30,0x04,0xD0,0x09,0x28,0x0A,0x28,0x0A,0xC8,0x0B,0x68,0x0A,0x10,0x05,0xE0,0x04, // 64 + 0x00,0x02,0xC0,0x01,0xB0,0x00,0x88,0x00,0xB0,0x00,0xC0,0x01,0x00,0x02, // 65 + 0x00,0x00,0xF8,0x03,0x48,0x02,0x48,0x02,0x48,0x02,0xF0,0x01, // 66 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x08,0x02,0x08,0x02,0x10,0x01, // 67 + 0x00,0x00,0xF8,0x03,0x08,0x02,0x08,0x02,0x10,0x01,0xE0, // 68 + 0x00,0x00,0xF8,0x03,0x48,0x02,0x48,0x02,0x48,0x02,0x48,0x02, // 69 + 0x00,0x00,0xF8,0x03,0x48,0x00,0x48,0x00,0x08, // 70 + 0x00,0x00,0xE0,0x00,0x10,0x01,0x08,0x02,0x48,0x02,0x50,0x01,0xC0, // 71 + 0x00,0x00,0xF8,0x03,0x40,0x00,0x40,0x00,0x40,0x00,0xF8,0x03, // 72 + 0x00,0x00,0xF8,0x03, // 73 + 0x00,0x03,0x00,0x02,0x00,0x02,0xF8,0x01, // 74 + 0x00,0x00,0xF8,0x03,0x80,0x00,0x60,0x00,0x90,0x00,0x08,0x01,0x00,0x02, // 75 + 0x00,0x00,0xF8,0x03,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02, // 76 + 0x00,0x00,0xF8,0x03,0x30,0x00,0xC0,0x01,0x00,0x02,0xC0,0x01,0x30,0x00,0xF8,0x03, // 77 + 0x00,0x00,0xF8,0x03,0x30,0x00,0x40,0x00,0x80,0x01,0xF8,0x03, // 78 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x08,0x02,0x08,0x02,0x08,0x02,0xF0,0x01, // 79 + 0x00,0x00,0xF8,0x03,0x48,0x00,0x48,0x00,0x48,0x00,0x30, // 80 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x08,0x02,0x08,0x03,0x08,0x03,0xF0,0x02, // 81 + 0x00,0x00,0xF8,0x03,0x48,0x00,0x48,0x00,0xC8,0x00,0x30,0x03, // 82 + 0x00,0x00,0x30,0x01,0x48,0x02,0x48,0x02,0x48,0x02,0x90,0x01, // 83 + 0x00,0x00,0x08,0x00,0x08,0x00,0xF8,0x03,0x08,0x00,0x08, // 84 + 0x00,0x00,0xF8,0x01,0x00,0x02,0x00,0x02,0x00,0x02,0xF8,0x01, // 85 + 0x08,0x00,0x70,0x00,0x80,0x01,0x00,0x02,0x80,0x01,0x70,0x00,0x08, // 86 + 0x18,0x00,0xE0,0x01,0x00,0x02,0xF0,0x01,0x08,0x00,0xF0,0x01,0x00,0x02,0xE0,0x01,0x18, // 87 + 0x00,0x02,0x08,0x01,0x90,0x00,0x60,0x00,0x90,0x00,0x08,0x01,0x00,0x02, // 88 + 0x08,0x00,0x10,0x00,0x20,0x00,0xC0,0x03,0x20,0x00,0x10,0x00,0x08, // 89 + 0x08,0x03,0x88,0x02,0xC8,0x02,0x68,0x02,0x38,0x02,0x18,0x02, // 90 + 0x00,0x00,0xF8,0x0F,0x08,0x08, // 91 + 0x18,0x00,0xE0,0x00,0x00,0x03, // 92 + 0x08,0x08,0xF8,0x0F, // 93 + 0x40,0x00,0x30,0x00,0x08,0x00,0x30,0x00,0x40, // 94 + 0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08, // 95 + 0x08,0x00,0x10, // 96 + 0x00,0x00,0x00,0x03,0xA0,0x02,0xA0,0x02,0xE0,0x03, // 97 + 0x00,0x00,0xF8,0x03,0x20,0x02,0x20,0x02,0xC0,0x01, // 98 + 0x00,0x00,0xC0,0x01,0x20,0x02,0x20,0x02,0x40,0x01, // 99 + 0x00,0x00,0xC0,0x01,0x20,0x02,0x20,0x02,0xF8,0x03, // 100 + 0x00,0x00,0xC0,0x01,0xA0,0x02,0xA0,0x02,0xC0,0x02, // 101 + 0x20,0x00,0xF0,0x03,0x28, // 102 + 0x00,0x00,0xC0,0x05,0x20,0x0A,0x20,0x0A,0xE0,0x07, // 103 + 0x00,0x00,0xF8,0x03,0x20,0x00,0x20,0x00,0xC0,0x03, // 104 + 0x00,0x00,0xE8,0x03, // 105 + 0x00,0x08,0xE8,0x07, // 106 + 0xF8,0x03,0x80,0x00,0xC0,0x01,0x20,0x02, // 107 + 0x00,0x00,0xF8,0x03, // 108 + 0x00,0x00,0xE0,0x03,0x20,0x00,0x20,0x00,0xE0,0x03,0x20,0x00,0x20,0x00,0xC0,0x03, // 109 + 0x00,0x00,0xE0,0x03,0x20,0x00,0x20,0x00,0xC0,0x03, // 110 + 0x00,0x00,0xC0,0x01,0x20,0x02,0x20,0x02,0xC0,0x01, // 111 + 0x00,0x00,0xE0,0x0F,0x20,0x02,0x20,0x02,0xC0,0x01, // 112 + 0x00,0x00,0xC0,0x01,0x20,0x02,0x20,0x02,0xE0,0x0F, // 113 + 0x00,0x00,0xE0,0x03,0x20, // 114 + 0x40,0x02,0xA0,0x02,0xA0,0x02,0x20,0x01, // 115 + 0x20,0x00,0xF8,0x03,0x20,0x02, // 116 + 0x00,0x00,0xE0,0x01,0x00,0x02,0x00,0x02,0xE0,0x03, // 117 + 0x20,0x00,0xC0,0x01,0x00,0x02,0xC0,0x01,0x20, // 118 + 0xE0,0x01,0x00,0x02,0xC0,0x01,0x20,0x00,0xC0,0x01,0x00,0x02,0xE0,0x01, // 119 + 0x20,0x02,0x40,0x01,0x80,0x00,0x40,0x01,0x20,0x02, // 120 + 0x20,0x00,0xC0,0x09,0x00,0x06,0xC0,0x01,0x20, // 121 + 0x20,0x02,0x20,0x03,0xA0,0x02,0x60,0x02,0x20,0x02, // 122 + 0x80,0x00,0x78,0x0F,0x08,0x08, // 123 + 0x00,0x00,0xF8,0x0F, // 124 + 0x08,0x08,0x78,0x0F,0x80, // 125 + 0xC0,0x00,0x40,0x00,0xC0,0x00,0x80,0x00,0xC0, // 126 + 0x00,0x00,0xA0,0x0F, // 161 + 0x00,0x00,0xC0,0x01,0xA0,0x0F,0x78,0x02,0x40,0x01, // 162 + 0x40,0x02,0x70,0x03,0xC8,0x02,0x48,0x02,0x08,0x02,0x10,0x02, // 163 + 0x00,0x00,0xE0,0x01,0x20,0x01,0x20,0x01,0xE0,0x01, // 164 + 0x48,0x01,0x70,0x01,0xC0,0x03,0x70,0x01,0x48,0x01, // 165 + 0x00,0x00,0x38,0x0F, // 166 + 0xD0,0x04,0x28,0x09,0x48,0x09,0x48,0x0A,0x90,0x05, // 167 + 0x08,0x00,0x00,0x00,0x08, // 168 + 0xE0,0x00,0x10,0x01,0x48,0x02,0xA8,0x02,0xA8,0x02,0x10,0x01,0xE0, // 169 + 0x68,0x00,0x68,0x00,0x68,0x00,0x78, // 170 + 0x00,0x00,0x80,0x01,0x40,0x02,0x80,0x01,0x40,0x02, // 171 + 0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0xE0, // 172 + 0x80,0x00,0x80, // 173 + 0xE0,0x00,0x10,0x01,0xE8,0x02,0x68,0x02,0xC8,0x02,0x10,0x01,0xE0, // 174 + 0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02, // 175 + 0x00,0x00,0x38,0x00,0x28,0x00,0x38, // 176 + 0x40,0x02,0x40,0x02,0xF0,0x03,0x40,0x02,0x40,0x02, // 177 + 0x48,0x00,0x68,0x00,0x58, // 178 + 0x48,0x00,0x58,0x00,0x68, // 179 + 0x00,0x00,0x10,0x00,0x08, // 180 + 0x00,0x00,0xE0,0x0F,0x00,0x02,0x00,0x02,0xE0,0x03, // 181 + 0x70,0x00,0xF8,0x0F,0x08,0x00,0xF8,0x0F,0x08, // 182 + 0x00,0x00,0x40, // 183 + 0x00,0x00,0x00,0x14,0x00,0x18, // 184 + 0x00,0x00,0x10,0x00,0x78, // 185 + 0x30,0x00,0x48,0x00,0x48,0x00,0x30, // 186 + 0x00,0x00,0x40,0x02,0x80,0x01,0x40,0x02,0x80,0x01, // 187 + 0x00,0x00,0x10,0x02,0x78,0x01,0xC0,0x00,0x20,0x01,0x90,0x01,0xC8,0x03,0x00,0x01, // 188 + 0x00,0x00,0x10,0x02,0x78,0x01,0x80,0x00,0x60,0x00,0x50,0x02,0x48,0x03,0xC0,0x02, // 189 + 0x48,0x00,0x58,0x00,0x68,0x03,0x80,0x00,0x60,0x01,0x90,0x01,0xC8,0x03,0x00,0x01, // 190 + 0x00,0x00,0x00,0x06,0x00,0x09,0xA0,0x09,0x00,0x04, // 191 + 0x00,0x02,0xC0,0x01,0xB0,0x00,0x89,0x00,0xB2,0x00,0xC0,0x01,0x00,0x02, // 192 + 0x00,0x02,0xC0,0x01,0xB0,0x00,0x8A,0x00,0xB1,0x00,0xC0,0x01,0x00,0x02, // 193 + 0x00,0x02,0xC0,0x01,0xB2,0x00,0x89,0x00,0xB2,0x00,0xC0,0x01,0x00,0x02, // 194 + 0x00,0x02,0xC2,0x01,0xB1,0x00,0x8A,0x00,0xB1,0x00,0xC0,0x01,0x00,0x02, // 195 + 0x00,0x02,0xC0,0x01,0xB2,0x00,0x88,0x00,0xB2,0x00,0xC0,0x01,0x00,0x02, // 196 + 0x00,0x02,0xC0,0x01,0xBE,0x00,0x8A,0x00,0xBE,0x00,0xC0,0x01,0x00,0x02, // 197 + 0x00,0x03,0xC0,0x00,0xE0,0x00,0x98,0x00,0x88,0x00,0xF8,0x03,0x48,0x02,0x48,0x02,0x48,0x02, // 198 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x08,0x16,0x08,0x1A,0x10,0x01, // 199 + 0x00,0x00,0xF8,0x03,0x49,0x02,0x4A,0x02,0x48,0x02,0x48,0x02, // 200 + 0x00,0x00,0xF8,0x03,0x48,0x02,0x4A,0x02,0x49,0x02,0x48,0x02, // 201 + 0x00,0x00,0xFA,0x03,0x49,0x02,0x4A,0x02,0x48,0x02,0x48,0x02, // 202 + 0x00,0x00,0xF8,0x03,0x4A,0x02,0x48,0x02,0x4A,0x02,0x48,0x02, // 203 + 0x00,0x00,0xF9,0x03,0x02, // 204 + 0x02,0x00,0xF9,0x03, // 205 + 0x01,0x00,0xFA,0x03, // 206 + 0x02,0x00,0xF8,0x03,0x02, // 207 + 0x40,0x00,0xF8,0x03,0x48,0x02,0x48,0x02,0x10,0x01,0xE0, // 208 + 0x00,0x00,0xFA,0x03,0x31,0x00,0x42,0x00,0x81,0x01,0xF8,0x03, // 209 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x09,0x02,0x0A,0x02,0x08,0x02,0xF0,0x01, // 210 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x0A,0x02,0x09,0x02,0x08,0x02,0xF0,0x01, // 211 + 0x00,0x00,0xF0,0x01,0x08,0x02,0x0A,0x02,0x09,0x02,0x0A,0x02,0xF0,0x01, // 212 + 0x00,0x00,0xF0,0x01,0x0A,0x02,0x09,0x02,0x0A,0x02,0x09,0x02,0xF0,0x01, // 213 + 0x00,0x00,0xF0,0x01,0x0A,0x02,0x08,0x02,0x0A,0x02,0x08,0x02,0xF0,0x01, // 214 + 0x10,0x01,0xA0,0x00,0xE0,0x00,0xA0,0x00,0x10,0x01, // 215 + 0x00,0x00,0xF0,0x02,0x08,0x03,0xC8,0x02,0x28,0x02,0x18,0x03,0xE8, // 216 + 0x00,0x00,0xF8,0x01,0x01,0x02,0x02,0x02,0x00,0x02,0xF8,0x01, // 217 + 0x00,0x00,0xF8,0x01,0x02,0x02,0x01,0x02,0x00,0x02,0xF8,0x01, // 218 + 0x00,0x00,0xF8,0x01,0x02,0x02,0x01,0x02,0x02,0x02,0xF8,0x01, // 219 + 0x00,0x00,0xF8,0x01,0x02,0x02,0x00,0x02,0x02,0x02,0xF8,0x01, // 220 + 0x08,0x00,0x10,0x00,0x20,0x00,0xC2,0x03,0x21,0x00,0x10,0x00,0x08, // 221 + 0x00,0x00,0xF8,0x03,0x10,0x01,0x10,0x01,0x10,0x01,0xE0, // 222 + 0x00,0x00,0xF0,0x03,0x08,0x01,0x48,0x02,0xB0,0x02,0x80,0x01, // 223 + 0x00,0x00,0x00,0x03,0xA4,0x02,0xA8,0x02,0xE0,0x03, // 224 + 0x00,0x00,0x00,0x03,0xA8,0x02,0xA4,0x02,0xE0,0x03, // 225 + 0x00,0x00,0x00,0x03,0xA8,0x02,0xA4,0x02,0xE8,0x03, // 226 + 0x00,0x00,0x08,0x03,0xA4,0x02,0xA8,0x02,0xE4,0x03, // 227 + 0x00,0x00,0x00,0x03,0xA8,0x02,0xA0,0x02,0xE8,0x03, // 228 + 0x00,0x00,0x00,0x03,0xAE,0x02,0xAA,0x02,0xEE,0x03, // 229 + 0x00,0x00,0x40,0x03,0xA0,0x02,0xA0,0x02,0xC0,0x01,0xA0,0x02,0xA0,0x02,0xC0,0x02, // 230 + 0x00,0x00,0xC0,0x01,0x20,0x16,0x20,0x1A,0x40,0x01, // 231 + 0x00,0x00,0xC0,0x01,0xA4,0x02,0xA8,0x02,0xC0,0x02, // 232 + 0x00,0x00,0xC0,0x01,0xA8,0x02,0xA4,0x02,0xC0,0x02, // 233 + 0x00,0x00,0xC0,0x01,0xA8,0x02,0xA4,0x02,0xC8,0x02, // 234 + 0x00,0x00,0xC0,0x01,0xA8,0x02,0xA0,0x02,0xC8,0x02, // 235 + 0x00,0x00,0xE4,0x03,0x08, // 236 + 0x08,0x00,0xE4,0x03, // 237 + 0x08,0x00,0xE4,0x03,0x08, // 238 + 0x08,0x00,0xE0,0x03,0x08, // 239 + 0x00,0x00,0xC0,0x01,0x28,0x02,0x38,0x02,0xE0,0x01, // 240 + 0x00,0x00,0xE8,0x03,0x24,0x00,0x28,0x00,0xC4,0x03, // 241 + 0x00,0x00,0xC0,0x01,0x24,0x02,0x28,0x02,0xC0,0x01, // 242 + 0x00,0x00,0xC0,0x01,0x28,0x02,0x24,0x02,0xC0,0x01, // 243 + 0x00,0x00,0xC0,0x01,0x28,0x02,0x24,0x02,0xC8,0x01, // 244 + 0x00,0x00,0xC8,0x01,0x24,0x02,0x28,0x02,0xC4,0x01, // 245 + 0x00,0x00,0xC0,0x01,0x28,0x02,0x20,0x02,0xC8,0x01, // 246 + 0x40,0x00,0x40,0x00,0x50,0x01,0x40,0x00,0x40, // 247 + 0x00,0x00,0xC0,0x02,0xA0,0x03,0x60,0x02,0xA0,0x01, // 248 + 0x00,0x00,0xE0,0x01,0x04,0x02,0x08,0x02,0xE0,0x03, // 249 + 0x00,0x00,0xE0,0x01,0x08,0x02,0x04,0x02,0xE0,0x03, // 250 + 0x00,0x00,0xE8,0x01,0x04,0x02,0x08,0x02,0xE0,0x03, // 251 + 0x00,0x00,0xE0,0x01,0x08,0x02,0x00,0x02,0xE8,0x03, // 252 + 0x20,0x00,0xC0,0x09,0x08,0x06,0xC4,0x01,0x20, // 253 + 0x00,0x00,0xF8,0x0F,0x20,0x02,0x20,0x02,0xC0,0x01, // 254 + 0x20,0x00,0xC8,0x09,0x00,0x06,0xC8,0x01,0x20 // 255 +}; + +const uint8_t ArialMT_Plain_16[] PROGMEM = { + 0x10, // Width: 16 + 0x13, // Height: 19 + 0x20, // First Char: 32 + 0xE0, // Numbers of Chars: 224 + + // Jump Table: + 0xFF, 0xFF, 0x00, 0x04, // 32:65535 + 0x00, 0x00, 0x08, 0x04, // 33:0 + 0x00, 0x08, 0x0D, 0x06, // 34:8 + 0x00, 0x15, 0x1A, 0x09, // 35:21 + 0x00, 0x2F, 0x17, 0x09, // 36:47 + 0x00, 0x46, 0x26, 0x0E, // 37:70 + 0x00, 0x6C, 0x1D, 0x0B, // 38:108 + 0x00, 0x89, 0x04, 0x03, // 39:137 + 0x00, 0x8D, 0x0C, 0x05, // 40:141 + 0x00, 0x99, 0x0B, 0x05, // 41:153 + 0x00, 0xA4, 0x0D, 0x06, // 42:164 + 0x00, 0xB1, 0x17, 0x09, // 43:177 + 0x00, 0xC8, 0x09, 0x04, // 44:200 + 0x00, 0xD1, 0x0B, 0x05, // 45:209 + 0x00, 0xDC, 0x08, 0x04, // 46:220 + 0x00, 0xE4, 0x0A, 0x04, // 47:228 + 0x00, 0xEE, 0x17, 0x09, // 48:238 + 0x01, 0x05, 0x11, 0x09, // 49:261 + 0x01, 0x16, 0x17, 0x09, // 50:278 + 0x01, 0x2D, 0x17, 0x09, // 51:301 + 0x01, 0x44, 0x17, 0x09, // 52:324 + 0x01, 0x5B, 0x17, 0x09, // 53:347 + 0x01, 0x72, 0x17, 0x09, // 54:370 + 0x01, 0x89, 0x16, 0x09, // 55:393 + 0x01, 0x9F, 0x17, 0x09, // 56:415 + 0x01, 0xB6, 0x17, 0x09, // 57:438 + 0x01, 0xCD, 0x05, 0x04, // 58:461 + 0x01, 0xD2, 0x06, 0x04, // 59:466 + 0x01, 0xD8, 0x17, 0x09, // 60:472 + 0x01, 0xEF, 0x17, 0x09, // 61:495 + 0x02, 0x06, 0x17, 0x09, // 62:518 + 0x02, 0x1D, 0x16, 0x09, // 63:541 + 0x02, 0x33, 0x2F, 0x10, // 64:563 + 0x02, 0x62, 0x1D, 0x0B, // 65:610 + 0x02, 0x7F, 0x1D, 0x0B, // 66:639 + 0x02, 0x9C, 0x20, 0x0C, // 67:668 + 0x02, 0xBC, 0x20, 0x0C, // 68:700 + 0x02, 0xDC, 0x1D, 0x0B, // 69:732 + 0x02, 0xF9, 0x19, 0x0A, // 70:761 + 0x03, 0x12, 0x20, 0x0C, // 71:786 + 0x03, 0x32, 0x1D, 0x0C, // 72:818 + 0x03, 0x4F, 0x05, 0x04, // 73:847 + 0x03, 0x54, 0x14, 0x08, // 74:852 + 0x03, 0x68, 0x1D, 0x0B, // 75:872 + 0x03, 0x85, 0x17, 0x09, // 76:901 + 0x03, 0x9C, 0x23, 0x0D, // 77:924 + 0x03, 0xBF, 0x1D, 0x0C, // 78:959 + 0x03, 0xDC, 0x20, 0x0C, // 79:988 + 0x03, 0xFC, 0x1C, 0x0B, // 80:1020 + 0x04, 0x18, 0x20, 0x0C, // 81:1048 + 0x04, 0x38, 0x1D, 0x0C, // 82:1080 + 0x04, 0x55, 0x1D, 0x0B, // 83:1109 + 0x04, 0x72, 0x19, 0x0A, // 84:1138 + 0x04, 0x8B, 0x1D, 0x0C, // 85:1163 + 0x04, 0xA8, 0x1C, 0x0B, // 86:1192 + 0x04, 0xC4, 0x2B, 0x0F, // 87:1220 + 0x04, 0xEF, 0x20, 0x0B, // 88:1263 + 0x05, 0x0F, 0x19, 0x0B, // 89:1295 + 0x05, 0x28, 0x1A, 0x0A, // 90:1320 + 0x05, 0x42, 0x0C, 0x04, // 91:1346 + 0x05, 0x4E, 0x0B, 0x04, // 92:1358 + 0x05, 0x59, 0x09, 0x04, // 93:1369 + 0x05, 0x62, 0x14, 0x08, // 94:1378 + 0x05, 0x76, 0x1B, 0x09, // 95:1398 + 0x05, 0x91, 0x07, 0x05, // 96:1425 + 0x05, 0x98, 0x17, 0x09, // 97:1432 + 0x05, 0xAF, 0x17, 0x09, // 98:1455 + 0x05, 0xC6, 0x14, 0x08, // 99:1478 + 0x05, 0xDA, 0x17, 0x09, // 100:1498 + 0x05, 0xF1, 0x17, 0x09, // 101:1521 + 0x06, 0x08, 0x0A, 0x04, // 102:1544 + 0x06, 0x12, 0x17, 0x09, // 103:1554 + 0x06, 0x29, 0x14, 0x09, // 104:1577 + 0x06, 0x3D, 0x05, 0x04, // 105:1597 + 0x06, 0x42, 0x06, 0x04, // 106:1602 + 0x06, 0x48, 0x17, 0x08, // 107:1608 + 0x06, 0x5F, 0x05, 0x04, // 108:1631 + 0x06, 0x64, 0x23, 0x0D, // 109:1636 + 0x06, 0x87, 0x14, 0x09, // 110:1671 + 0x06, 0x9B, 0x17, 0x09, // 111:1691 + 0x06, 0xB2, 0x17, 0x09, // 112:1714 + 0x06, 0xC9, 0x18, 0x09, // 113:1737 + 0x06, 0xE1, 0x0D, 0x05, // 114:1761 + 0x06, 0xEE, 0x14, 0x08, // 115:1774 + 0x07, 0x02, 0x0B, 0x04, // 116:1794 + 0x07, 0x0D, 0x14, 0x09, // 117:1805 + 0x07, 0x21, 0x13, 0x08, // 118:1825 + 0x07, 0x34, 0x1F, 0x0C, // 119:1844 + 0x07, 0x53, 0x14, 0x08, // 120:1875 + 0x07, 0x67, 0x13, 0x08, // 121:1895 + 0x07, 0x7A, 0x14, 0x08, // 122:1914 + 0x07, 0x8E, 0x0F, 0x05, // 123:1934 + 0x07, 0x9D, 0x06, 0x04, // 124:1949 + 0x07, 0xA3, 0x0E, 0x05, // 125:1955 + 0x07, 0xB1, 0x17, 0x09, // 126:1969 + 0xFF, 0xFF, 0x00, 0x00, // 127:65535 + 0xFF, 0xFF, 0x00, 0x10, // 128:65535 + 0xFF, 0xFF, 0x00, 0x10, // 129:65535 + 0xFF, 0xFF, 0x00, 0x10, // 130:65535 + 0xFF, 0xFF, 0x00, 0x10, // 131:65535 + 0xFF, 0xFF, 0x00, 0x10, // 132:65535 + 0xFF, 0xFF, 0x00, 0x10, // 133:65535 + 0xFF, 0xFF, 0x00, 0x10, // 134:65535 + 0xFF, 0xFF, 0x00, 0x10, // 135:65535 + 0xFF, 0xFF, 0x00, 0x10, // 136:65535 + 0xFF, 0xFF, 0x00, 0x10, // 137:65535 + 0xFF, 0xFF, 0x00, 0x10, // 138:65535 + 0xFF, 0xFF, 0x00, 0x10, // 139:65535 + 0xFF, 0xFF, 0x00, 0x10, // 140:65535 + 0xFF, 0xFF, 0x00, 0x10, // 141:65535 + 0xFF, 0xFF, 0x00, 0x10, // 142:65535 + 0xFF, 0xFF, 0x00, 0x10, // 143:65535 + 0xFF, 0xFF, 0x00, 0x10, // 144:65535 + 0xFF, 0xFF, 0x00, 0x10, // 145:65535 + 0xFF, 0xFF, 0x00, 0x10, // 146:65535 + 0xFF, 0xFF, 0x00, 0x10, // 147:65535 + 0xFF, 0xFF, 0x00, 0x10, // 148:65535 + 0xFF, 0xFF, 0x00, 0x10, // 149:65535 + 0xFF, 0xFF, 0x00, 0x10, // 150:65535 + 0xFF, 0xFF, 0x00, 0x10, // 151:65535 + 0xFF, 0xFF, 0x00, 0x10, // 152:65535 + 0xFF, 0xFF, 0x00, 0x10, // 153:65535 + 0xFF, 0xFF, 0x00, 0x10, // 154:65535 + 0xFF, 0xFF, 0x00, 0x10, // 155:65535 + 0xFF, 0xFF, 0x00, 0x10, // 156:65535 + 0xFF, 0xFF, 0x00, 0x10, // 157:65535 + 0xFF, 0xFF, 0x00, 0x10, // 158:65535 + 0xFF, 0xFF, 0x00, 0x10, // 159:65535 + 0xFF, 0xFF, 0x00, 0x04, // 160:65535 + 0x07, 0xC8, 0x09, 0x05, // 161:1992 + 0x07, 0xD1, 0x17, 0x09, // 162:2001 + 0x07, 0xE8, 0x17, 0x09, // 163:2024 + 0x07, 0xFF, 0x14, 0x09, // 164:2047 + 0x08, 0x13, 0x1A, 0x09, // 165:2067 + 0x08, 0x2D, 0x06, 0x04, // 166:2093 + 0x08, 0x33, 0x17, 0x09, // 167:2099 + 0x08, 0x4A, 0x07, 0x05, // 168:2122 + 0x08, 0x51, 0x23, 0x0C, // 169:2129 + 0x08, 0x74, 0x0E, 0x06, // 170:2164 + 0x08, 0x82, 0x14, 0x09, // 171:2178 + 0x08, 0x96, 0x17, 0x09, // 172:2198 + 0x08, 0xAD, 0x0B, 0x05, // 173:2221 + 0x08, 0xB8, 0x23, 0x0C, // 174:2232 + 0x08, 0xDB, 0x19, 0x09, // 175:2267 + 0x08, 0xF4, 0x0D, 0x06, // 176:2292 + 0x09, 0x01, 0x17, 0x09, // 177:2305 + 0x09, 0x18, 0x0E, 0x05, // 178:2328 + 0x09, 0x26, 0x0D, 0x05, // 179:2342 + 0x09, 0x33, 0x0A, 0x05, // 180:2355 + 0x09, 0x3D, 0x17, 0x09, // 181:2365 + 0x09, 0x54, 0x19, 0x09, // 182:2388 + 0x09, 0x6D, 0x08, 0x05, // 183:2413 + 0x09, 0x75, 0x0C, 0x05, // 184:2421 + 0x09, 0x81, 0x0B, 0x05, // 185:2433 + 0x09, 0x8C, 0x0D, 0x06, // 186:2444 + 0x09, 0x99, 0x17, 0x09, // 187:2457 + 0x09, 0xB0, 0x26, 0x0D, // 188:2480 + 0x09, 0xD6, 0x26, 0x0D, // 189:2518 + 0x09, 0xFC, 0x26, 0x0D, // 190:2556 + 0x0A, 0x22, 0x1A, 0x0A, // 191:2594 + 0x0A, 0x3C, 0x1D, 0x0B, // 192:2620 + 0x0A, 0x59, 0x1D, 0x0B, // 193:2649 + 0x0A, 0x76, 0x1D, 0x0B, // 194:2678 + 0x0A, 0x93, 0x1D, 0x0B, // 195:2707 + 0x0A, 0xB0, 0x1D, 0x0B, // 196:2736 + 0x0A, 0xCD, 0x1D, 0x0B, // 197:2765 + 0x0A, 0xEA, 0x2C, 0x10, // 198:2794 + 0x0B, 0x16, 0x20, 0x0C, // 199:2838 + 0x0B, 0x36, 0x1D, 0x0B, // 200:2870 + 0x0B, 0x53, 0x1D, 0x0B, // 201:2899 + 0x0B, 0x70, 0x1D, 0x0B, // 202:2928 + 0x0B, 0x8D, 0x1D, 0x0B, // 203:2957 + 0x0B, 0xAA, 0x05, 0x04, // 204:2986 + 0x0B, 0xAF, 0x07, 0x04, // 205:2991 + 0x0B, 0xB6, 0x0A, 0x04, // 206:2998 + 0x0B, 0xC0, 0x07, 0x04, // 207:3008 + 0x0B, 0xC7, 0x20, 0x0C, // 208:3015 + 0x0B, 0xE7, 0x1D, 0x0C, // 209:3047 + 0x0C, 0x04, 0x20, 0x0C, // 210:3076 + 0x0C, 0x24, 0x20, 0x0C, // 211:3108 + 0x0C, 0x44, 0x20, 0x0C, // 212:3140 + 0x0C, 0x64, 0x20, 0x0C, // 213:3172 + 0x0C, 0x84, 0x20, 0x0C, // 214:3204 + 0x0C, 0xA4, 0x17, 0x09, // 215:3236 + 0x0C, 0xBB, 0x20, 0x0C, // 216:3259 + 0x0C, 0xDB, 0x1D, 0x0C, // 217:3291 + 0x0C, 0xF8, 0x1D, 0x0C, // 218:3320 + 0x0D, 0x15, 0x1D, 0x0C, // 219:3349 + 0x0D, 0x32, 0x1D, 0x0C, // 220:3378 + 0x0D, 0x4F, 0x19, 0x0B, // 221:3407 + 0x0D, 0x68, 0x1D, 0x0B, // 222:3432 + 0x0D, 0x85, 0x17, 0x0A, // 223:3461 + 0x0D, 0x9C, 0x17, 0x09, // 224:3484 + 0x0D, 0xB3, 0x17, 0x09, // 225:3507 + 0x0D, 0xCA, 0x17, 0x09, // 226:3530 + 0x0D, 0xE1, 0x17, 0x09, // 227:3553 + 0x0D, 0xF8, 0x17, 0x09, // 228:3576 + 0x0E, 0x0F, 0x17, 0x09, // 229:3599 + 0x0E, 0x26, 0x29, 0x0E, // 230:3622 + 0x0E, 0x4F, 0x14, 0x08, // 231:3663 + 0x0E, 0x63, 0x17, 0x09, // 232:3683 + 0x0E, 0x7A, 0x17, 0x09, // 233:3706 + 0x0E, 0x91, 0x17, 0x09, // 234:3729 + 0x0E, 0xA8, 0x17, 0x09, // 235:3752 + 0x0E, 0xBF, 0x05, 0x04, // 236:3775 + 0x0E, 0xC4, 0x07, 0x04, // 237:3780 + 0x0E, 0xCB, 0x0A, 0x04, // 238:3787 + 0x0E, 0xD5, 0x07, 0x04, // 239:3797 + 0x0E, 0xDC, 0x17, 0x09, // 240:3804 + 0x0E, 0xF3, 0x14, 0x09, // 241:3827 + 0x0F, 0x07, 0x17, 0x09, // 242:3847 + 0x0F, 0x1E, 0x17, 0x09, // 243:3870 + 0x0F, 0x35, 0x17, 0x09, // 244:3893 + 0x0F, 0x4C, 0x17, 0x09, // 245:3916 + 0x0F, 0x63, 0x17, 0x09, // 246:3939 + 0x0F, 0x7A, 0x17, 0x09, // 247:3962 + 0x0F, 0x91, 0x17, 0x0A, // 248:3985 + 0x0F, 0xA8, 0x14, 0x09, // 249:4008 + 0x0F, 0xBC, 0x14, 0x09, // 250:4028 + 0x0F, 0xD0, 0x14, 0x09, // 251:4048 + 0x0F, 0xE4, 0x14, 0x09, // 252:4068 + 0x0F, 0xF8, 0x13, 0x08, // 253:4088 + 0x10, 0x0B, 0x17, 0x09, // 254:4107 + 0x10, 0x22, 0x13, 0x08, // 255:4130 + + // Font Data: + 0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x5F, // 33 + 0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78, // 34 + 0x80,0x08,0x00,0x80,0x78,0x00,0xC0,0x0F,0x00,0xB8,0x08,0x00,0x80,0x08,0x00,0x80,0x78,0x00,0xC0,0x0F,0x00,0xB8,0x08,0x00,0x80,0x08, // 35 + 0x00,0x00,0x00,0xE0,0x10,0x00,0x10,0x21,0x00,0x08,0x41,0x00,0xFC,0xFF,0x00,0x08,0x42,0x00,0x10,0x22,0x00,0x20,0x1C, // 36 + 0x00,0x00,0x00,0xF0,0x00,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0x08,0x61,0x00,0xF0,0x18,0x00,0x00,0x06,0x00,0xC0,0x01,0x00,0x30,0x3C,0x00,0x08,0x42,0x00,0x00,0x42,0x00,0x00,0x42,0x00,0x00,0x3C, // 37 + 0x00,0x00,0x00,0x00,0x1C,0x00,0x70,0x22,0x00,0x88,0x41,0x00,0x08,0x43,0x00,0x88,0x44,0x00,0x70,0x28,0x00,0x00,0x10,0x00,0x00,0x28,0x00,0x00,0x44, // 38 + 0x00,0x00,0x00,0x78, // 39 + 0x00,0x00,0x00,0x80,0x3F,0x00,0x70,0xC0,0x01,0x08,0x00,0x02, // 40 + 0x00,0x00,0x00,0x08,0x00,0x02,0x70,0xC0,0x01,0x80,0x3F, // 41 + 0x10,0x00,0x00,0xD0,0x00,0x00,0x38,0x00,0x00,0xD0,0x00,0x00,0x10, // 42 + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0xC0,0x1F,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02, // 43 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x01, // 44 + 0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08, // 45 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40, // 46 + 0x00,0x60,0x00,0x00,0x1E,0x00,0xE0,0x01,0x00,0x18, // 47 + 0x00,0x00,0x00,0xE0,0x1F,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0xE0,0x1F, // 48 + 0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0x00,0x10,0x00,0x00,0xF8,0x7F, // 49 + 0x00,0x00,0x00,0x20,0x40,0x00,0x10,0x60,0x00,0x08,0x50,0x00,0x08,0x48,0x00,0x08,0x44,0x00,0x10,0x43,0x00,0xE0,0x40, // 50 + 0x00,0x00,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x88,0x41,0x00,0xF0,0x22,0x00,0x00,0x1C, // 51 + 0x00,0x0C,0x00,0x00,0x0A,0x00,0x00,0x09,0x00,0xC0,0x08,0x00,0x20,0x08,0x00,0x10,0x08,0x00,0xF8,0x7F,0x00,0x00,0x08, // 52 + 0x00,0x00,0x00,0xC0,0x11,0x00,0xB8,0x20,0x00,0x88,0x40,0x00,0x88,0x40,0x00,0x88,0x40,0x00,0x08,0x21,0x00,0x08,0x1E, // 53 + 0x00,0x00,0x00,0xE0,0x1F,0x00,0x10,0x21,0x00,0x88,0x40,0x00,0x88,0x40,0x00,0x88,0x40,0x00,0x10,0x21,0x00,0x20,0x1E, // 54 + 0x00,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x78,0x00,0x08,0x07,0x00,0xC8,0x00,0x00,0x28,0x00,0x00,0x18, // 55 + 0x00,0x00,0x00,0x60,0x1C,0x00,0x90,0x22,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x90,0x22,0x00,0x60,0x1C, // 56 + 0x00,0x00,0x00,0xE0,0x11,0x00,0x10,0x22,0x00,0x08,0x44,0x00,0x08,0x44,0x00,0x08,0x44,0x00,0x10,0x22,0x00,0xE0,0x1F, // 57 + 0x00,0x00,0x00,0x40,0x40, // 58 + 0x00,0x00,0x00,0x40,0xC0,0x01, // 59 + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x05,0x00,0x00,0x05,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x40,0x10, // 60 + 0x00,0x00,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08, // 61 + 0x00,0x00,0x00,0x40,0x10,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x00,0x05,0x00,0x00,0x05,0x00,0x00,0x02, // 62 + 0x00,0x00,0x00,0x60,0x00,0x00,0x10,0x00,0x00,0x08,0x00,0x00,0x08,0x5C,0x00,0x08,0x02,0x00,0x10,0x01,0x00,0xE0, // 63 + 0x00,0x00,0x00,0x00,0x3F,0x00,0xC0,0x40,0x00,0x20,0x80,0x00,0x10,0x1E,0x01,0x10,0x21,0x01,0x88,0x40,0x02,0x48,0x40,0x02,0x48,0x40,0x02,0x48,0x20,0x02,0x88,0x7C,0x02,0xC8,0x43,0x02,0x10,0x40,0x02,0x10,0x20,0x01,0x60,0x10,0x01,0x80,0x8F, // 64 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x70,0x04,0x00,0x08,0x04,0x00,0x70,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 65 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x90,0x22,0x00,0x60,0x1C, // 66 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10, // 67 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 68 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 69 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08, // 70 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x10,0x22,0x00,0x20,0x12,0x00,0x00,0x0E, // 71 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0xF8,0x7F, // 72 + 0x00,0x00,0x00,0xF8,0x7F, // 73 + 0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0xF8,0x3F, // 74 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x04,0x00,0x00,0x02,0x00,0x00,0x01,0x00,0x80,0x03,0x00,0x40,0x04,0x00,0x20,0x18,0x00,0x10,0x20,0x00,0x08,0x40, // 75 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40, // 76 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x30,0x00,0x00,0xC0,0x00,0x00,0x00,0x03,0x00,0x00,0x1C,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x00,0x03,0x00,0xC0,0x00,0x00,0x30,0x00,0x00,0xF8,0x7F, // 77 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x10,0x00,0x00,0x60,0x00,0x00,0x80,0x00,0x00,0x00,0x03,0x00,0x00,0x04,0x00,0x00,0x18,0x00,0x00,0x20,0x00,0xF8,0x7F, // 78 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 79 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x10,0x01,0x00,0xE0, // 80 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x50,0x00,0x08,0x50,0x00,0x10,0x20,0x00,0x20,0x70,0x00,0xC0,0x4F, // 81 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x06,0x00,0x08,0x1A,0x00,0x10,0x21,0x00,0xE0,0x40, // 82 + 0x00,0x00,0x00,0x60,0x10,0x00,0x90,0x20,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x10,0x22,0x00,0x20,0x1C, // 83 + 0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0xF8,0x7F,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08, // 84 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 85 + 0x00,0x00,0x00,0x18,0x00,0x00,0xE0,0x00,0x00,0x00,0x07,0x00,0x00,0x18,0x00,0x00,0x60,0x00,0x00,0x18,0x00,0x00,0x07,0x00,0xE0,0x00,0x00,0x18, // 86 + 0x18,0x00,0x00,0xE0,0x01,0x00,0x00,0x1E,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x03,0x00,0x70,0x00,0x00,0x08,0x00,0x00,0x70,0x00,0x00,0x80,0x03,0x00,0x00,0x1C,0x00,0x00,0x60,0x00,0x00,0x1E,0x00,0xE0,0x01,0x00,0x18, // 87 + 0x00,0x40,0x00,0x08,0x20,0x00,0x10,0x10,0x00,0x60,0x0C,0x00,0x80,0x02,0x00,0x00,0x01,0x00,0x80,0x02,0x00,0x60,0x0C,0x00,0x10,0x10,0x00,0x08,0x20,0x00,0x00,0x40, // 88 + 0x08,0x00,0x00,0x30,0x00,0x00,0x40,0x00,0x00,0x80,0x01,0x00,0x00,0x7E,0x00,0x80,0x01,0x00,0x40,0x00,0x00,0x30,0x00,0x00,0x08, // 89 + 0x00,0x40,0x00,0x08,0x60,0x00,0x08,0x58,0x00,0x08,0x44,0x00,0x08,0x43,0x00,0x88,0x40,0x00,0x68,0x40,0x00,0x18,0x40,0x00,0x08,0x40, // 90 + 0x00,0x00,0x00,0xF8,0xFF,0x03,0x08,0x00,0x02,0x08,0x00,0x02, // 91 + 0x18,0x00,0x00,0xE0,0x01,0x00,0x00,0x1E,0x00,0x00,0x60, // 92 + 0x08,0x00,0x02,0x08,0x00,0x02,0xF8,0xFF,0x03, // 93 + 0x00,0x01,0x00,0xC0,0x00,0x00,0x30,0x00,0x00,0x08,0x00,0x00,0x30,0x00,0x00,0xC0,0x00,0x00,0x00,0x01, // 94 + 0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02, // 95 + 0x00,0x00,0x00,0x08,0x00,0x00,0x10, // 96 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 97 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 98 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20, // 99 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0xF8,0x7F, // 100 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 101 + 0x40,0x00,0x00,0xF0,0x7F,0x00,0x48,0x00,0x00,0x48, // 102 + 0x00,0x00,0x00,0x00,0x1F,0x01,0x80,0x20,0x02,0x40,0x40,0x02,0x40,0x40,0x02,0x40,0x40,0x02,0x80,0x20,0x01,0xC0,0xFF, // 103 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x7F, // 104 + 0x00,0x00,0x00,0xC8,0x7F, // 105 + 0x00,0x00,0x02,0xC8,0xFF,0x01, // 106 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x08,0x00,0x00,0x04,0x00,0x00,0x06,0x00,0x00,0x19,0x00,0x80,0x20,0x00,0x40,0x40, // 107 + 0x00,0x00,0x00,0xF8,0x7F, // 108 + 0x00,0x00,0x00,0xC0,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x7F, // 109 + 0x00,0x00,0x00,0xC0,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x7F, // 110 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 111 + 0x00,0x00,0x00,0xC0,0xFF,0x03,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 112 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0xC0,0xFF,0x03, // 113 + 0x00,0x00,0x00,0xC0,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40, // 114 + 0x00,0x00,0x00,0x80,0x23,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x80,0x38, // 115 + 0x40,0x00,0x00,0xF0,0x7F,0x00,0x40,0x40,0x00,0x40,0x40, // 116 + 0x00,0x00,0x00,0xC0,0x3F,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xC0,0x7F, // 117 + 0xC0,0x00,0x00,0x00,0x03,0x00,0x00,0x1C,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x00,0x03,0x00,0xC0, // 118 + 0xC0,0x00,0x00,0x00,0x1F,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x00,0x03,0x00,0xC0,0x00,0x00,0x00,0x03,0x00,0x00,0x1C,0x00,0x00,0x60,0x00,0x00,0x1F,0x00,0xC0, // 119 + 0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1B,0x00,0x00,0x04,0x00,0x00,0x1B,0x00,0x80,0x20,0x00,0x40,0x40, // 120 + 0xC0,0x01,0x00,0x00,0x06,0x02,0x00,0x38,0x02,0x00,0xE0,0x01,0x00,0x38,0x00,0x00,0x07,0x00,0xC0, // 121 + 0x40,0x40,0x00,0x40,0x60,0x00,0x40,0x58,0x00,0x40,0x44,0x00,0x40,0x43,0x00,0xC0,0x40,0x00,0x40,0x40, // 122 + 0x00,0x04,0x00,0x00,0x04,0x00,0xF0,0xFB,0x01,0x08,0x00,0x02,0x08,0x00,0x02, // 123 + 0x00,0x00,0x00,0xF8,0xFF,0x03, // 124 + 0x08,0x00,0x02,0x08,0x00,0x02,0xF0,0xFB,0x01,0x00,0x04,0x00,0x00,0x04, // 125 + 0x00,0x02,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x01, // 126 + 0x00,0x00,0x00,0x00,0x00,0x00,0x40,0xFF,0x03, // 161 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x03,0x40,0xF0,0x00,0x40,0x4E,0x00,0xC0,0x41,0x00,0xB8,0x20,0x00,0x00,0x11, // 162 + 0x00,0x41,0x00,0xE0,0x31,0x00,0x10,0x2F,0x00,0x08,0x21,0x00,0x08,0x21,0x00,0x08,0x40,0x00,0x10,0x40,0x00,0x20,0x20, // 163 + 0x00,0x00,0x00,0x40,0x0B,0x00,0x80,0x04,0x00,0x40,0x08,0x00,0x40,0x08,0x00,0x80,0x04,0x00,0x40,0x0B, // 164 + 0x08,0x0A,0x00,0x10,0x0A,0x00,0x60,0x0A,0x00,0x80,0x0B,0x00,0x00,0x7E,0x00,0x80,0x0B,0x00,0x60,0x0A,0x00,0x10,0x0A,0x00,0x08,0x0A, // 165 + 0x00,0x00,0x00,0xF8,0xF1,0x03, // 166 + 0x00,0x86,0x00,0x70,0x09,0x01,0xC8,0x10,0x02,0x88,0x10,0x02,0x08,0x21,0x02,0x08,0x61,0x02,0x30,0xD2,0x01,0x00,0x0C, // 167 + 0x08,0x00,0x00,0x00,0x00,0x00,0x08, // 168 + 0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0xC8,0x47,0x00,0x28,0x48,0x00,0x28,0x48,0x00,0x28,0x48,0x00,0x28,0x48,0x00,0x48,0x44,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 169 + 0xD0,0x00,0x00,0x48,0x01,0x00,0x28,0x01,0x00,0x28,0x01,0x00,0xF0,0x01, // 170 + 0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x1B,0x00,0x80,0x20,0x00,0x00,0x04,0x00,0x00,0x1B,0x00,0x80,0x20, // 171 + 0x00,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x0F, // 172 + 0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08, // 173 + 0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0xE8,0x4F,0x00,0x28,0x41,0x00,0x28,0x41,0x00,0x28,0x43,0x00,0x28,0x45,0x00,0xC8,0x48,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 174 + 0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04, // 175 + 0x00,0x00,0x00,0x30,0x00,0x00,0x48,0x00,0x00,0x48,0x00,0x00,0x30, // 176 + 0x00,0x00,0x00,0x00,0x41,0x00,0x00,0x41,0x00,0x00,0x41,0x00,0xE0,0x4F,0x00,0x00,0x41,0x00,0x00,0x41,0x00,0x00,0x41, // 177 + 0x10,0x01,0x00,0x88,0x01,0x00,0x48,0x01,0x00,0x48,0x01,0x00,0x30,0x01, // 178 + 0x90,0x00,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0x28,0x01,0x00,0xD8, // 179 + 0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x08, // 180 + 0x00,0x00,0x00,0xC0,0xFF,0x03,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xC0,0x7F, // 181 + 0xF0,0x00,0x00,0xF8,0x00,0x00,0xF8,0x01,0x00,0xF8,0x01,0x00,0xF8,0xFF,0x03,0x08,0x00,0x00,0x08,0x00,0x00,0xF8,0xFF,0x03,0x08, // 182 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02, // 183 + 0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x80,0x02,0x00,0x00,0x03, // 184 + 0x00,0x00,0x00,0x10,0x00,0x00,0x08,0x00,0x00,0xF8,0x01, // 185 + 0xF0,0x00,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0xF0, // 186 + 0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x20,0x00,0x00,0x1B,0x00,0x00,0x04,0x00,0x80,0x20,0x00,0x00,0x1B,0x00,0x00,0x04, // 187 + 0x00,0x00,0x00,0x10,0x00,0x00,0x08,0x40,0x00,0xF8,0x21,0x00,0x00,0x10,0x00,0x00,0x0C,0x00,0x00,0x02,0x00,0x80,0x01,0x00,0x40,0x30,0x00,0x30,0x28,0x00,0x08,0x24,0x00,0x00,0x7E,0x00,0x00,0x20, // 188 + 0x00,0x00,0x00,0x10,0x00,0x00,0x08,0x40,0x00,0xF8,0x31,0x00,0x00,0x08,0x00,0x00,0x04,0x00,0x00,0x03,0x00,0x80,0x00,0x00,0x60,0x44,0x00,0x10,0x62,0x00,0x08,0x52,0x00,0x00,0x52,0x00,0x00,0x4C, // 189 + 0x90,0x00,0x00,0x08,0x01,0x00,0x08,0x41,0x00,0x28,0x21,0x00,0xD8,0x18,0x00,0x00,0x04,0x00,0x00,0x03,0x00,0x80,0x00,0x00,0x40,0x30,0x00,0x30,0x28,0x00,0x08,0x24,0x00,0x00,0x7E,0x00,0x00,0x20, // 190 + 0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x10,0x01,0x00,0x08,0x02,0x40,0x07,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x01,0x00,0xC0, // 191 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x71,0x04,0x00,0x0A,0x04,0x00,0x70,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 192 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x70,0x04,0x00,0x0A,0x04,0x00,0x71,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 193 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x72,0x04,0x00,0x09,0x04,0x00,0x71,0x04,0x00,0x82,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 194 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x72,0x04,0x00,0x09,0x04,0x00,0x72,0x04,0x00,0x81,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 195 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x72,0x04,0x00,0x08,0x04,0x00,0x72,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 196 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x7E,0x04,0x00,0x0A,0x04,0x00,0x7E,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 197 + 0x00,0x60,0x00,0x00,0x18,0x00,0x00,0x06,0x00,0x80,0x05,0x00,0x60,0x04,0x00,0x18,0x04,0x00,0x08,0x04,0x00,0x08,0x04,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41, // 198 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x02,0x08,0xC0,0x02,0x08,0x40,0x03,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10, // 199 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x09,0x41,0x00,0x0A,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 200 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x0A,0x41,0x00,0x09,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 201 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x0A,0x41,0x00,0x09,0x41,0x00,0x09,0x41,0x00,0x0A,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 202 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x0A,0x41,0x00,0x08,0x41,0x00,0x0A,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 203 + 0x01,0x00,0x00,0xFA,0x7F, // 204 + 0x00,0x00,0x00,0xFA,0x7F,0x00,0x01, // 205 + 0x02,0x00,0x00,0xF9,0x7F,0x00,0x01,0x00,0x00,0x02, // 206 + 0x02,0x00,0x00,0xF8,0x7F,0x00,0x02, // 207 + 0x00,0x02,0x00,0xF8,0x7F,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 208 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x10,0x00,0x00,0x60,0x00,0x00,0x82,0x00,0x00,0x01,0x03,0x00,0x02,0x04,0x00,0x01,0x18,0x00,0x00,0x20,0x00,0xF8,0x7F, // 209 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x09,0x40,0x00,0x0A,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 210 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x0A,0x40,0x00,0x09,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 211 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x0A,0x40,0x00,0x09,0x40,0x00,0x09,0x40,0x00,0x0A,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 212 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x0A,0x40,0x00,0x09,0x40,0x00,0x0A,0x40,0x00,0x09,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 213 + 0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x0A,0x40,0x00,0x08,0x40,0x00,0x0A,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 214 + 0x00,0x00,0x00,0x40,0x10,0x00,0x80,0x08,0x00,0x00,0x05,0x00,0x00,0x07,0x00,0x00,0x05,0x00,0x80,0x08,0x00,0x40,0x10, // 215 + 0x00,0x00,0x00,0xC0,0x4F,0x00,0x20,0x30,0x00,0x10,0x30,0x00,0x08,0x4C,0x00,0x08,0x42,0x00,0x08,0x41,0x00,0xC8,0x40,0x00,0x30,0x20,0x00,0x30,0x10,0x00,0xC8,0x0F, // 216 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x01,0x40,0x00,0x02,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 217 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x02,0x40,0x00,0x01,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 218 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x02,0x40,0x00,0x01,0x40,0x00,0x01,0x40,0x00,0x02,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 219 + 0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x02,0x40,0x00,0x00,0x40,0x00,0x02,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 220 + 0x08,0x00,0x00,0x30,0x00,0x00,0x40,0x00,0x00,0x80,0x01,0x00,0x02,0x7E,0x00,0x81,0x01,0x00,0x40,0x00,0x00,0x30,0x00,0x00,0x08, // 221 + 0x00,0x00,0x00,0xF8,0x7F,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x40,0x08,0x00,0x80,0x07, // 222 + 0x00,0x00,0x00,0xE0,0x7F,0x00,0x10,0x00,0x00,0x08,0x20,0x00,0x88,0x43,0x00,0x70,0x42,0x00,0x00,0x44,0x00,0x00,0x38, // 223 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x40,0x44,0x00,0x48,0x44,0x00,0x50,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 224 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x40,0x44,0x00,0x50,0x44,0x00,0x48,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 225 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x50,0x44,0x00,0x48,0x44,0x00,0x48,0x42,0x00,0x50,0x22,0x00,0x80,0x7F, // 226 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x50,0x44,0x00,0x48,0x44,0x00,0x50,0x42,0x00,0x48,0x22,0x00,0x80,0x7F, // 227 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x50,0x44,0x00,0x40,0x44,0x00,0x50,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 228 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x5C,0x44,0x00,0x54,0x44,0x00,0x5C,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 229 + 0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x42,0x00,0x40,0x22,0x00,0x80,0x3F,0x00,0x80,0x24,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 230 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x02,0x40,0xC0,0x02,0x40,0x40,0x03,0x80,0x20, // 231 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x48,0x44,0x00,0x50,0x44,0x00,0x40,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 232 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x40,0x44,0x00,0x50,0x44,0x00,0x48,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 233 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x50,0x44,0x00,0x48,0x44,0x00,0x48,0x44,0x00,0x90,0x24,0x00,0x00,0x17, // 234 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x50,0x44,0x00,0x40,0x44,0x00,0x50,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 235 + 0x08,0x00,0x00,0xD0,0x7F, // 236 + 0x00,0x00,0x00,0xD0,0x7F,0x00,0x08, // 237 + 0x10,0x00,0x00,0xC8,0x7F,0x00,0x08,0x00,0x00,0x10, // 238 + 0x10,0x00,0x00,0xC0,0x7F,0x00,0x10, // 239 + 0x00,0x00,0x00,0x00,0x1F,0x00,0xA0,0x20,0x00,0x68,0x40,0x00,0x58,0x40,0x00,0x70,0x40,0x00,0xE8,0x20,0x00,0x00,0x1F, // 240 + 0x00,0x00,0x00,0xC0,0x7F,0x00,0x90,0x00,0x00,0x48,0x00,0x00,0x50,0x00,0x00,0x48,0x00,0x00,0x80,0x7F, // 241 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x48,0x40,0x00,0x50,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 242 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x50,0x40,0x00,0x48,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 243 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x50,0x40,0x00,0x48,0x40,0x00,0x48,0x40,0x00,0x90,0x20,0x00,0x00,0x1F, // 244 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x50,0x40,0x00,0x48,0x40,0x00,0x50,0x40,0x00,0x88,0x20,0x00,0x00,0x1F, // 245 + 0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x50,0x40,0x00,0x40,0x40,0x00,0x50,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 246 + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x80,0x0A,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02, // 247 + 0x00,0x00,0x00,0x00,0x5F,0x00,0x80,0x30,0x00,0x40,0x48,0x00,0x40,0x44,0x00,0x40,0x42,0x00,0x80,0x21,0x00,0x40,0x1F, // 248 + 0x00,0x00,0x00,0xC0,0x3F,0x00,0x00,0x40,0x00,0x08,0x40,0x00,0x10,0x40,0x00,0x00,0x20,0x00,0xC0,0x7F, // 249 + 0x00,0x00,0x00,0xC0,0x3F,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x10,0x40,0x00,0x08,0x20,0x00,0xC0,0x7F, // 250 + 0x00,0x00,0x00,0xC0,0x3F,0x00,0x10,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0xC0,0x7F, // 251 + 0x00,0x00,0x00,0xD0,0x3F,0x00,0x00,0x40,0x00,0x10,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xC0,0x7F, // 252 + 0xC0,0x01,0x00,0x00,0x06,0x02,0x00,0x38,0x02,0x10,0xE0,0x01,0x08,0x38,0x00,0x00,0x07,0x00,0xC0, // 253 + 0x00,0x00,0x00,0xF8,0xFF,0x03,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 254 + 0xC0,0x01,0x00,0x00,0x06,0x02,0x10,0x38,0x02,0x00,0xE0,0x01,0x10,0x38,0x00,0x00,0x07,0x00,0xC0 // 255 +}; +const uint8_t ArialMT_Plain_24[] PROGMEM = { + 0x18, // Width: 24 + 0x1C, // Height: 28 + 0x20, // First Char: 32 + 0xE0, // Numbers of Chars: 224 + + // Jump Table: + 0xFF, 0xFF, 0x00, 0x07, // 32:65535 + 0x00, 0x00, 0x13, 0x07, // 33:0 + 0x00, 0x13, 0x1A, 0x09, // 34:19 + 0x00, 0x2D, 0x33, 0x0D, // 35:45 + 0x00, 0x60, 0x2F, 0x0D, // 36:96 + 0x00, 0x8F, 0x4F, 0x15, // 37:143 + 0x00, 0xDE, 0x3B, 0x10, // 38:222 + 0x01, 0x19, 0x0A, 0x05, // 39:281 + 0x01, 0x23, 0x1C, 0x08, // 40:291 + 0x01, 0x3F, 0x1B, 0x08, // 41:319 + 0x01, 0x5A, 0x21, 0x09, // 42:346 + 0x01, 0x7B, 0x32, 0x0E, // 43:379 + 0x01, 0xAD, 0x10, 0x07, // 44:429 + 0x01, 0xBD, 0x1B, 0x08, // 45:445 + 0x01, 0xD8, 0x0F, 0x07, // 46:472 + 0x01, 0xE7, 0x19, 0x07, // 47:487 + 0x02, 0x00, 0x2F, 0x0D, // 48:512 + 0x02, 0x2F, 0x23, 0x0D, // 49:559 + 0x02, 0x52, 0x2F, 0x0D, // 50:594 + 0x02, 0x81, 0x2F, 0x0D, // 51:641 + 0x02, 0xB0, 0x2F, 0x0D, // 52:688 + 0x02, 0xDF, 0x2F, 0x0D, // 53:735 + 0x03, 0x0E, 0x2F, 0x0D, // 54:782 + 0x03, 0x3D, 0x2D, 0x0D, // 55:829 + 0x03, 0x6A, 0x2F, 0x0D, // 56:874 + 0x03, 0x99, 0x2F, 0x0D, // 57:921 + 0x03, 0xC8, 0x0F, 0x07, // 58:968 + 0x03, 0xD7, 0x10, 0x07, // 59:983 + 0x03, 0xE7, 0x2F, 0x0E, // 60:999 + 0x04, 0x16, 0x2F, 0x0E, // 61:1046 + 0x04, 0x45, 0x2E, 0x0E, // 62:1093 + 0x04, 0x73, 0x2E, 0x0D, // 63:1139 + 0x04, 0xA1, 0x5B, 0x18, // 64:1185 + 0x04, 0xFC, 0x3B, 0x10, // 65:1276 + 0x05, 0x37, 0x3B, 0x10, // 66:1335 + 0x05, 0x72, 0x3F, 0x11, // 67:1394 + 0x05, 0xB1, 0x3F, 0x11, // 68:1457 + 0x05, 0xF0, 0x3B, 0x10, // 69:1520 + 0x06, 0x2B, 0x35, 0x0F, // 70:1579 + 0x06, 0x60, 0x43, 0x13, // 71:1632 + 0x06, 0xA3, 0x3B, 0x11, // 72:1699 + 0x06, 0xDE, 0x0F, 0x07, // 73:1758 + 0x06, 0xED, 0x27, 0x0C, // 74:1773 + 0x07, 0x14, 0x3F, 0x10, // 75:1812 + 0x07, 0x53, 0x2F, 0x0D, // 76:1875 + 0x07, 0x82, 0x43, 0x14, // 77:1922 + 0x07, 0xC5, 0x3B, 0x11, // 78:1989 + 0x08, 0x00, 0x47, 0x13, // 79:2048 + 0x08, 0x47, 0x3A, 0x10, // 80:2119 + 0x08, 0x81, 0x47, 0x13, // 81:2177 + 0x08, 0xC8, 0x3F, 0x11, // 82:2248 + 0x09, 0x07, 0x3B, 0x10, // 83:2311 + 0x09, 0x42, 0x35, 0x0F, // 84:2370 + 0x09, 0x77, 0x3B, 0x11, // 85:2423 + 0x09, 0xB2, 0x39, 0x10, // 86:2482 + 0x09, 0xEB, 0x59, 0x17, // 87:2539 + 0x0A, 0x44, 0x3B, 0x10, // 88:2628 + 0x0A, 0x7F, 0x3D, 0x10, // 89:2687 + 0x0A, 0xBC, 0x37, 0x0F, // 90:2748 + 0x0A, 0xF3, 0x14, 0x07, // 91:2803 + 0x0B, 0x07, 0x1B, 0x07, // 92:2823 + 0x0B, 0x22, 0x18, 0x07, // 93:2850 + 0x0B, 0x3A, 0x2A, 0x0B, // 94:2874 + 0x0B, 0x64, 0x34, 0x0D, // 95:2916 + 0x0B, 0x98, 0x11, 0x08, // 96:2968 + 0x0B, 0xA9, 0x2F, 0x0D, // 97:2985 + 0x0B, 0xD8, 0x33, 0x0D, // 98:3032 + 0x0C, 0x0B, 0x2B, 0x0C, // 99:3083 + 0x0C, 0x36, 0x2F, 0x0D, // 100:3126 + 0x0C, 0x65, 0x2F, 0x0D, // 101:3173 + 0x0C, 0x94, 0x1A, 0x07, // 102:3220 + 0x0C, 0xAE, 0x2F, 0x0D, // 103:3246 + 0x0C, 0xDD, 0x2F, 0x0D, // 104:3293 + 0x0D, 0x0C, 0x0F, 0x05, // 105:3340 + 0x0D, 0x1B, 0x10, 0x05, // 106:3355 + 0x0D, 0x2B, 0x2F, 0x0C, // 107:3371 + 0x0D, 0x5A, 0x0F, 0x05, // 108:3418 + 0x0D, 0x69, 0x47, 0x14, // 109:3433 + 0x0D, 0xB0, 0x2F, 0x0D, // 110:3504 + 0x0D, 0xDF, 0x2F, 0x0D, // 111:3551 + 0x0E, 0x0E, 0x33, 0x0D, // 112:3598 + 0x0E, 0x41, 0x30, 0x0D, // 113:3649 + 0x0E, 0x71, 0x1E, 0x08, // 114:3697 + 0x0E, 0x8F, 0x2B, 0x0C, // 115:3727 + 0x0E, 0xBA, 0x1B, 0x07, // 116:3770 + 0x0E, 0xD5, 0x2F, 0x0D, // 117:3797 + 0x0F, 0x04, 0x2A, 0x0C, // 118:3844 + 0x0F, 0x2E, 0x42, 0x11, // 119:3886 + 0x0F, 0x70, 0x2B, 0x0C, // 120:3952 + 0x0F, 0x9B, 0x2A, 0x0C, // 121:3995 + 0x0F, 0xC5, 0x2B, 0x0C, // 122:4037 + 0x0F, 0xF0, 0x1C, 0x08, // 123:4080 + 0x10, 0x0C, 0x10, 0x06, // 124:4108 + 0x10, 0x1C, 0x1B, 0x08, // 125:4124 + 0x10, 0x37, 0x32, 0x0E, // 126:4151 + 0xFF, 0xFF, 0x00, 0x00, // 127:65535 + 0xFF, 0xFF, 0x00, 0x18, // 128:65535 + 0xFF, 0xFF, 0x00, 0x18, // 129:65535 + 0xFF, 0xFF, 0x00, 0x18, // 130:65535 + 0xFF, 0xFF, 0x00, 0x18, // 131:65535 + 0xFF, 0xFF, 0x00, 0x18, // 132:65535 + 0xFF, 0xFF, 0x00, 0x18, // 133:65535 + 0xFF, 0xFF, 0x00, 0x18, // 134:65535 + 0xFF, 0xFF, 0x00, 0x18, // 135:65535 + 0xFF, 0xFF, 0x00, 0x18, // 136:65535 + 0xFF, 0xFF, 0x00, 0x18, // 137:65535 + 0xFF, 0xFF, 0x00, 0x18, // 138:65535 + 0xFF, 0xFF, 0x00, 0x18, // 139:65535 + 0xFF, 0xFF, 0x00, 0x18, // 140:65535 + 0xFF, 0xFF, 0x00, 0x18, // 141:65535 + 0xFF, 0xFF, 0x00, 0x18, // 142:65535 + 0xFF, 0xFF, 0x00, 0x18, // 143:65535 + 0xFF, 0xFF, 0x00, 0x18, // 144:65535 + 0xFF, 0xFF, 0x00, 0x18, // 145:65535 + 0xFF, 0xFF, 0x00, 0x18, // 146:65535 + 0xFF, 0xFF, 0x00, 0x18, // 147:65535 + 0xFF, 0xFF, 0x00, 0x18, // 148:65535 + 0xFF, 0xFF, 0x00, 0x18, // 149:65535 + 0xFF, 0xFF, 0x00, 0x18, // 150:65535 + 0xFF, 0xFF, 0x00, 0x18, // 151:65535 + 0xFF, 0xFF, 0x00, 0x18, // 152:65535 + 0xFF, 0xFF, 0x00, 0x18, // 153:65535 + 0xFF, 0xFF, 0x00, 0x18, // 154:65535 + 0xFF, 0xFF, 0x00, 0x18, // 155:65535 + 0xFF, 0xFF, 0x00, 0x18, // 156:65535 + 0xFF, 0xFF, 0x00, 0x18, // 157:65535 + 0xFF, 0xFF, 0x00, 0x18, // 158:65535 + 0xFF, 0xFF, 0x00, 0x18, // 159:65535 + 0xFF, 0xFF, 0x00, 0x07, // 160:65535 + 0x10, 0x69, 0x14, 0x08, // 161:4201 + 0x10, 0x7D, 0x2B, 0x0D, // 162:4221 + 0x10, 0xA8, 0x2F, 0x0D, // 163:4264 + 0x10, 0xD7, 0x33, 0x0D, // 164:4311 + 0x11, 0x0A, 0x31, 0x0D, // 165:4362 + 0x11, 0x3B, 0x10, 0x06, // 166:4411 + 0x11, 0x4B, 0x2F, 0x0D, // 167:4427 + 0x11, 0x7A, 0x19, 0x08, // 168:4474 + 0x11, 0x93, 0x46, 0x12, // 169:4499 + 0x11, 0xD9, 0x1A, 0x09, // 170:4569 + 0x11, 0xF3, 0x27, 0x0D, // 171:4595 + 0x12, 0x1A, 0x2F, 0x0E, // 172:4634 + 0x12, 0x49, 0x1B, 0x08, // 173:4681 + 0x12, 0x64, 0x46, 0x12, // 174:4708 + 0x12, 0xAA, 0x31, 0x0D, // 175:4778 + 0x12, 0xDB, 0x1E, 0x0A, // 176:4827 + 0x12, 0xF9, 0x33, 0x0D, // 177:4857 + 0x13, 0x2C, 0x1A, 0x08, // 178:4908 + 0x13, 0x46, 0x1A, 0x08, // 179:4934 + 0x13, 0x60, 0x19, 0x08, // 180:4960 + 0x13, 0x79, 0x2F, 0x0E, // 181:4985 + 0x13, 0xA8, 0x31, 0x0D, // 182:5032 + 0x13, 0xD9, 0x12, 0x08, // 183:5081 + 0x13, 0xEB, 0x18, 0x08, // 184:5099 + 0x14, 0x03, 0x16, 0x08, // 185:5123 + 0x14, 0x19, 0x1E, 0x09, // 186:5145 + 0x14, 0x37, 0x2E, 0x0D, // 187:5175 + 0x14, 0x65, 0x4F, 0x14, // 188:5221 + 0x14, 0xB4, 0x4B, 0x14, // 189:5300 + 0x14, 0xFF, 0x4B, 0x14, // 190:5375 + 0x15, 0x4A, 0x33, 0x0F, // 191:5450 + 0x15, 0x7D, 0x3B, 0x10, // 192:5501 + 0x15, 0xB8, 0x3B, 0x10, // 193:5560 + 0x15, 0xF3, 0x3B, 0x10, // 194:5619 + 0x16, 0x2E, 0x3B, 0x10, // 195:5678 + 0x16, 0x69, 0x3B, 0x10, // 196:5737 + 0x16, 0xA4, 0x3B, 0x10, // 197:5796 + 0x16, 0xDF, 0x5B, 0x18, // 198:5855 + 0x17, 0x3A, 0x3F, 0x11, // 199:5946 + 0x17, 0x79, 0x3B, 0x10, // 200:6009 + 0x17, 0xB4, 0x3B, 0x10, // 201:6068 + 0x17, 0xEF, 0x3B, 0x10, // 202:6127 + 0x18, 0x2A, 0x3B, 0x10, // 203:6186 + 0x18, 0x65, 0x11, 0x07, // 204:6245 + 0x18, 0x76, 0x11, 0x07, // 205:6262 + 0x18, 0x87, 0x15, 0x07, // 206:6279 + 0x18, 0x9C, 0x15, 0x07, // 207:6300 + 0x18, 0xB1, 0x3F, 0x11, // 208:6321 + 0x18, 0xF0, 0x3B, 0x11, // 209:6384 + 0x19, 0x2B, 0x47, 0x13, // 210:6443 + 0x19, 0x72, 0x47, 0x13, // 211:6514 + 0x19, 0xB9, 0x47, 0x13, // 212:6585 + 0x1A, 0x00, 0x47, 0x13, // 213:6656 + 0x1A, 0x47, 0x47, 0x13, // 214:6727 + 0x1A, 0x8E, 0x2B, 0x0E, // 215:6798 + 0x1A, 0xB9, 0x47, 0x13, // 216:6841 + 0x1B, 0x00, 0x3B, 0x11, // 217:6912 + 0x1B, 0x3B, 0x3B, 0x11, // 218:6971 + 0x1B, 0x76, 0x3B, 0x11, // 219:7030 + 0x1B, 0xB1, 0x3B, 0x11, // 220:7089 + 0x1B, 0xEC, 0x3D, 0x10, // 221:7148 + 0x1C, 0x29, 0x3A, 0x10, // 222:7209 + 0x1C, 0x63, 0x37, 0x0F, // 223:7267 + 0x1C, 0x9A, 0x2F, 0x0D, // 224:7322 + 0x1C, 0xC9, 0x2F, 0x0D, // 225:7369 + 0x1C, 0xF8, 0x2F, 0x0D, // 226:7416 + 0x1D, 0x27, 0x2F, 0x0D, // 227:7463 + 0x1D, 0x56, 0x2F, 0x0D, // 228:7510 + 0x1D, 0x85, 0x2F, 0x0D, // 229:7557 + 0x1D, 0xB4, 0x53, 0x15, // 230:7604 + 0x1E, 0x07, 0x2B, 0x0C, // 231:7687 + 0x1E, 0x32, 0x2F, 0x0D, // 232:7730 + 0x1E, 0x61, 0x2F, 0x0D, // 233:7777 + 0x1E, 0x90, 0x2F, 0x0D, // 234:7824 + 0x1E, 0xBF, 0x2F, 0x0D, // 235:7871 + 0x1E, 0xEE, 0x11, 0x07, // 236:7918 + 0x1E, 0xFF, 0x11, 0x07, // 237:7935 + 0x1F, 0x10, 0x15, 0x07, // 238:7952 + 0x1F, 0x25, 0x15, 0x07, // 239:7973 + 0x1F, 0x3A, 0x2F, 0x0D, // 240:7994 + 0x1F, 0x69, 0x2F, 0x0D, // 241:8041 + 0x1F, 0x98, 0x2F, 0x0D, // 242:8088 + 0x1F, 0xC7, 0x2F, 0x0D, // 243:8135 + 0x1F, 0xF6, 0x2F, 0x0D, // 244:8182 + 0x20, 0x25, 0x2F, 0x0D, // 245:8229 + 0x20, 0x54, 0x2F, 0x0D, // 246:8276 + 0x20, 0x83, 0x32, 0x0D, // 247:8323 + 0x20, 0xB5, 0x33, 0x0F, // 248:8373 + 0x20, 0xE8, 0x2F, 0x0D, // 249:8424 + 0x21, 0x17, 0x2F, 0x0D, // 250:8471 + 0x21, 0x46, 0x2F, 0x0D, // 251:8518 + 0x21, 0x75, 0x2F, 0x0D, // 252:8565 + 0x21, 0xA4, 0x2A, 0x0C, // 253:8612 + 0x21, 0xCE, 0x2F, 0x0D, // 254:8654 + 0x21, 0xFD, 0x2A, 0x0C, // 255:8701 + + // Font Data: + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x33,0x00,0xE0,0xFF,0x33, // 33 + 0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xE0,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xE0,0x07, // 34 + 0x00,0x0C,0x03,0x00,0x00,0x0C,0x33,0x00,0x00,0x0C,0x3F,0x00,0x00,0xFC,0x0F,0x00,0x80,0xFF,0x03,0x00,0xE0,0x0F,0x03,0x00,0x60,0x0C,0x33,0x00,0x00,0x0C,0x3F,0x00,0x00,0xFC,0x0F,0x00,0x80,0xFF,0x03,0x00,0xE0,0x0F,0x03,0x00,0x60,0x0C,0x03,0x00,0x00,0x0C,0x03, // 35 + 0x00,0x00,0x00,0x00,0x80,0x07,0x06,0x00,0xC0,0x0F,0x1E,0x00,0xC0,0x18,0x1C,0x00,0x60,0x18,0x38,0x00,0x60,0x30,0x30,0x00,0xF0,0xFF,0xFF,0x00,0x60,0x30,0x30,0x00,0x60,0x60,0x38,0x00,0xC0,0x60,0x18,0x00,0xC0,0xC1,0x1F,0x00,0x00,0x81,0x07, // 36 + 0x00,0x00,0x00,0x00,0x80,0x0F,0x00,0x00,0xC0,0x1F,0x00,0x00,0x60,0x30,0x00,0x00,0x20,0x20,0x00,0x00,0x20,0x20,0x20,0x00,0x60,0x30,0x38,0x00,0xC0,0x1F,0x1E,0x00,0x80,0x8F,0x0F,0x00,0x00,0xC0,0x03,0x00,0x00,0xF0,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x8F,0x0F,0x00,0xC0,0xC3,0x1F,0x00,0xE0,0x60,0x30,0x00,0x20,0x20,0x20,0x00,0x00,0x20,0x20,0x00,0x00,0x60,0x30,0x00,0x00,0xC0,0x1F,0x00,0x00,0x80,0x0F, // 37 + 0x00,0x00,0x00,0x00,0x00,0x80,0x07,0x00,0x00,0xC0,0x0F,0x00,0x80,0xE3,0x1C,0x00,0xC0,0x77,0x38,0x00,0xE0,0x3C,0x30,0x00,0x60,0x38,0x30,0x00,0x60,0x78,0x30,0x00,0xE0,0xEC,0x38,0x00,0xC0,0x8F,0x1B,0x00,0x80,0x03,0x1F,0x00,0x00,0x00,0x0F,0x00,0x00,0xC0,0x1F,0x00,0x00,0xC0,0x38,0x00,0x00,0x00,0x10, // 38 + 0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xE0,0x07, // 39 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x0F,0x00,0x00,0xFE,0x7F,0x00,0x80,0x0F,0xF0,0x01,0xC0,0x01,0x80,0x03,0x60,0x00,0x00,0x06,0x20,0x00,0x00,0x04, // 40 + 0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x04,0x60,0x00,0x00,0x06,0xC0,0x01,0x80,0x03,0x80,0x0F,0xF0,0x01,0x00,0xFE,0x7F,0x00,0x00,0xF0,0x0F, // 41 + 0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x04,0x00,0x00,0x80,0x0F,0x00,0x00,0xE0,0x03,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x80,0x04,0x00,0x00,0x80, // 42 + 0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xFF,0x0F,0x00,0x00,0xFF,0x0F,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60, // 43 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x03,0x00,0x00,0xF0,0x01, // 44 + 0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01, // 45 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30, // 46 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0xE0,0x0F,0x00,0x00,0xFC,0x01,0x00,0x80,0x3F,0x00,0x00,0xE0,0x03,0x00,0x00,0x60, // 47 + 0x00,0x00,0x00,0x00,0x00,0xFE,0x03,0x00,0x80,0xFF,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xE0,0x00,0x38,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x38,0x00,0xC0,0x01,0x1C,0x00,0x80,0xFF,0x0F,0x00,0x00,0xFE,0x03, // 48 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x03,0x00,0x00,0x80,0x01,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 49 + 0x00,0x00,0x00,0x00,0x00,0x03,0x30,0x00,0xC0,0x03,0x38,0x00,0xC0,0x00,0x3C,0x00,0x60,0x00,0x36,0x00,0x60,0x00,0x33,0x00,0x60,0x80,0x31,0x00,0x60,0xC0,0x30,0x00,0x60,0x60,0x30,0x00,0xC0,0x30,0x30,0x00,0xC0,0x1F,0x30,0x00,0x00,0x0F,0x30, // 50 + 0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x00,0xC0,0x01,0x0E,0x00,0xC0,0x00,0x1C,0x00,0x60,0x00,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0xC0,0x38,0x30,0x00,0xC0,0x6F,0x18,0x00,0x80,0xC7,0x0F,0x00,0x00,0x80,0x07, // 51 + 0x00,0x00,0x00,0x00,0x00,0x80,0x03,0x00,0x00,0xC0,0x03,0x00,0x00,0xF0,0x03,0x00,0x00,0x3C,0x03,0x00,0x00,0x0E,0x03,0x00,0x80,0x07,0x03,0x00,0xC0,0x01,0x03,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03, // 52 + 0x00,0x00,0x00,0x00,0x00,0x30,0x06,0x00,0x80,0x3F,0x0E,0x00,0xE0,0x1F,0x18,0x00,0x60,0x08,0x30,0x00,0x60,0x0C,0x30,0x00,0x60,0x0C,0x30,0x00,0x60,0x0C,0x30,0x00,0x60,0x0C,0x30,0x00,0x60,0x18,0x1C,0x00,0x60,0xF0,0x0F,0x00,0x00,0xE0,0x03, // 53 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x03,0x00,0x80,0xFF,0x0F,0x00,0xC0,0x63,0x1C,0x00,0xC0,0x30,0x38,0x00,0x60,0x18,0x30,0x00,0x60,0x18,0x30,0x00,0x60,0x18,0x30,0x00,0x60,0x18,0x30,0x00,0xE0,0x30,0x18,0x00,0xC0,0xF1,0x0F,0x00,0x80,0xC1,0x07, // 54 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x3C,0x00,0x60,0x80,0x3F,0x00,0x60,0xE0,0x03,0x00,0x60,0x78,0x00,0x00,0x60,0x0E,0x00,0x00,0x60,0x03,0x00,0x00,0xE0,0x01,0x00,0x00,0x60, // 55 + 0x00,0x00,0x00,0x00,0x00,0x80,0x07,0x00,0x80,0xC7,0x1F,0x00,0xC0,0x6F,0x18,0x00,0xE0,0x38,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0xE0,0x38,0x30,0x00,0xC0,0x6F,0x18,0x00,0x80,0xC7,0x1F,0x00,0x00,0x80,0x07, // 56 + 0x00,0x00,0x00,0x00,0x00,0x1F,0x0C,0x00,0x80,0x7F,0x1C,0x00,0xC0,0x61,0x38,0x00,0x60,0xC0,0x30,0x00,0x60,0xC0,0x30,0x00,0x60,0xC0,0x30,0x00,0x60,0xC0,0x30,0x00,0x60,0x60,0x18,0x00,0xC0,0x31,0x1E,0x00,0x80,0xFF,0x0F,0x00,0x00,0xFE,0x01, // 57 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30, // 58 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x30,0x03,0x00,0x06,0xF0,0x01, // 59 + 0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0xD8,0x00,0x00,0x00,0xD8,0x00,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x04,0x01,0x00,0x00,0x06,0x03,0x00,0x00,0x06,0x03,0x00,0x00,0x03,0x06, // 60 + 0x00,0x00,0x00,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01, // 61 + 0x00,0x00,0x00,0x00,0x00,0x03,0x06,0x00,0x00,0x06,0x03,0x00,0x00,0x06,0x03,0x00,0x00,0x04,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0xD8,0x00,0x00,0x00,0xD8,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x20, // 62 + 0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x80,0x03,0x00,0x00,0xC0,0x01,0x00,0x00,0xE0,0x00,0x00,0x00,0x60,0x80,0x33,0x00,0x60,0xC0,0x33,0x00,0x60,0xE0,0x00,0x00,0x60,0x30,0x00,0x00,0xC0,0x38,0x00,0x00,0xC0,0x1F,0x00,0x00,0x00,0x07, // 63 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x0F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x1E,0xF0,0x00,0x00,0x07,0xC0,0x01,0x80,0xC3,0x87,0x01,0xC0,0xF1,0x9F,0x03,0xC0,0x38,0x18,0x03,0xC0,0x0C,0x30,0x03,0x60,0x0E,0x30,0x06,0x60,0x06,0x30,0x06,0x60,0x06,0x18,0x06,0x60,0x06,0x0C,0x06,0x60,0x0C,0x1E,0x06,0x60,0xF8,0x3F,0x06,0xE0,0xFE,0x31,0x06,0xC0,0x0E,0x30,0x06,0xC0,0x01,0x18,0x03,0x80,0x03,0x1C,0x03,0x00,0x07,0x8F,0x01,0x00,0xFE,0x87,0x01,0x00,0xF8,0xC1,0x00,0x00,0x00,0x40, // 64 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x80,0x8F,0x01,0x00,0xE0,0x83,0x01,0x00,0x60,0x80,0x01,0x00,0xE0,0x83,0x01,0x00,0x80,0x8F,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 65 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0xC0,0x78,0x30,0x00,0xC0,0xFF,0x18,0x00,0x80,0xC7,0x1F,0x00,0x00,0x80,0x07, // 66 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x03,0x0F,0x00,0x00,0x02,0x03, // 67 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x03,0x0E,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 68 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 69 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60, // 70 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x18,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x60,0x30,0x00,0x60,0x60,0x30,0x00,0xE0,0x60,0x38,0x00,0xC0,0x60,0x18,0x00,0xC0,0x61,0x18,0x00,0x80,0xE3,0x0F,0x00,0x00,0xE2,0x0F, // 71 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 72 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 73 + 0x00,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x38,0x00,0xE0,0xFF,0x1F,0x00,0xE0,0xFF,0x0F, // 74 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0xE0,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0xE7,0x01,0x00,0x80,0x83,0x07,0x00,0xC0,0x01,0x0F,0x00,0xE0,0x00,0x1E,0x00,0x60,0x00,0x38,0x00,0x20,0x00,0x30,0x00,0x00,0x00,0x20, // 75 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30, // 76 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0x01,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x3F,0x00,0x00,0xE0,0x07,0x00,0x00,0xFE,0x00,0x00,0xC0,0x0F,0x00,0x00,0xE0,0x01,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 77 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0xC0,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0xE0,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 78 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 79 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0xC0,0x30,0x00,0x00,0xC0,0x3F,0x00,0x00,0x00,0x0F, // 80 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x0C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x18,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x36,0x00,0x60,0x00,0x36,0x00,0xE0,0x00,0x3C,0x00,0xC0,0x00,0x1C,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x3F,0x00,0x00,0xFF,0x77,0x00,0x00,0xFC,0x61, // 81 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x70,0x00,0x00,0x60,0xF0,0x00,0x00,0x60,0xF0,0x03,0x00,0x60,0xB0,0x07,0x00,0xE0,0x18,0x1F,0x00,0xC0,0x1F,0x3C,0x00,0x80,0x0F,0x30,0x00,0x00,0x00,0x20, // 82 + 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x07,0x0F,0x00,0xC0,0x1F,0x1C,0x00,0xC0,0x18,0x18,0x00,0x60,0x38,0x38,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x70,0x30,0x00,0xC0,0x60,0x18,0x00,0xC0,0xE1,0x18,0x00,0x80,0xC3,0x0F,0x00,0x00,0x83,0x07, // 83 + 0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60, // 84 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 85 + 0x20,0x00,0x00,0x00,0xE0,0x01,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0xF8,0x01,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x3E,0x00,0x00,0xC0,0x0F,0x00,0x00,0xF8,0x01,0x00,0x00,0x3E,0x00,0x00,0xC0,0x0F,0x00,0x00,0xE0,0x01,0x00,0x00,0x20, // 86 + 0x60,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0x80,0xFF,0x00,0x00,0x00,0xF8,0x0F,0x00,0x00,0x80,0x3F,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x3F,0x00,0x00,0xE0,0x0F,0x00,0x00,0xFC,0x01,0x00,0x80,0x1F,0x00,0x00,0xE0,0x03,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,0x1F,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xE0,0x0F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x30,0x00,0x00,0x80,0x3F,0x00,0x00,0xF8,0x0F,0x00,0x80,0xFF,0x00,0x00,0xE0,0x07,0x00,0x00,0x60, // 87 + 0x00,0x00,0x20,0x00,0x20,0x00,0x30,0x00,0x60,0x00,0x3C,0x00,0xE0,0x01,0x1E,0x00,0xC0,0x83,0x07,0x00,0x00,0xCF,0x03,0x00,0x00,0xFE,0x01,0x00,0x00,0x38,0x00,0x00,0x00,0xFE,0x01,0x00,0x00,0xCF,0x03,0x00,0xC0,0x03,0x07,0x00,0xE0,0x01,0x1E,0x00,0x60,0x00,0x3C,0x00,0x20,0x00,0x30,0x00,0x00,0x00,0x20, // 88 + 0x20,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xC0,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0xF0,0x3F,0x00,0x00,0xF0,0x3F,0x00,0x00,0x3C,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x07,0x00,0x00,0xC0,0x03,0x00,0x00,0xE0,0x01,0x00,0x00,0x60,0x00,0x00,0x00,0x20, // 89 + 0x00,0x00,0x30,0x00,0x60,0x00,0x38,0x00,0x60,0x00,0x3C,0x00,0x60,0x00,0x37,0x00,0x60,0x80,0x33,0x00,0x60,0xC0,0x31,0x00,0x60,0xE0,0x30,0x00,0x60,0x38,0x30,0x00,0x60,0x1C,0x30,0x00,0x60,0x0E,0x30,0x00,0x60,0x07,0x30,0x00,0xE0,0x01,0x30,0x00,0xE0,0x00,0x30,0x00,0x60,0x00,0x30, // 90 + 0x00,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x60,0x00,0x00,0x06,0x60,0x00,0x00,0x06, // 91 + 0x60,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,0x3F,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xE0,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 92 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x06,0x60,0x00,0x00,0x06,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07, // 93 + 0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1F,0x00,0x00,0xC0,0x07,0x00,0x00,0xE0,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0xC0,0x07,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x20, // 94 + 0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06, // 95 + 0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x80, // 96 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x00,0x8C,0x39,0x00,0x00,0x86,0x31,0x00,0x00,0x86,0x31,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x18,0x00,0x00,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 97 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x18,0x0C,0x00,0x00,0x0C,0x18,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xE0,0x03, // 98 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0x18,0x0C, // 99 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0C,0x18,0x00,0x00,0x18,0x0C,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 100 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0x00,0xCE,0x38,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xCE,0x38,0x00,0x00,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 101 + 0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0xC0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x06,0x00,0x00,0x60,0x06,0x00,0x00,0x60,0x06, // 102 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x83,0x01,0x00,0xF8,0x8F,0x03,0x00,0x1C,0x1C,0x07,0x00,0x0E,0x38,0x06,0x00,0x06,0x30,0x06,0x00,0x06,0x30,0x06,0x00,0x06,0x30,0x06,0x00,0x0C,0x18,0x07,0x00,0x18,0x8C,0x03,0x00,0xFE,0xFF,0x01,0x00,0xFE,0xFF, // 103 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x18,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0xFC,0x3F,0x00,0x00,0xF8,0x3F, // 104 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xFE,0x3F,0x00,0x60,0xFE,0x3F, // 105 + 0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x60,0xFE,0xFF,0x07,0x60,0xFE,0xFF,0x03, // 106 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0xC0,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0xF0,0x01,0x00,0x00,0x98,0x07,0x00,0x00,0x0C,0x0E,0x00,0x00,0x06,0x3C,0x00,0x00,0x02,0x30,0x00,0x00,0x00,0x20, // 107 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 108 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F,0x00,0x00,0x0C,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0xFC,0x3F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x0C,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0xFC,0x3F,0x00,0x00,0xF8,0x3F, // 109 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F,0x00,0x00,0x18,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0xFC,0x3F,0x00,0x00,0xF8,0x3F, // 110 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 111 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0xFF,0x07,0x00,0xFE,0xFF,0x07,0x00,0x18,0x0C,0x00,0x00,0x0C,0x18,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xE0,0x03, // 112 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0C,0x18,0x00,0x00,0x18,0x0C,0x00,0x00,0xFE,0xFF,0x07,0x00,0xFE,0xFF,0x07, // 113 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F,0x00,0x00,0x0C,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06, // 114 + 0x00,0x00,0x00,0x00,0x00,0x38,0x0C,0x00,0x00,0x7C,0x1C,0x00,0x00,0xEE,0x38,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x31,0x00,0x00,0xC6,0x31,0x00,0x00,0x8E,0x39,0x00,0x00,0x9C,0x1F,0x00,0x00,0x18,0x0F, // 115 + 0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0xC0,0xFF,0x1F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30, // 116 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 117 + 0x00,0x06,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0xC0,0x07,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1F,0x00,0x00,0xC0,0x07,0x00,0x00,0xF8,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x06, // 118 + 0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x80,0x1F,0x00,0x00,0xE0,0x03,0x00,0x00,0x7C,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x80,0x1F,0x00,0x00,0xF0,0x03,0x00,0x00,0x7E,0x00,0x00,0x00,0x0E, // 119 + 0x00,0x02,0x20,0x00,0x00,0x06,0x30,0x00,0x00,0x1E,0x3C,0x00,0x00,0x38,0x0E,0x00,0x00,0xF0,0x07,0x00,0x00,0xC0,0x01,0x00,0x00,0xE0,0x07,0x00,0x00,0x38,0x0E,0x00,0x00,0x1C,0x3C,0x00,0x00,0x0E,0x30,0x00,0x00,0x02,0x20, // 120 + 0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x06,0x00,0xF0,0x01,0x06,0x00,0x80,0x0F,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xFC,0x00,0x00,0xC0,0x1F,0x00,0x00,0xF8,0x03,0x00,0x00,0x3E,0x00,0x00,0x00,0x06, // 121 + 0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x06,0x3C,0x00,0x00,0x06,0x3E,0x00,0x00,0x06,0x37,0x00,0x00,0xC6,0x33,0x00,0x00,0xE6,0x30,0x00,0x00,0x76,0x30,0x00,0x00,0x3E,0x30,0x00,0x00,0x1E,0x30,0x00,0x00,0x06,0x30, // 122 + 0x00,0x00,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0xC0,0x03,0x00,0xC0,0x7F,0xFE,0x03,0xE0,0x3F,0xFC,0x07,0x60,0x00,0x00,0x06,0x60,0x00,0x00,0x06, // 123 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x0F,0xE0,0xFF,0xFF,0x0F, // 124 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x06,0x60,0x00,0x00,0x06,0xE0,0x3F,0xFC,0x07,0xC0,0x7F,0xFF,0x03,0x00,0xC0,0x03,0x00,0x00,0x80,0x01, // 125 + 0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x60, // 126 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE6,0xFF,0x07,0x00,0xE6,0xFF,0x07, // 161 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x9C,0x07,0x00,0x0E,0x78,0x00,0x00,0x06,0x3F,0x00,0x00,0xF6,0x30,0x00,0x00,0x0E,0x30,0x00,0xE0,0x0D,0x1C,0x00,0x00,0x1C,0x0E,0x00,0x00,0x10,0x06, // 162 + 0x00,0x60,0x10,0x00,0x00,0x60,0x38,0x00,0x00,0x7F,0x1C,0x00,0xC0,0xFF,0x1F,0x00,0xE0,0xE0,0x19,0x00,0x60,0x60,0x18,0x00,0x60,0x60,0x18,0x00,0x60,0x60,0x30,0x00,0xE0,0x00,0x30,0x00,0xC0,0x01,0x30,0x00,0x80,0x01,0x38,0x00,0x00,0x00,0x10, // 163 + 0x00,0x00,0x00,0x00,0x00,0x02,0x04,0x00,0x00,0xF7,0x0E,0x00,0x00,0xFE,0x07,0x00,0x00,0x0C,0x03,0x00,0x00,0x06,0x06,0x00,0x00,0x06,0x06,0x00,0x00,0x06,0x06,0x00,0x00,0x06,0x06,0x00,0x00,0x0C,0x03,0x00,0x00,0xFE,0x07,0x00,0x00,0xF7,0x0E,0x00,0x00,0x02,0x04, // 164 + 0xE0,0x60,0x06,0x00,0xC0,0x61,0x06,0x00,0x80,0x67,0x06,0x00,0x00,0x7E,0x06,0x00,0x00,0x7C,0x06,0x00,0x00,0xF0,0x3F,0x00,0x00,0xF0,0x3F,0x00,0x00,0x7C,0x06,0x00,0x00,0x7E,0x06,0x00,0x80,0x67,0x06,0x00,0xC0,0x61,0x06,0x00,0xE0,0x60,0x06,0x00,0x20, // 165 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x7F,0xF8,0x0F,0xE0,0x7F,0xF8,0x0F, // 166 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x80,0xF3,0xC1,0x00,0xC0,0x1F,0xC3,0x03,0xE0,0x0C,0x07,0x03,0x60,0x1C,0x06,0x06,0x60,0x18,0x0C,0x06,0x60,0x30,0x1C,0x06,0xE0,0x70,0x38,0x07,0xC0,0xE1,0xF4,0x03,0x80,0xC1,0xE7,0x01,0x00,0x80,0x03, // 167 + 0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60, // 168 + 0x00,0xF8,0x00,0x00,0x00,0xFE,0x03,0x00,0x00,0x07,0x07,0x00,0x80,0x01,0x0C,0x00,0xC0,0x79,0x1C,0x00,0xC0,0xFE,0x19,0x00,0x60,0x86,0x31,0x00,0x60,0x03,0x33,0x00,0x60,0x03,0x33,0x00,0x60,0x03,0x33,0x00,0x60,0x03,0x33,0x00,0x60,0x87,0x33,0x00,0xC0,0x86,0x19,0x00,0xC0,0x85,0x1C,0x00,0x80,0x01,0x0C,0x00,0x00,0x07,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xF8, // 169 + 0x00,0x00,0x00,0x00,0xC0,0x1C,0x00,0x00,0xE0,0x3E,0x00,0x00,0x60,0x32,0x00,0x00,0x60,0x32,0x00,0x00,0xE0,0x3F,0x00,0x00,0xC0,0x3F, // 170 + 0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0x78,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x84,0x10,0x00,0x00,0xE0,0x03,0x00,0x00,0x78,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x04,0x10, // 171 + 0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFC,0x01, // 172 + 0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01, // 173 + 0x00,0xF8,0x00,0x00,0x00,0xFE,0x03,0x00,0x00,0x07,0x07,0x00,0x80,0x01,0x0C,0x00,0xC0,0x01,0x1C,0x00,0xC0,0xFE,0x1B,0x00,0x60,0xFE,0x33,0x00,0x60,0x66,0x30,0x00,0x60,0x66,0x30,0x00,0x60,0xE6,0x30,0x00,0x60,0xFE,0x31,0x00,0x60,0x3C,0x33,0x00,0xC0,0x00,0x1A,0x00,0xC0,0x01,0x1C,0x00,0x80,0x01,0x0C,0x00,0x00,0x07,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xF8, // 174 + 0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C, // 175 + 0x00,0x00,0x00,0x00,0x80,0x03,0x00,0x00,0x40,0x04,0x00,0x00,0x20,0x08,0x00,0x00,0x20,0x08,0x00,0x00,0x20,0x08,0x00,0x00,0x40,0x04,0x00,0x00,0x80,0x03, // 176 + 0x00,0x00,0x00,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0xFF,0x3F,0x00,0x00,0xFF,0x3F,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30, // 177 + 0x40,0x20,0x00,0x00,0x60,0x30,0x00,0x00,0x20,0x38,0x00,0x00,0x20,0x2C,0x00,0x00,0x20,0x26,0x00,0x00,0xE0,0x23,0x00,0x00,0xC0,0x21, // 178 + 0x40,0x10,0x00,0x00,0x60,0x30,0x00,0x00,0x20,0x20,0x00,0x00,0x20,0x22,0x00,0x00,0x20,0x22,0x00,0x00,0xE0,0x3D,0x00,0x00,0xC0,0x1D, // 179 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x20, // 180 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0xFF,0x07,0x00,0xFE,0xFF,0x07,0x00,0x00,0x1C,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x1C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 181 + 0x00,0x0F,0x00,0x00,0xC0,0x3F,0x00,0x00,0xC0,0x3F,0x00,0x00,0xE0,0x7F,0x00,0x00,0xE0,0x7F,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x60,0x00,0x00,0x00,0x60, // 182 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60, // 183 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0xC0,0x02,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x01, // 184 + 0x00,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0xE0,0x3F,0x00,0x00,0xE0,0x3F, // 185 + 0x00,0x00,0x00,0x00,0x80,0x0F,0x00,0x00,0xC0,0x1F,0x00,0x00,0xE0,0x38,0x00,0x00,0x60,0x30,0x00,0x00,0xE0,0x38,0x00,0x00,0xC0,0x1F,0x00,0x00,0x80,0x0F, // 186 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x00,0x00,0x1C,0x1C,0x00,0x00,0x78,0x0F,0x00,0x00,0xE0,0x03,0x00,0x00,0x84,0x10,0x00,0x00,0x1C,0x1C,0x00,0x00,0x78,0x0F,0x00,0x00,0xE0,0x03,0x00,0x00,0x80, // 187 + 0x00,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x20,0x00,0xE0,0x3F,0x38,0x00,0xE0,0x3F,0x1C,0x00,0x00,0x00,0x0E,0x00,0x00,0x80,0x03,0x00,0x00,0xC0,0x01,0x00,0x00,0xE0,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x07,0x0C,0x00,0xC0,0x01,0x0E,0x00,0xE0,0x80,0x0B,0x00,0x60,0xC0,0x08,0x00,0x00,0xE0,0x3F,0x00,0x00,0xE0,0x3F,0x00,0x00,0x00,0x08, // 188 + 0x00,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x20,0x00,0xE0,0x3F,0x30,0x00,0xE0,0x3F,0x1C,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x07,0x00,0x00,0xC0,0x01,0x00,0x00,0xE0,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x4E,0x20,0x00,0x00,0x67,0x30,0x00,0xC0,0x21,0x38,0x00,0xE0,0x20,0x2C,0x00,0x60,0x20,0x26,0x00,0x00,0xE0,0x27,0x00,0x00,0xC0,0x21, // 189 + 0x40,0x10,0x00,0x00,0x60,0x30,0x00,0x00,0x20,0x20,0x00,0x00,0x20,0x22,0x20,0x00,0x20,0x22,0x30,0x00,0xE0,0x3D,0x38,0x00,0xC0,0x1D,0x0E,0x00,0x00,0x00,0x07,0x00,0x00,0x80,0x03,0x00,0x00,0xE0,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x0E,0x0C,0x00,0x00,0x07,0x0E,0x00,0x80,0x83,0x0B,0x00,0xE0,0xC0,0x08,0x00,0x60,0xE0,0x3F,0x00,0x20,0xE0,0x3F,0x00,0x00,0x00,0x08, // 190 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0xF8,0x03,0x00,0x00,0x1E,0x03,0x00,0x00,0x07,0x07,0x00,0xE6,0x03,0x06,0x00,0xE6,0x01,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x80,0x03,0x00,0x00,0xC0,0x01,0x00,0x00,0xC0, // 191 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x82,0x8F,0x01,0x00,0xE6,0x83,0x01,0x00,0x6E,0x80,0x01,0x00,0xE8,0x83,0x01,0x00,0x80,0x8F,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 192 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x80,0x8F,0x01,0x00,0xE8,0x83,0x01,0x00,0x6E,0x80,0x01,0x00,0xE6,0x83,0x01,0x00,0x82,0x8F,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 193 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x88,0x8F,0x01,0x00,0xEC,0x83,0x01,0x00,0x66,0x80,0x01,0x00,0xE6,0x83,0x01,0x00,0x8C,0x8F,0x01,0x00,0x08,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 194 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x0C,0xFE,0x01,0x00,0x8E,0x8F,0x01,0x00,0xE6,0x83,0x01,0x00,0x66,0x80,0x01,0x00,0xEC,0x83,0x01,0x00,0x8C,0x8F,0x01,0x00,0x0E,0xFE,0x01,0x00,0x06,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 195 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x8C,0x8F,0x01,0x00,0xEC,0x83,0x01,0x00,0x60,0x80,0x01,0x00,0xE0,0x83,0x01,0x00,0x8C,0x8F,0x01,0x00,0x0C,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 196 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x9C,0x8F,0x01,0x00,0xE2,0x83,0x01,0x00,0x62,0x80,0x01,0x00,0xE2,0x83,0x01,0x00,0x9C,0x8F,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 197 + 0x00,0x00,0x30,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x0F,0x00,0x00,0xC0,0x03,0x00,0x00,0xF0,0x01,0x00,0x00,0xBC,0x01,0x00,0x00,0x8F,0x01,0x00,0xC0,0x83,0x01,0x00,0xE0,0x80,0x01,0x00,0x60,0x80,0x01,0x00,0x60,0x80,0x01,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 198 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0x60,0x00,0x30,0x02,0x60,0x00,0x30,0x02,0x60,0x00,0xF0,0x02,0x60,0x00,0xB0,0x03,0x60,0x00,0x30,0x01,0x60,0x00,0x30,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x03,0x0F,0x00,0x00,0x02,0x03, // 199 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x62,0x30,0x30,0x00,0x66,0x30,0x30,0x00,0x6E,0x30,0x30,0x00,0x68,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 200 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x68,0x30,0x30,0x00,0x6E,0x30,0x30,0x00,0x66,0x30,0x30,0x00,0x62,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 201 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x68,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x66,0x30,0x30,0x00,0x66,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x68,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 202 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 203 + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0xE6,0xFF,0x3F,0x00,0xEE,0xFF,0x3F,0x00,0x08, // 204 + 0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0xEE,0xFF,0x3F,0x00,0xE6,0xFF,0x3F,0x00,0x02, // 205 + 0x08,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0xE6,0xFF,0x3F,0x00,0xE6,0xFF,0x3F,0x00,0x0C,0x00,0x00,0x00,0x08, // 206 + 0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x0C,0x00,0x00,0x00,0x0C, // 207 + 0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x03,0x0E,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 208 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0xC0,0x01,0x00,0x00,0x8C,0x03,0x00,0x00,0x0E,0x0E,0x00,0x00,0x06,0x3C,0x00,0x00,0x06,0x70,0x00,0x00,0x0C,0xE0,0x01,0x00,0x0C,0x80,0x03,0x00,0x0E,0x00,0x0F,0x00,0x06,0x00,0x1C,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 209 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x62,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x6E,0x00,0x30,0x00,0x68,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 210 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x60,0x00,0x30,0x00,0x68,0x00,0x30,0x00,0x6E,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x62,0x00,0x30,0x00,0xE0,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 211 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x68,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0xE8,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 212 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xCC,0x00,0x18,0x00,0xEE,0x00,0x38,0x00,0x66,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0x6E,0x00,0x30,0x00,0xE6,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 213 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x6C,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0xEC,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 214 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x03,0x00,0x00,0x8E,0x03,0x00,0x00,0xDC,0x01,0x00,0x00,0xF8,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0xDC,0x01,0x00,0x00,0x8E,0x03,0x00,0x00,0x06,0x03, // 215 + 0x00,0x00,0x00,0x00,0x00,0xFC,0x21,0x00,0x00,0xFF,0x77,0x00,0x80,0x07,0x3F,0x00,0xC0,0x01,0x1E,0x00,0xC0,0x00,0x1F,0x00,0xE0,0x80,0x3B,0x00,0x60,0xC0,0x31,0x00,0x60,0xE0,0x30,0x00,0x60,0x70,0x30,0x00,0x60,0x38,0x30,0x00,0x60,0x1C,0x30,0x00,0xE0,0x0E,0x38,0x00,0xC0,0x07,0x18,0x00,0xC0,0x03,0x1C,0x00,0xE0,0x07,0x0F,0x00,0x70,0xFF,0x07,0x00,0x20,0xFC,0x01, // 216 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x02,0x00,0x30,0x00,0x06,0x00,0x30,0x00,0x0E,0x00,0x30,0x00,0x08,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 217 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x08,0x00,0x30,0x00,0x0E,0x00,0x30,0x00,0x06,0x00,0x30,0x00,0x02,0x00,0x30,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 218 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x08,0x00,0x30,0x00,0x0C,0x00,0x30,0x00,0x06,0x00,0x30,0x00,0x06,0x00,0x30,0x00,0x0C,0x00,0x30,0x00,0x08,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 219 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x0C,0x00,0x30,0x00,0x0C,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x0C,0x00,0x30,0x00,0x0C,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 220 + 0x20,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xC0,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x3C,0x00,0x00,0x08,0xF0,0x3F,0x00,0x0E,0xF0,0x3F,0x00,0x06,0x3C,0x00,0x00,0x02,0x1E,0x00,0x00,0x00,0x07,0x00,0x00,0xC0,0x03,0x00,0x00,0xE0,0x01,0x00,0x00,0x60,0x00,0x00,0x00,0x20, // 221 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x07,0x00,0x00,0x86,0x03,0x00,0x00,0xFE,0x01,0x00,0x00,0xF8, // 222 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xFF,0x3F,0x00,0xC0,0xFF,0x3F,0x00,0xC0,0x00,0x00,0x00,0x60,0x00,0x08,0x00,0x60,0x00,0x1C,0x00,0x60,0x00,0x38,0x00,0xE0,0x78,0x30,0x00,0xC0,0x7F,0x30,0x00,0x80,0xC7,0x30,0x00,0x00,0x80,0x39,0x00,0x00,0x80,0x1F,0x00,0x00,0x00,0x0F, // 223 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x00,0x8C,0x39,0x00,0x20,0x86,0x31,0x00,0x60,0x86,0x31,0x00,0xE0,0xC6,0x30,0x00,0x80,0xC6,0x18,0x00,0x00,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 224 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x00,0x8C,0x39,0x00,0x00,0x86,0x31,0x00,0x80,0x86,0x31,0x00,0xE0,0xC6,0x30,0x00,0x60,0xC6,0x18,0x00,0x20,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 225 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x80,0x8C,0x39,0x00,0xC0,0x86,0x31,0x00,0x60,0x86,0x31,0x00,0x60,0xC6,0x30,0x00,0xC0,0xC6,0x18,0x00,0x80,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 226 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0xC0,0x1C,0x1F,0x00,0xE0,0x8C,0x39,0x00,0x60,0x86,0x31,0x00,0x60,0x86,0x31,0x00,0xC0,0xC6,0x30,0x00,0xC0,0xC6,0x18,0x00,0xE0,0xCE,0x0C,0x00,0x60,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 227 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0xC0,0x8C,0x39,0x00,0xC0,0x86,0x31,0x00,0x00,0x86,0x31,0x00,0x00,0xC6,0x30,0x00,0xC0,0xC6,0x18,0x00,0xC0,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 228 + 0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x00,0x8C,0x39,0x00,0x70,0x86,0x31,0x00,0x88,0x86,0x31,0x00,0x88,0xC6,0x30,0x00,0x88,0xC6,0x18,0x00,0x70,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 229 + 0x00,0x00,0x00,0x00,0x00,0x10,0x0F,0x00,0x00,0x9C,0x1F,0x00,0x00,0xCC,0x39,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0x66,0x18,0x00,0x00,0x6E,0x1C,0x00,0x00,0xFC,0x0F,0x00,0x00,0xFC,0x1F,0x00,0x00,0xCC,0x1C,0x00,0x00,0xCE,0x38,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xCC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xE0,0x04, // 230 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x02,0x00,0x06,0x30,0x02,0x00,0x06,0xF0,0x02,0x00,0x06,0xB0,0x03,0x00,0x0E,0x38,0x01,0x00,0x1C,0x1C,0x00,0x00,0x18,0x0C, // 231 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0x20,0xCE,0x38,0x00,0x60,0xC6,0x30,0x00,0xE0,0xC6,0x30,0x00,0x80,0xC6,0x30,0x00,0x00,0xCE,0x38,0x00,0x00,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 232 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0x00,0xCE,0x38,0x00,0x80,0xC6,0x30,0x00,0xE0,0xC6,0x30,0x00,0x60,0xC6,0x30,0x00,0x20,0xCE,0x38,0x00,0x00,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 233 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0x80,0xCE,0x38,0x00,0xC0,0xC6,0x30,0x00,0x60,0xC6,0x30,0x00,0x60,0xC6,0x30,0x00,0xC0,0xCE,0x38,0x00,0x80,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 234 + 0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0xC0,0xCE,0x38,0x00,0xC0,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0xC0,0xCE,0x38,0x00,0xC0,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 235 + 0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x60,0xFE,0x3F,0x00,0xE0,0xFE,0x3F,0x00,0x80, // 236 + 0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0xE0,0xFE,0x3F,0x00,0x60,0xFE,0x3F,0x00,0x20, // 237 + 0x80,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x60,0xFE,0x3F,0x00,0x60,0xFE,0x3F,0x00,0xC0,0x00,0x00,0x00,0x80, // 238 + 0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F,0x00,0xC0,0x00,0x00,0x00,0xC0, // 239 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1D,0x1C,0x00,0xA0,0x0F,0x38,0x00,0xA0,0x06,0x30,0x00,0xE0,0x06,0x30,0x00,0xC0,0x06,0x30,0x00,0xC0,0x0F,0x38,0x00,0x20,0x1F,0x1C,0x00,0x00,0xFC,0x0F,0x00,0x00,0xE0,0x07, // 240 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0xC0,0xFE,0x3F,0x00,0xE0,0x18,0x00,0x00,0x60,0x0C,0x00,0x00,0x60,0x06,0x00,0x00,0xC0,0x06,0x00,0x00,0xC0,0x06,0x00,0x00,0xE0,0x0E,0x00,0x00,0x60,0xFC,0x3F,0x00,0x00,0xF8,0x3F, // 241 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x20,0x0E,0x38,0x00,0x60,0x06,0x30,0x00,0xE0,0x06,0x30,0x00,0x80,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 242 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x80,0x06,0x30,0x00,0xE0,0x06,0x30,0x00,0x60,0x06,0x30,0x00,0x20,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 243 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x80,0x0E,0x38,0x00,0xC0,0x06,0x30,0x00,0x60,0x06,0x30,0x00,0x60,0x06,0x30,0x00,0xC0,0x0E,0x38,0x00,0x80,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 244 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0xC0,0x1C,0x1C,0x00,0xE0,0x0E,0x38,0x00,0x60,0x06,0x30,0x00,0x60,0x06,0x30,0x00,0xC0,0x06,0x30,0x00,0xC0,0x0E,0x38,0x00,0xE0,0x1C,0x1C,0x00,0x60,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 245 + 0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0xC0,0x0E,0x38,0x00,0xC0,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0xC0,0x0E,0x38,0x00,0xC0,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 246 + 0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0xB6,0x01,0x00,0x00,0xB6,0x01,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30, // 247 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x67,0x00,0x00,0xF8,0x7F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x3F,0x00,0x00,0x86,0x33,0x00,0x00,0xE6,0x31,0x00,0x00,0x76,0x30,0x00,0x00,0x3E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xFF,0x0F,0x00,0x00,0xF3,0x07, // 248 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0x20,0x00,0x38,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x30,0x00,0x80,0x00,0x30,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 249 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x80,0x00,0x30,0x00,0xE0,0x00,0x30,0x00,0x60,0x00,0x18,0x00,0x20,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 250 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0x80,0x00,0x38,0x00,0xC0,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xC0,0x00,0x18,0x00,0x80,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 251 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0xC0,0x00,0x38,0x00,0xC0,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0xC0,0x00,0x18,0x00,0xC0,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 252 + 0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x06,0x00,0xF0,0x01,0x06,0x00,0x80,0x0F,0x07,0x80,0x00,0xFE,0x03,0xE0,0x00,0xFC,0x00,0x60,0xC0,0x1F,0x00,0x20,0xF8,0x03,0x00,0x00,0x3E,0x00,0x00,0x00,0x06, // 253 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x00,0x1C,0x18,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x03, // 254 + 0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x06,0xC0,0xF0,0x01,0x06,0xC0,0x80,0x0F,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xFC,0x00,0xC0,0xC0,0x1F,0x00,0xC0,0xF8,0x03,0x00,0x00,0x3E,0x00,0x00,0x00,0x06 // 255 +}; +#endif diff --git a/lib/esp8266-oled-ssd1306-master/src/OLEDDisplayUi.cpp b/lib/esp8266-oled-ssd1306-master/src/OLEDDisplayUi.cpp new file mode 100644 index 00000000..778a2e77 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/src/OLEDDisplayUi.cpp @@ -0,0 +1,471 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de + * + * 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. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#include "OLEDDisplayUi.h" + +void LoadingDrawDefault(OLEDDisplay *display, LoadingStage* stage, uint8_t progress) { + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(ArialMT_Plain_10); + display->drawString(64, 18, stage->process); + display->drawProgressBar(4, 32, 120, 8, progress); +}; + + +OLEDDisplayUi::OLEDDisplayUi(OLEDDisplay *display) { + this->display = display; + + indicatorPosition = BOTTOM; + indicatorDirection = LEFT_RIGHT; + activeSymbol = ANIMATION_activeSymbol; + inactiveSymbol = ANIMATION_inactiveSymbol; + frameAnimationDirection = SLIDE_RIGHT; + lastTransitionDirection = 1; + frameCount = 0; + nextFrameNumber = -1; + overlayCount = 0; + indicatorDrawState = 1; + loadingDrawFunction = LoadingDrawDefault; + updateInterval = 33; + state.lastUpdate = 0; + state.ticksSinceLastStateSwitch = 0; + state.frameState = FIXED; + state.currentFrame = 0; + state.frameTransitionDirection = 1; + state.isIndicatorDrawen = true; + state.manuelControll = false; + state.userData = NULL; + shouldDrawIndicators = true; + autoTransition = true; + setTimePerFrame(5000); + setTimePerTransition(500); +} + +void OLEDDisplayUi::init() { + this->display->init(); +} + +void OLEDDisplayUi::setTargetFPS(uint8_t fps){ + this->updateInterval = ((float) 1.0 / (float) fps) * 1000; + + this->ticksPerFrame = timePerFrame / updateInterval; + this->ticksPerTransition = timePerTransition / updateInterval; +} + +// -/------ Automatic controll ------\- + +void OLEDDisplayUi::enableAutoTransition(){ + this->autoTransition = true; +} +void OLEDDisplayUi::disableAutoTransition(){ + this->autoTransition = false; +} +void OLEDDisplayUi::setAutoTransitionForwards(){ + this->state.frameTransitionDirection = 1; + this->lastTransitionDirection = 1; +} +void OLEDDisplayUi::setAutoTransitionBackwards(){ + this->state.frameTransitionDirection = -1; + this->lastTransitionDirection = -1; +} +void OLEDDisplayUi::setTimePerFrame(uint16_t time){ + this->timePerFrame = time; + this->ticksPerFrame = timePerFrame / updateInterval; +} +void OLEDDisplayUi::setTimePerTransition(uint16_t time){ + this->timePerTransition = time; + this->ticksPerTransition = timePerTransition / updateInterval; +} + +// -/------ Customize indicator position and style -------\- +void OLEDDisplayUi::enableIndicator(){ + this->state.isIndicatorDrawen = true; +} + +void OLEDDisplayUi::disableIndicator(){ + this->state.isIndicatorDrawen = false; +} + +void OLEDDisplayUi::enableAllIndicators(){ + this->shouldDrawIndicators = true; +} + +void OLEDDisplayUi::disableAllIndicators(){ + this->shouldDrawIndicators = false; +} + +void OLEDDisplayUi::setIndicatorPosition(IndicatorPosition pos) { + this->indicatorPosition = pos; +} +void OLEDDisplayUi::setIndicatorDirection(IndicatorDirection dir) { + this->indicatorDirection = dir; +} +void OLEDDisplayUi::setActiveSymbol(const uint8_t* symbol) { + this->activeSymbol = symbol; +} +void OLEDDisplayUi::setInactiveSymbol(const uint8_t* symbol) { + this->inactiveSymbol = symbol; +} + + +// -/----- Frame settings -----\- +void OLEDDisplayUi::setFrameAnimation(AnimationDirection dir) { + this->frameAnimationDirection = dir; +} +void OLEDDisplayUi::setFrames(FrameCallback* frameFunctions, uint8_t frameCount) { + this->frameFunctions = frameFunctions; + this->frameCount = frameCount; + this->resetState(); +} + +// -/----- Overlays ------\- +void OLEDDisplayUi::setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount){ + this->overlayFunctions = overlayFunctions; + this->overlayCount = overlayCount; +} + +// -/----- Loading Process -----\- + +void OLEDDisplayUi::setLoadingDrawFunction(LoadingDrawFunction loadingDrawFunction) { + this->loadingDrawFunction = loadingDrawFunction; +} + +void OLEDDisplayUi::runLoadingProcess(LoadingStage* stages, uint8_t stagesCount) { + uint8_t progress = 0; + uint8_t increment = 100 / stagesCount; + + for (uint8_t i = 0; i < stagesCount; i++) { + display->clear(); + this->loadingDrawFunction(this->display, &stages[i], progress); + display->display(); + + stages[i].callback(); + + progress += increment; + yield(); + } + + display->clear(); + this->loadingDrawFunction(this->display, &stages[stagesCount-1], progress); + display->display(); + + delay(150); +} + +// -/----- Manuel control -----\- +void OLEDDisplayUi::nextFrame() { + if (this->state.frameState != IN_TRANSITION) { + this->state.manuelControll = true; + this->state.frameState = IN_TRANSITION; + this->state.ticksSinceLastStateSwitch = 0; + this->lastTransitionDirection = this->state.frameTransitionDirection; + this->state.frameTransitionDirection = 1; + } +} +void OLEDDisplayUi::previousFrame() { + if (this->state.frameState != IN_TRANSITION) { + this->state.manuelControll = true; + this->state.frameState = IN_TRANSITION; + this->state.ticksSinceLastStateSwitch = 0; + this->lastTransitionDirection = this->state.frameTransitionDirection; + this->state.frameTransitionDirection = -1; + } +} + +void OLEDDisplayUi::switchToFrame(uint8_t frame) { + if (frame >= this->frameCount) return; + this->state.ticksSinceLastStateSwitch = 0; + if (frame == this->state.currentFrame) return; + this->state.frameState = FIXED; + this->state.currentFrame = frame; + this->state.isIndicatorDrawen = true; +} + +void OLEDDisplayUi::transitionToFrame(uint8_t frame) { + if (frame >= this->frameCount) return; + this->state.ticksSinceLastStateSwitch = 0; + if (frame == this->state.currentFrame) return; + this->nextFrameNumber = frame; + this->lastTransitionDirection = this->state.frameTransitionDirection; + this->state.manuelControll = true; + this->state.frameState = IN_TRANSITION; + this->state.frameTransitionDirection = frame < this->state.currentFrame ? -1 : 1; +} + + +// -/----- State information -----\- +OLEDDisplayUiState* OLEDDisplayUi::getUiState(){ + return &this->state; +} + +int16_t OLEDDisplayUi::update(){ +#ifdef ARDUINO + unsigned long frameStart = millis(); +#elif __MBED__ + Timer t; + t.start(); + unsigned long frameStart = t.read_ms(); +#else +#error "Unkown operating system" +#endif + int32_t timeBudget = this->updateInterval - (frameStart - this->state.lastUpdate); + if ( timeBudget <= 0) { + // Implement frame skipping to ensure time budget is keept + if (this->autoTransition && this->state.lastUpdate != 0) this->state.ticksSinceLastStateSwitch += ceil((double)-timeBudget / (double)this->updateInterval); + + this->state.lastUpdate = frameStart; + this->tick(); + } +#ifdef ARDUINO + return this->updateInterval - (millis() - frameStart); +#elif __MBED__ + return this->updateInterval - (t.read_ms() - frameStart); +#else +#error "Unkown operating system" +#endif +} + + +void OLEDDisplayUi::tick() { + this->state.ticksSinceLastStateSwitch++; + + switch (this->state.frameState) { + case IN_TRANSITION: + if (this->state.ticksSinceLastStateSwitch >= this->ticksPerTransition){ + this->state.frameState = FIXED; + this->state.currentFrame = getNextFrameNumber(); + this->state.ticksSinceLastStateSwitch = 0; + this->nextFrameNumber = -1; + } + break; + case FIXED: + // Revert manuelControll + if (this->state.manuelControll) { + this->state.frameTransitionDirection = this->lastTransitionDirection; + this->state.manuelControll = false; + } + if (this->state.ticksSinceLastStateSwitch >= this->ticksPerFrame){ + if (this->autoTransition){ + this->state.frameState = IN_TRANSITION; + } + this->state.ticksSinceLastStateSwitch = 0; + } + break; + } + + this->display->clear(); + this->drawFrame(); + if (shouldDrawIndicators) { + this->drawIndicator(); + } + this->drawOverlays(); + this->display->display(); +} + +void OLEDDisplayUi::resetState() { + this->state.lastUpdate = 0; + this->state.ticksSinceLastStateSwitch = 0; + this->state.frameState = FIXED; + this->state.currentFrame = 0; + this->state.isIndicatorDrawen = true; +} + +void OLEDDisplayUi::drawFrame(){ + switch (this->state.frameState){ + case IN_TRANSITION: { + float progress = 0.f; + if (this->ticksPerTransition > 0u) { + progress = (float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition; + } + int16_t x = 0, y = 0, x1 = 0, y1 = 0; + switch(this->frameAnimationDirection){ + case SLIDE_LEFT: + x = -this->display->width() * progress; + y = 0; + x1 = x + this->display->width(); + y1 = 0; + break; + case SLIDE_RIGHT: + x = this->display->width() * progress; + y = 0; + x1 = x - this->display->width(); + y1 = 0; + break; + case SLIDE_UP: + x = 0; + y = -this->display->height() * progress; + x1 = 0; + y1 = y + this->display->height(); + break; + case SLIDE_DOWN: + default: + x = 0; + y = this->display->height() * progress; + x1 = 0; + y1 = y - this->display->height(); + break; + } + + // Invert animation if direction is reversed. + int8_t dir = this->state.frameTransitionDirection >= 0 ? 1 : -1; + x *= dir; y *= dir; x1 *= dir; y1 *= dir; + + bool drawenCurrentFrame; + + + // Prope each frameFunction for the indicator Drawen state + this->enableIndicator(); + (this->frameFunctions[this->state.currentFrame])(this->display, &this->state, x, y); + drawenCurrentFrame = this->state.isIndicatorDrawen; + + this->enableIndicator(); + (this->frameFunctions[this->getNextFrameNumber()])(this->display, &this->state, x1, y1); + + // Build up the indicatorDrawState + if (drawenCurrentFrame && !this->state.isIndicatorDrawen) { + // Drawen now but not next + this->indicatorDrawState = 2; + } else if (!drawenCurrentFrame && this->state.isIndicatorDrawen) { + // Not drawen now but next + this->indicatorDrawState = 1; + } else if (!drawenCurrentFrame && !this->state.isIndicatorDrawen) { + // Not drawen in both frames + this->indicatorDrawState = 3; + } + + // If the indicator isn't draw in the current frame + // reflect it in state.isIndicatorDrawen + if (!drawenCurrentFrame) this->state.isIndicatorDrawen = false; + + break; + } + case FIXED: + // Always assume that the indicator is drawn! + // And set indicatorDrawState to "not known yet" + this->indicatorDrawState = 0; + this->enableIndicator(); + (this->frameFunctions[this->state.currentFrame])(this->display, &this->state, 0, 0); + break; + } +} + +void OLEDDisplayUi::drawIndicator() { + + // Only draw if the indicator is invisible + // for both frames or + // the indiactor is shown and we are IN_TRANSITION + if (this->indicatorDrawState == 3 || (!this->state.isIndicatorDrawen && this->state.frameState != IN_TRANSITION)) { + return; + } + + uint8_t posOfHighlightFrame = 0; + float indicatorFadeProgress = 0; + + // if the indicator needs to be slided in we want to + // highlight the next frame in the transition + uint8_t frameToHighlight = this->indicatorDrawState == 1 ? this->getNextFrameNumber() : this->state.currentFrame; + + // Calculate the frame that needs to be highlighted + // based on the Direction the indiactor is drawn + switch (this->indicatorDirection){ + case LEFT_RIGHT: + posOfHighlightFrame = frameToHighlight; + break; + case RIGHT_LEFT: + default: + posOfHighlightFrame = this->frameCount - frameToHighlight; + break; + } + + switch (this->indicatorDrawState) { + case 1: // Indicator was not drawn in this frame but will be in next + // Slide IN + indicatorFadeProgress = 1 - ((float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition); + break; + case 2: // Indicator was drawn in this frame but not in next + // Slide OUT + indicatorFadeProgress = ((float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition); + break; + } + + //Space between indicators - reduce for small screen sizes + uint16_t indicatorSpacing = 12; + if (this->display->getHeight() < 64 && (this->indicatorPosition == RIGHT || this->indicatorPosition == LEFT)) { + indicatorSpacing = 6; + } + + uint16_t frameStartPos = (indicatorSpacing * frameCount / 2); + const uint8_t *image; + + uint16_t x = 0,y = 0; + + + for (uint8_t i = 0; i < this->frameCount; i++) { + + switch (this->indicatorPosition){ + case TOP: + y = 0 - (8 * indicatorFadeProgress); + x = (this->display->width() / 2) - frameStartPos + 12 * i; + break; + case BOTTOM: + y = (this->display->height() - 8) + (8 * indicatorFadeProgress); + x = (this->display->width() / 2) - frameStartPos + 12 * i; + break; + case RIGHT: + x = (this->display->width() - 8) + (8 * indicatorFadeProgress); + y = (this->display->height() / 2) - frameStartPos + 2 + 12 * i; + break; + case LEFT: + default: + x = 0 - (8 * indicatorFadeProgress); + y = (this->display->height() / 2) - frameStartPos + 2 + indicatorSpacing * i; + break; + } + + if (posOfHighlightFrame == i) { + image = this->activeSymbol; + } else { + image = this->inactiveSymbol; + } + + this->display->drawFastImage(x, y, 8, 8, image); + } +} + +void OLEDDisplayUi::drawOverlays() { + for (uint8_t i=0;ioverlayCount;i++){ + (this->overlayFunctions[i])(this->display, &this->state); + } +} + +uint8_t OLEDDisplayUi::getNextFrameNumber(){ + if (this->nextFrameNumber != -1) return this->nextFrameNumber; + return (this->state.currentFrame + this->frameCount + this->state.frameTransitionDirection) % this->frameCount; +} diff --git a/lib/esp8266-oled-ssd1306-master/src/OLEDDisplayUi.h b/lib/esp8266-oled-ssd1306-master/src/OLEDDisplayUi.h new file mode 100644 index 00000000..9aa3f320 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/src/OLEDDisplayUi.h @@ -0,0 +1,315 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de + * + * 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. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef OLEDDISPLAYUI_h +#define OLEDDISPLAYUI_h + +#ifdef ARDUINO +#include +#elif __MBED__ +#include +#else +#error "Unkown operating system" +#endif + +#include "OLEDDisplay.h" + +//#define DEBUG_OLEDDISPLAYUI(...) Serial.printf( __VA_ARGS__ ) + +#ifndef DEBUG_OLEDDISPLAYUI +#define DEBUG_OLEDDISPLAYUI(...) +#endif + +enum AnimationDirection { + SLIDE_UP, + SLIDE_DOWN, + SLIDE_LEFT, + SLIDE_RIGHT +}; + +enum IndicatorPosition { + TOP, + RIGHT, + BOTTOM, + LEFT +}; + +enum IndicatorDirection { + LEFT_RIGHT, + RIGHT_LEFT +}; + +enum FrameState { + IN_TRANSITION, + FIXED +}; + + +const uint8_t ANIMATION_activeSymbol[] PROGMEM = { + 0x00, 0x18, 0x3c, 0x7e, 0x7e, 0x3c, 0x18, 0x00 +}; + +const uint8_t ANIMATION_inactiveSymbol[] PROGMEM = { + 0x00, 0x0, 0x0, 0x18, 0x18, 0x0, 0x0, 0x00 +}; + + +// Structure of the UiState +struct OLEDDisplayUiState { + uint64_t lastUpdate; + uint16_t ticksSinceLastStateSwitch; + + FrameState frameState; + uint8_t currentFrame; + + bool isIndicatorDrawen; + + // Normal = 1, Inverse = -1; + int8_t frameTransitionDirection; + + bool manuelControll; + + // Custom data that can be used by the user + void* userData; +}; + +struct LoadingStage { + const char* process; + void (*callback)(); +}; + +typedef void (*FrameCallback)(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y); +typedef void (*OverlayCallback)(OLEDDisplay *display, OLEDDisplayUiState* state); +typedef void (*LoadingDrawFunction)(OLEDDisplay *display, LoadingStage* stage, uint8_t progress); + +class OLEDDisplayUi { + private: + OLEDDisplay *display; + + // Symbols for the Indicator + IndicatorPosition indicatorPosition; + IndicatorDirection indicatorDirection; + + const uint8_t* activeSymbol; + const uint8_t* inactiveSymbol; + + bool shouldDrawIndicators; + + // Values for the Frames + AnimationDirection frameAnimationDirection; + + int8_t lastTransitionDirection; + + uint16_t ticksPerFrame; // ~ 5000ms at 30 FPS + uint16_t ticksPerTransition; // ~ 500ms at 30 FPS + + bool autoTransition; + + FrameCallback* frameFunctions; + uint8_t frameCount; + + // Internally used to transition to a specific frame + int8_t nextFrameNumber; + + // Values for Overlays + OverlayCallback* overlayFunctions; + uint8_t overlayCount; + + // Will the Indicator be drawen + // 3 Not drawn in both frames + // 2 Drawn this frame but not next + // 1 Not drown this frame but next + // 0 Not known yet + uint8_t indicatorDrawState; + + // Loading screen + LoadingDrawFunction loadingDrawFunction; + + // UI State + OLEDDisplayUiState state; + + // Bookeeping for update + uint16_t updateInterval = 33; + + uint16_t timePerFrame; + uint16_t timePerTransition; + + uint8_t getNextFrameNumber(); + void drawIndicator(); + void drawFrame(); + void drawOverlays(); + void tick(); + void resetState(); + + public: + + OLEDDisplayUi(OLEDDisplay *display); + + /** + * Initialise the display + */ + void init(); + + /** + * Configure the internal used target FPS + */ + void setTargetFPS(uint8_t fps); + + // Automatic Controll + /** + * Enable automatic transition to next frame after the some time can be configured with `setTimePerFrame` and `setTimePerTransition`. + */ + void enableAutoTransition(); + + /** + * Disable automatic transition to next frame. + */ + void disableAutoTransition(); + + /** + * Set the direction if the automatic transitioning + */ + void setAutoTransitionForwards(); + void setAutoTransitionBackwards(); + + /** + * Set the approx. time a frame is displayed + */ + void setTimePerFrame(uint16_t time); + + /** + * Set the approx. time a transition will take + */ + void setTimePerTransition(uint16_t time); + + // Customize indicator position and style + + /** + * Draw the indicator. + * This is the defaut state for all frames if + * the indicator was hidden on the previous frame + * it will be slided in. + */ + void enableIndicator(); + + /** + * Don't draw the indicator. + * This will slide out the indicator + * when transitioning to the next frame. + */ + void disableIndicator(); + + /** + * Enable drawing of indicators + */ + void enableAllIndicators(); + + /** + * Disable draw of indicators. + */ + void disableAllIndicators(); + + /** + * Set the position of the indicator bar. + */ + void setIndicatorPosition(IndicatorPosition pos); + + /** + * Set the direction of the indicator bar. Defining the order of frames ASCENDING / DESCENDING + */ + void setIndicatorDirection(IndicatorDirection dir); + + /** + * Set the symbol to indicate an active frame in the indicator bar. + */ + void setActiveSymbol(const uint8_t* symbol); + + /** + * Set the symbol to indicate an inactive frame in the indicator bar. + */ + void setInactiveSymbol(const uint8_t* symbol); + + + // Frame settings + + /** + * Configure what animation is used to transition from one frame to another + */ + void setFrameAnimation(AnimationDirection dir); + + /** + * Add frame drawing functions + */ + void setFrames(FrameCallback* frameFunctions, uint8_t frameCount); + + // Overlay + + /** + * Add overlays drawing functions that are draw independent of the Frames + */ + void setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount); + + + // Loading animation + /** + * Set the function that will draw each step + * in the loading animation + */ + void setLoadingDrawFunction(LoadingDrawFunction loadingFunction); + + + /** + * Run the loading process + */ + void runLoadingProcess(LoadingStage* stages, uint8_t stagesCount); + + + // Manual Control + void nextFrame(); + void previousFrame(); + + /** + * Switch without transition to frame `frame`. + */ + void switchToFrame(uint8_t frame); + + /** + * Transition to frame `frame`, when the `frame` number is bigger than the current + * frame the forward animation will be used, otherwise the backwards animation is used. + */ + void transitionToFrame(uint8_t frame); + + // State Info + OLEDDisplayUiState* getUiState(); + + int16_t update(); +}; +#endif diff --git a/lib/esp8266-oled-ssd1306-master/src/SH1106.h b/lib/esp8266-oled-ssd1306-master/src/SH1106.h new file mode 100644 index 00000000..47188d1f --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/src/SH1106.h @@ -0,0 +1,39 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * 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. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef SH1106_h +#define SH1106_h +#include "SH1106Wire.h" + +// For make SH1106 an alias for SH1106Wire +typedef SH1106Wire SH1106; + + +#endif diff --git a/lib/esp8266-oled-ssd1306-master/src/SH1106Brzo.h b/lib/esp8266-oled-ssd1306-master/src/SH1106Brzo.h new file mode 100644 index 00000000..be9c0c74 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/src/SH1106Brzo.h @@ -0,0 +1,141 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * 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. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef SH1106Brzo_h +#define SH1106Brzo_h + +#include "OLEDDisplay.h" +#include + +#if F_CPU == 160000000L + #define BRZO_I2C_SPEED 1000 +#else + #define BRZO_I2C_SPEED 800 +#endif + +class SH1106Brzo : public OLEDDisplay { + private: + uint8_t _address; + uint8_t _sda; + uint8_t _scl; + + public: + SH1106Brzo(uint8_t _address, uint8_t _sda, uint8_t _scl, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64) { + setGeometry(g); + + this->_address = _address; + this->_sda = _sda; + this->_scl = _scl; + } + + bool connect(){ + brzo_i2c_setup(_sda, _scl, 0); + return true; + } + + void display(void) { + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = UINT8_MAX; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = UINT8_MAX; + uint8_t maxBoundX = 0; + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (displayHeight / 8); y++) { + for (x = 0; x < displayWidth; x++) { + uint16_t pos = x + y * displayWidth; + if (buffer[pos] != buffer_back[pos]) { + minBoundY = _min(minBoundY, y); + maxBoundY = _max(maxBoundY, y); + minBoundX = _min(minBoundX, x); + maxBoundX = _max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + yield(); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + if (minBoundY == UINT8_MAX) return; + + byte k = 0; + uint8_t sendBuffer[17]; + sendBuffer[0] = 0x40; + + // Calculate the colum offset + uint8_t minBoundXp2H = (minBoundX + 2) & 0x0F; + uint8_t minBoundXp2L = 0x10 | ((minBoundX + 2) >> 4 ); + + brzo_i2c_start_transaction(this->_address, BRZO_I2C_SPEED); + + for (y = minBoundY; y <= maxBoundY; y++) { + sendCommand(0xB0 + y); + sendCommand(minBoundXp2H); + sendCommand(minBoundXp2L); + for (x = minBoundX; x <= maxBoundX; x++) { + k++; + sendBuffer[k] = buffer[x + y * displayWidth]; + if (k == 16) { + brzo_i2c_write(sendBuffer, 17, true); + k = 0; + } + } + if (k != 0) { + brzo_i2c_write(sendBuffer, k + 1, true); + k = 0; + } + yield(); + } + if (k != 0) { + brzo_i2c_write(sendBuffer, k + 1, true); + } + brzo_i2c_end_transaction(); + #else + #endif + } + + private: + int getBufferOffset(void) { + return 0; + } + inline void sendCommand(uint8_t com) __attribute__((always_inline)){ + uint8_t command[2] = {0x80 /* command mode */, com}; + brzo_i2c_start_transaction(_address, BRZO_I2C_SPEED); + brzo_i2c_write(command, 2, true); + brzo_i2c_end_transaction(); + } +}; + +#endif diff --git a/lib/esp8266-oled-ssd1306-master/src/SH1106Spi.h b/lib/esp8266-oled-ssd1306-master/src/SH1106Spi.h new file mode 100644 index 00000000..23693bc4 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/src/SH1106Spi.h @@ -0,0 +1,135 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * 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. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef SH1106Spi_h +#define SH1106Spi_h + +#include "OLEDDisplay.h" +#include + +class SH1106Spi : public OLEDDisplay { + private: + uint8_t _rst; + uint8_t _dc; + + public: + SH1106Spi(uint8_t _rst, uint8_t _dc, uint8_t _cs, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64) { + setGeometry(g); + + this->_rst = _rst; + this->_dc = _dc; + } + + bool connect(){ + pinMode(_dc, OUTPUT); + pinMode(_rst, OUTPUT); + + SPI.begin (); + SPI.setClockDivider (SPI_CLOCK_DIV2); + + // Pulse Reset low for 10ms + digitalWrite(_rst, HIGH); + delay(1); + digitalWrite(_rst, LOW); + delay(10); + digitalWrite(_rst, HIGH); + return true; + } + + void display(void) { + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = UINT8_MAX; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = UINT8_MAX; + uint8_t maxBoundX = 0; + + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (displayHeight / 8); y++) { + for (x = 0; x < displayWidth; x++) { + uint16_t pos = x + y * displayWidth; + if (buffer[pos] != buffer_back[pos]) { + minBoundY = _min(minBoundY, y); + maxBoundY = _max(maxBoundY, y); + minBoundX = _min(minBoundX, x); + maxBoundX = _max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + yield(); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + if (minBoundY == UINT8_MAX) return; + + // Calculate the colum offset + uint8_t minBoundXp2H = (minBoundX + 2) & 0x0F; + uint8_t minBoundXp2L = 0x10 | ((minBoundX + 2) >> 4 ); + + for (y = minBoundY; y <= maxBoundY; y++) { + sendCommand(0xB0 + y); + sendCommand(minBoundXp2H); + sendCommand(minBoundXp2L); + digitalWrite(_dc, HIGH); // data mode + for (x = minBoundX; x <= maxBoundX; x++) { + SPI.transfer(buffer[x + y * displayWidth]); + } + yield(); + } + #else + for (uint8_t y=0; y + +#define SH1106_SET_PUMP_VOLTAGE 0X30 +#define SH1106_SET_PUMP_MODE 0XAD +#define SH1106_PUMP_ON 0X8B +#define SH1106_PUMP_OFF 0X8A +//-------------------------------------- + +class SH1106Wire : public OLEDDisplay { + private: + uint8_t _address; + uint8_t _sda; + uint8_t _scl; + bool _doI2cAutoInit = false; + TwoWire* _wire = NULL; + int _frequency; + + public: + /** + * Create and initialize the Display using Wire library + * + * Beware for retro-compatibility default values are provided for all parameters see below. + * Please note that if you don't wan't SD1306Wire to initialize and change frequency speed ot need to + * ensure -1 value are specified for all 3 parameters. This can be usefull to control TwoWire with multiple + * device on the same bus. + * + * @param _address I2C Display address + * @param _sda I2C SDA pin number, default to -1 to skip Wire begin call + * @param _scl I2C SCL pin number, default to -1 (only SDA = -1 is considered to skip Wire begin call) + * @param g display geometry dafault to generic GEOMETRY_128_64, see OLEDDISPLAY_GEOMETRY definition for other options + * @param _i2cBus on ESP32 with 2 I2C HW buses, I2C_ONE for 1st Bus, I2C_TWO fot 2nd bus, default I2C_ONE + * @param _frequency for Frequency by default Let's use ~700khz if ESP8266 is in 160Mhz mode, this will be limited to ~400khz if the ESP8266 in 80Mhz mode + */ + SH1106Wire(uint8_t _address, uint8_t _sda, uint8_t _scl, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64, HW_I2C _i2cBus = I2C_ONE, int _frequency = 700000) { + setGeometry(g); + + this->_address = _address; + this->_sda = _sda; + this->_scl = _scl; +#if !defined(ARDUINO_ARCH_ESP32) + this->_wire = &Wire; +#else + this->_wire = (_i2cBus==I2C_ONE) ? &Wire : &Wire1; +#endif + this->_frequency = _frequency; + } + + bool connect() { +#if !defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH8266) + _wire->begin(); +#else + // On ESP32 arduino, -1 means 'don't change pins', someone else has called begin for us. + if(this->_sda != -1) + _wire->begin(this->_sda, this->_scl); +#endif + // Let's use ~700khz if ESP8266 is in 160Mhz mode + // this will be limited to ~400khz if the ESP8266 in 80Mhz mode. + if(this->_frequency != -1) + _wire->setClock(this->_frequency); + return true; + } + + void display(void) { + initI2cIfNeccesary(); + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = UINT8_MAX; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = UINT8_MAX; + uint8_t maxBoundX = 0; + + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (displayHeight / 8); y++) { + for (x = 0; x < displayWidth; x++) { + uint16_t pos = x + y * displayWidth; + if (buffer[pos] != buffer_back[pos]) { + minBoundY = _min(minBoundY, y); + maxBoundY = _max(maxBoundY, y); + minBoundX = _min(minBoundX, x); + maxBoundX = _max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + yield(); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + if (minBoundY == UINT8_MAX) return; + + // Calculate the colum offset + uint8_t minBoundXp2H = (minBoundX + 2) & 0x0F; + uint8_t minBoundXp2L = 0x10 | ((minBoundX + 2) >> 4 ); + + byte k = 0; + for (y = minBoundY; y <= maxBoundY; y++) { + sendCommand(0xB0 + y); + sendCommand(minBoundXp2H); + sendCommand(minBoundXp2L); + for (x = minBoundX; x <= maxBoundX; x++) { + if (k == 0) { + _wire->beginTransmission(_address); + _wire->write(0x40); + } + _wire->write(buffer[x + y * displayWidth]); + k++; + if (k == 16) { + _wire->endTransmission(); + k = 0; + } + } + if (k != 0) { + _wire->endTransmission(); + k = 0; + } + yield(); + } + + if (k != 0) { + _wire->endTransmission(); + } + #else + uint8_t * p = &buffer[0]; + for (uint8_t y=0; y<8; y++) { + sendCommand(0xB0+y); + sendCommand(0x02); + sendCommand(0x10); + for( uint8_t x=0; x<8; x++) { + _wire->beginTransmission(_address); + _wire->write(0x40); + for (uint8_t k = 0; k < 16; k++) { + _wire->write(*p++); + } + _wire->endTransmission(); + } + } + #endif + } + + void setI2cAutoInit(bool doI2cAutoInit) { + _doI2cAutoInit = doI2cAutoInit; + } + + private: + int getBufferOffset(void) { + return 0; + } + inline void sendCommand(uint8_t command) __attribute__((always_inline)){ + _wire->beginTransmission(_address); + _wire->write(0x80); + _wire->write(command); + _wire->endTransmission(); + } + + void initI2cIfNeccesary() { + if (_doI2cAutoInit) { +#ifdef ARDUINO_ARCH_AVR + _wire->begin(); +#else + _wire->begin(this->_sda, this->_scl); +#endif + } + } + +}; + +#endif diff --git a/lib/esp8266-oled-ssd1306-master/src/SSD1306.h b/lib/esp8266-oled-ssd1306-master/src/SSD1306.h new file mode 100644 index 00000000..f6bd554c --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/src/SSD1306.h @@ -0,0 +1,39 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * 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. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef SSD1306_h +#define SSD1306_h +#include "SSD1306Wire.h" + +// For legacy support make SSD1306 an alias for SSD1306 +typedef SSD1306Wire SSD1306; + + +#endif diff --git a/lib/esp8266-oled-ssd1306-master/src/SSD1306Brzo.h b/lib/esp8266-oled-ssd1306-master/src/SSD1306Brzo.h new file mode 100644 index 00000000..fbcffcda --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/src/SSD1306Brzo.h @@ -0,0 +1,167 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * 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. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef SSD1306Brzo_h +#define SSD1306Brzo_h + +#include "OLEDDisplay.h" +#include + +#if F_CPU == 160000000L + #define BRZO_I2C_SPEED 1000 +#else + #define BRZO_I2C_SPEED 800 +#endif + +class SSD1306Brzo : public OLEDDisplay { + private: + uint8_t _address; + uint8_t _sda; + uint8_t _scl; + + public: + SSD1306Brzo(uint8_t _address, uint8_t _sda, uint8_t _scl, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64) { + setGeometry(g); + + this->_address = _address; + this->_sda = _sda; + this->_scl = _scl; + } + + bool connect(){ + brzo_i2c_setup(_sda, _scl, 0); + return true; + } + + void display(void) { + const int x_offset = (128 - this->width()) / 2; + + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = UINT8_MAX; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = UINT8_MAX; + uint8_t maxBoundX = 0; + + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (this->height() / 8); y++) { + for (x = 0; x < this->width(); x++) { + uint16_t pos = x + y * this->width(); + if (buffer[pos] != buffer_back[pos]) { + minBoundY = _min(minBoundY, y); + maxBoundY = _max(maxBoundY, y); + minBoundX = _min(minBoundX, x); + maxBoundX = _max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + yield(); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + if (minBoundY == UINT8_MAX) return; + + sendCommand(COLUMNADDR); + sendCommand(x_offset + minBoundX); + sendCommand(x_offset + maxBoundX); + + sendCommand(PAGEADDR); + sendCommand(minBoundY); + sendCommand(maxBoundY); + + byte k = 0; + + int buflen = ( this->width() / 8 ) + 1; + + uint8_t sendBuffer[buflen]; + sendBuffer[0] = 0x40; + brzo_i2c_start_transaction(this->_address, BRZO_I2C_SPEED); + for (y = minBoundY; y <= maxBoundY; y++) { + for (x = minBoundX; x <= maxBoundX; x++) { + k++; + sendBuffer[k] = buffer[x + y * this->width()]; + if (k == (buflen-1)) { + brzo_i2c_write(sendBuffer, buflen, true); + k = 0; + } + } + yield(); + } + brzo_i2c_write(sendBuffer, k + 1, true); + brzo_i2c_end_transaction(); + #else + // No double buffering + sendCommand(COLUMNADDR); + + sendCommand(x_offset); + sendCommand(x_offset + (this->width() - 1)); + + sendCommand(PAGEADDR); + sendCommand(0x0); + sendCommand((this->height() / 8) - 1); + + int buflen = ( this->width() / 8 ) + 1; + + uint8_t sendBuffer[buflen]; + sendBuffer[0] = 0x40; + + brzo_i2c_start_transaction(this->_address, BRZO_I2C_SPEED); + + for (uint16_t i=0; i + +#ifndef UINT8_MAX + #define UINT8_MAX 0xff +#endif + +class SSD1306I2C : public OLEDDisplay { +public: + SSD1306I2C(uint8_t _address, PinName _sda, PinName _scl, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64) { + setGeometry(g); + + this->_address = _address << 1; // convert from 7 to 8 bit for mbed. + this->_sda = _sda; + this->_scl = _scl; + _i2c = new I2C(_sda, _scl); + } + + bool connect() { + // mbed supports 100k and 400k some device maybe 1000k +#ifdef TARGET_STM32L4 + _i2c->frequency(1000000); +#else + _i2c->frequency(400000); +#endif + return true; + } + + void display(void) { + const int x_offset = (128 - this->width()) / 2; +#ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = UINT8_MAX; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = UINT8_MAX; + uint8_t maxBoundX = 0; + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (this->height() / 8); y++) { + for (x = 0; x < this->width(); x++) { + uint16_t pos = x + y * this->width(); + if (buffer[pos] != buffer_back[pos]) { + minBoundY = std::min(minBoundY, y); + maxBoundY = std::max(maxBoundY, y); + minBoundX = std::min(minBoundX, x); + maxBoundX = std::max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + yield(); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + + if (minBoundY == UINT8_MAX) return; + + sendCommand(COLUMNADDR); + sendCommand(x_offset + minBoundX); // column start address (0 = reset) + sendCommand(x_offset + maxBoundX); // column end address (127 = reset) + + sendCommand(PAGEADDR); + sendCommand(minBoundY); // page start address + sendCommand(maxBoundY); // page end address + + for (y = minBoundY; y <= maxBoundY; y++) { + uint8_t *start = &buffer[(minBoundX + y * this->width())-1]; + uint8_t save = *start; + + *start = 0x40; // control + _i2c->write(_address, (char *)start, (maxBoundX-minBoundX) + 1 + 1); + *start = save; + } +#else + + sendCommand(COLUMNADDR); + sendCommand(x_offset); // column start address (0 = reset) + sendCommand(x_offset + (this->width() - 1));// column end address (127 = reset) + + sendCommand(PAGEADDR); + sendCommand(0x0); // page start address (0 = reset) + + if (geometry == GEOMETRY_128_64) { + sendCommand(0x7); + } else if (geometry == GEOMETRY_128_32) { + sendCommand(0x3); + } + + buffer[-1] = 0x40; // control + _i2c->write(_address, (char *)&buffer[-1], displayBufferSize + 1); +#endif + } + +private: + int getBufferOffset(void) { + return 0; + } + + inline void sendCommand(uint8_t command) __attribute__((always_inline)) { + char _data[2]; + _data[0] = 0x80; // control + _data[1] = command; + _i2c->write(_address, _data, sizeof(_data)); + } + + uint8_t _address; + PinName _sda; + PinName _scl; + I2C *_i2c; +}; + +#endif + +#endif diff --git a/lib/esp8266-oled-ssd1306-master/src/SSD1306Spi.h b/lib/esp8266-oled-ssd1306-master/src/SSD1306Spi.h new file mode 100644 index 00000000..08cb4a80 --- /dev/null +++ b/lib/esp8266-oled-ssd1306-master/src/SSD1306Spi.h @@ -0,0 +1,163 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn + * Copyright (c) 2018 by Fabrice Weinberg + * + * 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. + * + * ThingPulse invests considerable time and money to develop these open source libraries. + * Please support us by buying our products (and not the clones) from + * https://thingpulse.com + * + */ + +#ifndef SSD1306Spi_h +#define SSD1306Spi_h + +#include "OLEDDisplay.h" +#include + +#if F_CPU == 160000000L + #define BRZO_I2C_SPEED 1000 +#else + #define BRZO_I2C_SPEED 800 +#endif + +class SSD1306Spi : public OLEDDisplay { + private: + uint8_t _rst; + uint8_t _dc; + uint8_t _cs; + + public: + SSD1306Spi(uint8_t _rst, uint8_t _dc, uint8_t _cs, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64) { + setGeometry(g); + + this->_rst = _rst; + this->_dc = _dc; + this->_cs = _cs; + } + + bool connect(){ + pinMode(_dc, OUTPUT); + pinMode(_cs, OUTPUT); + pinMode(_rst, OUTPUT); + + SPI.begin (); + SPI.setClockDivider (SPI_CLOCK_DIV2); + + // Pulse Reset low for 10ms + digitalWrite(_rst, HIGH); + delay(1); + digitalWrite(_rst, LOW); + delay(10); + digitalWrite(_rst, HIGH); + return true; + } + + void display(void) { + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = UINT8_MAX; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = UINT8_MAX; + uint8_t maxBoundX = 0; + + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (displayHeight / 8); y++) { + for (x = 0; x < displayWidth; x++) { + uint16_t pos = x + y * displayWidth; + if (buffer[pos] != buffer_back[pos]) { + minBoundY = _min(minBoundY, y); + maxBoundY = _max(maxBoundY, y); + minBoundX = _min(minBoundX, x); + maxBoundX = _max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + yield(); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + if (minBoundY == UINT8_MAX) return; + + sendCommand(COLUMNADDR); + sendCommand(minBoundX); + sendCommand(maxBoundX); + + sendCommand(PAGEADDR); + sendCommand(minBoundY); + sendCommand(maxBoundY); + + digitalWrite(_cs, HIGH); + digitalWrite(_dc, HIGH); // data mode + digitalWrite(_cs, LOW); + for (y = minBoundY; y <= maxBoundY; y++) { + for (x = minBoundX; x <= maxBoundX; x++) { + SPI.transfer(buffer[x + y * displayWidth]); + } + yield(); + } + digitalWrite(_cs, HIGH); + #else + // No double buffering + sendCommand(COLUMNADDR); + sendCommand(0x0); + sendCommand(0x7F); + + sendCommand(PAGEADDR); + sendCommand(0x0); + + if (geometry == GEOMETRY_128_64 || geometry == GEOMETRY_64_48 || geometry == GEOMETRY_64_32 ) { + sendCommand(0x7); + } else if (geometry == GEOMETRY_128_32) { + sendCommand(0x3); + } + + digitalWrite(_cs, HIGH); + digitalWrite(_dc, HIGH); // data mode + digitalWrite(_cs, LOW); + for (uint16_t i=0; i +#include + +#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_STM32) +#define _min min +#define _max max +#endif +//-------------------------------------- + +class SSD1306Wire : public OLEDDisplay { + private: + uint8_t _address; + int _sda; + int _scl; + bool _doI2cAutoInit = false; + TwoWire* _wire = NULL; + int _frequency; + + public: + + /** + * Create and initialize the Display using Wire library + * + * Beware for retro-compatibility default values are provided for all parameters see below. + * Please note that if you don't wan't SD1306Wire to initialize and change frequency speed ot need to + * ensure -1 value are specified for all 3 parameters. This can be usefull to control TwoWire with multiple + * device on the same bus. + * + * @param _address I2C Display address + * @param _sda I2C SDA pin number, default to -1 to skip Wire begin call + * @param _scl I2C SCL pin number, default to -1 (only SDA = -1 is considered to skip Wire begin call) + * @param g display geometry dafault to generic GEOMETRY_128_64, see OLEDDISPLAY_GEOMETRY definition for other options + * @param _i2cBus on ESP32 with 2 I2C HW buses, I2C_ONE for 1st Bus, I2C_TWO fot 2nd bus, default I2C_ONE + * @param _frequency for Frequency by default Let's use ~700khz if ESP8266 is in 160Mhz mode, this will be limited to ~400khz if the ESP8266 in 80Mhz mode + */ + SSD1306Wire(uint8_t _address, int _sda = -1, int _scl = -1, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64, HW_I2C _i2cBus = I2C_ONE, int _frequency = 700000) { + setGeometry(g); + + this->_address = _address; + this->_sda = _sda; + this->_scl = _scl; +#if !defined(ARDUINO_ARCH_ESP32) + this->_wire = &Wire; +#else + this->_wire = (_i2cBus==I2C_ONE) ? &Wire : &Wire1; +#endif + this->_frequency = _frequency; + } + + bool connect() { +#if !defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH_ESP8266) + _wire->begin(); +#else + // On ESP32 arduino, -1 means 'don't change pins', someone else has called begin for us. + if(this->_sda != -1) + _wire->begin(this->_sda, this->_scl); +#endif + // Let's use ~700khz if ESP8266 is in 160Mhz mode + // this will be limited to ~400khz if the ESP8266 in 80Mhz mode. + if(this->_frequency != -1) + _wire->setClock(this->_frequency); + return true; + } + + void display(void) { + initI2cIfNeccesary(); + const int x_offset = (128 - this->width()) / 2; + #ifdef OLEDDISPLAY_DOUBLE_BUFFER + uint8_t minBoundY = UINT8_MAX; + uint8_t maxBoundY = 0; + + uint8_t minBoundX = UINT8_MAX; + uint8_t maxBoundX = 0; + uint8_t x, y; + + // Calculate the Y bounding box of changes + // and copy buffer[pos] to buffer_back[pos]; + for (y = 0; y < (this->height() / 8); y++) { + for (x = 0; x < this->width(); x++) { + uint16_t pos = x + y * this->width(); + if (buffer[pos] != buffer_back[pos]) { + minBoundY = std::min(minBoundY, y); + maxBoundY = std::max(maxBoundY, y); + minBoundX = std::min(minBoundX, x); + maxBoundX = std::max(maxBoundX, x); + } + buffer_back[pos] = buffer[pos]; + } + yield(); + } + + // If the minBoundY wasn't updated + // we can savely assume that buffer_back[pos] == buffer[pos] + // holdes true for all values of pos + + if (minBoundY == UINT8_MAX) return; + + sendCommand(COLUMNADDR); + sendCommand(x_offset + minBoundX); + sendCommand(x_offset + maxBoundX); + + sendCommand(PAGEADDR); + sendCommand(minBoundY); + sendCommand(maxBoundY); + + byte k = 0; + for (y = minBoundY; y <= maxBoundY; y++) { + for (x = minBoundX; x <= maxBoundX; x++) { + if (k == 0) { + _wire->beginTransmission(_address); + _wire->write(0x40); + } + + _wire->write(buffer[x + y * this->width()]); + k++; + if (k == 16) { + _wire->endTransmission(); + k = 0; + } + } + yield(); + } + + if (k != 0) { + _wire->endTransmission(); + } + #else + + sendCommand(COLUMNADDR); + sendCommand(x_offset); + sendCommand(x_offset + (this->width() - 1)); + + sendCommand(PAGEADDR); + sendCommand(0x0); + + for (uint16_t i=0; i < displayBufferSize; i++) { + _wire->beginTransmission(this->_address); + _wire->write(0x40); + for (uint8_t x = 0; x < 16; x++) { + _wire->write(buffer[i]); + i++; + } + i--; + _wire->endTransmission(); + } + #endif + } + + void setI2cAutoInit(bool doI2cAutoInit) { + _doI2cAutoInit = doI2cAutoInit; + } + + private: + int getBufferOffset(void) { + return 0; + } + inline void sendCommand(uint8_t command) __attribute__((always_inline)){ + initI2cIfNeccesary(); + _wire->beginTransmission(_address); + _wire->write(0x80); + _wire->write(command); + _wire->endTransmission(); + } + + void initI2cIfNeccesary() { + if (_doI2cAutoInit) { +#if !defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH_ESP8266) + _wire->begin(); +#else + _wire->begin(this->_sda, this->_scl); +#endif + } + } + +}; + +#endif diff --git a/platformio.ini b/platformio.ini index 9ea7a14c..cae98d16 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,9 +10,10 @@ [platformio] default_envs = - GUI_Generic_1M -; GUI_Generic_1M-en -; GUI_Generic_4M + GUI_Generic_1MB +; GUI_Generic_1MB-en + GUI_Generic_2MB +; GUI_Generic_4MB ; GUI_Generic_minimal ; GUI_Generic_lite ; GUI_Generic_sensors @@ -22,25 +23,11 @@ default_envs = [common] -platform = espressif8266 @ 2.6.0 -upload_speed = 256000 -monitor_speed = 74880 -upload_resetmethod = nodemcu -board_build.flash_mode = dout -board_build.ldscript = eagle.flash.1m64.ld -lib_deps = - milesburton/DallasTemperature@^3.9.1 - adafruit/DHT sensor library@^1.4.0 - paulstoffregen/OneWire@^2.3.5 - adafruit/Adafruit BME280 Library@^2.1.1 - datacute/DoubleResetDetector@^1.0.3 - closedcube/ClosedCube SHT31D@^1.5.1 - adafruit/Adafruit Si7021 Library@^1.3.0 - -build_flags = -D BUILD_VERSION='"GUI 1.0.1"' +build_flags = -D BUILD_VERSION='"GUI-Generic 1.4.3"' -w - -DATOMIC_FS_UPDATE - -DBEARSSL_SSL_BASIC + -D ATOMIC_FS_UPDATE + -D BEARSSL_SSL_BASIC + ;-D SUPLA_ENABLE_SSL -D SUPLA_OTA -D SUPLA_RELAY -D SUPLA_BUTTON @@ -57,62 +44,60 @@ build_flags = -D BUILD_VERSION='"GUI 1.0.1"' -D SUPLA_MAX6675 -D SUPLA_HC_SR04 -D SUPLA_IMPULSE_COUNTER + -D SUPLA_OLED + -D SUPLA_HLW8012 + -D SUPLA_MCP23017 + -D SUPLA_RGBW + -D SUPLA_PUSHOVER + -D SUPLA_DIRECT_LINKS + -D SUPLA_LED -extra_scripts = tools/copy_files.py - -[env:GUI_Generic_1M] -platform = ${common.platform} +[env] framework = arduino +platform = espressif8266@2.6.3 +upload_speed = 256000 +monitor_speed = 74880 +upload_resetmethod = nodemcu +board_build.flash_mode = dout +;set frequency to 160MHz +board_build.f_cpu = 160000000L +; set frequency to 80MHz +board_build.f_flash = 80000000L +lib_deps = + milesburton/DallasTemperature@^3.9.1 + adafruit/DHT sensor library@^1.4.0 + paulstoffregen/OneWire@^2.3.5 + adafruit/Adafruit BME280 Library@^2.1.1 + datacute/DoubleResetDetector@^1.0.3 + closedcube/ClosedCube SHT31D@^1.5.1 + adafruit/Adafruit Si7021 Library@^1.3.0 + xoseperez/HLW8012 @ ^1.1.1 +extra_scripts = tools/copy_files.py + +[env:GUI_Generic_1MB] board = esp8285 -upload_resetmethod = ${common.upload_resetmethod} -board_build.flash_mode = ${common.board_build.flash_mode} -board_build.ldscript = ${common.board_build.ldscript} -upload_speed = ${common.upload_speed} -monitor_speed = ${common.monitor_speed} -lib_deps = ${common.lib_deps} -extra_scripts = ${common.extra_scripts} -;build_unflags = -D SUPLA_OTA +board_build.ldscript = eagle.flash.1m64.ld build_flags = ${common.build_flags} -[env:GUI_Generic_1M-en] -platform = ${common.platform} -framework = arduino +[env:GUI_Generic_1MB-en] board = esp8285 -upload_resetmethod = ${common.upload_resetmethod} -board_build.flash_mode = ${common.board_build.flash_mode} -board_build.ldscript = ${common.board_build.ldscript} -upload_speed = ${common.upload_speed} -monitor_speed = ${common.monitor_speed} -lib_deps = ${common.lib_deps} -extra_scripts = ${common.extra_scripts} -;build_unflags = -D SUPLA_OTA +board_build.ldscript = eagle.flash.1m64.ld build_flags = ${common.build_flags} -D UI_LANGUAGE=en -[env:GUI_Generic_4M] -platform = ${common.platform} -framework = arduino +[env:GUI_Generic_2MB] +board = esp8285 +board_build.ldscript = eagle.flash.2m64.ld +build_flags = ${common.build_flags} + +[env:GUI_Generic_4MB] board = esp12e -upload_resetmethod = ${common.upload_resetmethod} -board_build.flash_mode = ${common.board_build.flash_mode} -board_build.ldscript = ${common.board_build.ldscript} -upload_speed = ${common.upload_speed} -monitor_speed = ${common.monitor_speed} -lib_deps = ${common.lib_deps} -extra_scripts = ${common.extra_scripts} +board_build.ldscript = eagle.flash.4m1m.ld build_flags = ${common.build_flags} [env:GUI_Generic_minimal] -platform = ${common.platform} board = esp8285 -framework = arduino -upload_resetmethod = ${common.upload_resetmethod} -board_build.flash_mode = ${common.board_build.flash_mode} -board_build.ldscript = ${common.board_build.ldscript} -upload_speed = ${common.upload_speed} -monitor_speed = ${common.monitor_speed} -lib_deps = ${common.lib_deps} -extra_scripts = ${common.extra_scripts} +board_build.ldscript = eagle.flash.1m64.ld build_flags = ${common.build_flags} build_unflags = -D SUPLA_DS18B20 -D SUPLA_DHT11 @@ -126,16 +111,8 @@ build_unflags = -D SUPLA_DS18B20 -D SUPLA_IMPULSE_COUNTER [env:GUI_Generic_lite] -platform = ${common.platform} board = esp8285 -framework = arduino -upload_resetmethod = ${common.upload_resetmethod} -board_build.flash_mode = ${common.board_build.flash_mode} -board_build.ldscript = ${common.board_build.ldscript} -upload_speed = ${common.upload_speed} -monitor_speed = ${common.monitor_speed} -lib_deps = ${common.lib_deps} -extra_scripts = ${common.extra_scripts} +board_build.ldscript = eagle.flash.1m64.ld build_flags = ${common.build_flags} build_unflags = -D SUPLA_DHT11 @@ -147,16 +124,8 @@ build_unflags = -D SUPLA_IMPULSE_COUNTER [env:GUI_Generic_sensors] -platform = ${common.platform} board = esp8285 -framework = arduino -upload_resetmethod = ${common.upload_resetmethod} -board_build.flash_mode = ${common.board_build.flash_mode} -board_build.ldscript = ${common.board_build.ldscript} -upload_speed = ${common.upload_speed} -monitor_speed = ${common.monitor_speed} -lib_deps = ${common.lib_deps} -extra_scripts = ${common.extra_scripts} +board_build.ldscript = eagle.flash.1m64.ld build_flags = ${common.build_flags} build_unflags = -D SUPLA_RELAY -D SUPLA_BUTTON @@ -165,30 +134,28 @@ build_unflags = -D SUPLA_RELAY -D SUPLA_CONFIG [env:GUI_Generic_DEBUG] -platform = ${common.platform} -framework = arduino board = nodemcuv2 -upload_resetmethod = ${common.upload_resetmethod} -board_build.flash_mode = ${common.board_build.flash_mode} -board_build.ldscript = ${common.board_build.ldscript} -upload_speed = ${common.upload_speed} -monitor_speed = ${common.monitor_speed} -lib_deps = ${common.lib_deps} -extra_scripts = ${common.extra_scripts} +board_build.ldscript = eagle.flash.4m1m.ld build_flags = ${common.build_flags} - -D GUI_Generic -D DEBUG_MODE [env:GUI_Generic_blank] -platform = ${common.platform} -framework = arduino board = nodemcuv2 -upload_resetmethod = ${common.upload_resetmethod} -board_build.flash_mode = ${common.board_build.flash_mode} -board_build.ldscript = ${common.board_build.ldscript} -upload_speed = ${common.upload_speed} -monitor_speed = ${common.monitor_speed} -lib_deps = ${common.lib_deps} -extra_scripts = ${common.extra_scripts} +board_build.ldscript = eagle.flash.4m1m.ld build_flags = ${common.build_flags} - \ No newline at end of file +build_unflags = -D SUPLA_OTA + -D SUPLA_RELAY + -D SUPLA_BUTTON + -D SUPLA_LIMIT_SWITCH + -D SUPLA_ROLLERSHUTTER + -D SUPLA_CONFIG + -D SUPLA_DS18B20 + -D SUPLA_DHT11 + -D SUPLA_DHT22 + -D SUPLA_SI7021_SONOFF + -D SUPLA_BME280 + -D SUPLA_SHT3x + -D SUPLA_SI7021 + -D SUPLA_MAX6675 + -D SUPLA_HC_SR04 + -D SUPLA_IMPULSE_COUNTER \ No newline at end of file diff --git a/src/GUI-Generic.ino b/src/GUI-Generic.ino index 7466b190..f894f7d4 100644 --- a/src/GUI-Generic.ino +++ b/src/GUI-Generic.ino @@ -13,40 +13,9 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include "GUI-Generic_Config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef SUPLA_BME280 -#include -#include "SuplaWebPageSensor.h" -#endif -#ifdef SUPLA_SHT3x -#include -#endif -#ifdef SUPLA_SI7021 -#include -#endif -#ifdef SUPLA_SI7021_SONOFF -#include -#endif -#ifdef SUPLA_MAX6675 -#include -#endif -#ifdef SUPLA_IMPULSE_COUNTER -#include -#endif #include "SuplaDeviceGUI.h" -#include "SuplaWebServer.h" + +#include #define DRD_TIMEOUT 5 // Number of seconds after reset during which a subseqent reset will be considered a double reset. #define DRD_ADDRESS 0 // RTC Memory Address for the DoubleResetDetector to use @@ -61,26 +30,21 @@ void setup() { } uint8_t nr, gpio; - String key; #if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) uint8_t rollershutters = ConfigManager->get(KEY_MAX_ROLLERSHUTTER)->getValueInt(); - if (ConfigESP->getGpio(FUNCTION_RELAY) != OFF_GPIO && ConfigManager->get(KEY_MAX_RELAY)->getValueInt() > 0) { - for (nr = 1; nr <= ConfigManager->get(KEY_MAX_RELAY)->getValueInt(); nr++) { + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_RELAY)->getValueInt(); nr++) { + if (ConfigESP->getGpio(nr, FUNCTION_RELAY) != OFF_GPIO) { #ifdef SUPLA_ROLLERSHUTTER if (rollershutters > 0) { #ifdef SUPLA_BUTTON - if (ConfigESP->getLevel(nr, FUNCTION_BUTTON) == Supla::ON_CHANGE && ConfigESP->getLevel(nr + 1, FUNCTION_BUTTON) == Supla::ON_CHANGE) { - Supla::GUI::addRolleShutterMomentary(ConfigESP->getGpio(nr, FUNCTION_RELAY), ConfigESP->getGpio(nr + 1, FUNCTION_RELAY), - ConfigESP->getGpio(nr, FUNCTION_BUTTON), ConfigESP->getGpio(nr + 1, FUNCTION_BUTTON), - ConfigESP->getLevel(nr, FUNCTION_RELAY)); + if (ConfigESP->getEvent(nr, FUNCTION_BUTTON) == Supla::Event::ON_CHANGE && ConfigESP->getEvent(nr + 1, FUNCTION_BUTTON) == Supla::Event::ON_CHANGE) { + Supla::GUI::addRolleShutterMomentary(nr); } else { #endif - Supla::GUI::addRolleShutter(ConfigESP->getGpio(nr, FUNCTION_RELAY), ConfigESP->getGpio(nr + 1, FUNCTION_RELAY), - ConfigESP->getGpio(nr, FUNCTION_BUTTON), ConfigESP->getGpio(nr + 1, FUNCTION_BUTTON), - ConfigESP->getLevel(nr, FUNCTION_RELAY)); + Supla::GUI::addRolleShutter(nr); #ifdef SUPLA_BUTTON } #endif @@ -89,8 +53,7 @@ void setup() { } else { #endif - Supla::GUI::addRelayButton(ConfigESP->getGpio(nr, FUNCTION_RELAY), ConfigESP->getGpio(nr, FUNCTION_BUTTON), - ConfigESP->getLevel(nr, FUNCTION_RELAY)); + Supla::GUI::addRelayButton(nr); #ifdef SUPLA_ROLLERSHUTTER } #endif @@ -99,8 +62,8 @@ void setup() { #endif #ifdef SUPLA_LIMIT_SWITCH - if (ConfigESP->getGpio(FUNCTION_LIMIT_SWITCH) != OFF_GPIO && ConfigManager->get(KEY_MAX_LIMIT_SWITCH)->getValueInt() > 0) { - for (nr = 1; nr <= ConfigManager->get(KEY_MAX_LIMIT_SWITCH)->getValueInt(); nr++) { + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_LIMIT_SWITCH)->getValueInt(); nr++) { + if (ConfigESP->getGpio(nr, FUNCTION_LIMIT_SWITCH) != OFF_GPIO) { new Supla::Sensor::Binary(ConfigESP->getGpio(nr, FUNCTION_LIMIT_SWITCH), true); } } @@ -111,100 +74,171 @@ void setup() { ConfigManager->get(KEY_CFG_MODE)->getValueInt(), ConfigESP->getLevel(FUNCTION_CFG_LED)); #endif -#ifdef SUPLA_DHT11 - if (ConfigESP->getGpio(FUNCTION_DHT11) != OFF_GPIO && ConfigManager->get(KEY_MAX_DHT11)->getValueInt() > 0) { - for (nr = 1; nr <= ConfigManager->get(KEY_MAX_DHT11)->getValueInt(); nr++) { - new Supla::Sensor::DHT(ConfigESP->getGpio(nr, FUNCTION_DHT11), DHT11); - } +#ifdef SUPLA_DS18B20 + if (ConfigESP->getGpio(FUNCTION_DS18B20) != OFF_GPIO) { + Supla::GUI::addDS18B20MultiThermometer(ConfigESP->getGpio(FUNCTION_DS18B20)); } #endif -#ifdef SUPLA_DHT22 - if (ConfigESP->getGpio(FUNCTION_DHT22) != OFF_GPIO && ConfigManager->get(KEY_MAX_DHT22)->getValueInt() > 0) { - for (nr = 1; nr <= ConfigManager->get(KEY_MAX_DHT22)->getValueInt(); nr++) { - new Supla::Sensor::DHT(ConfigESP->getGpio(nr, FUNCTION_DHT22), DHT22); +#ifdef SUPLA_DHT11 + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_DHT11)->getValueInt(); nr++) { + if (ConfigESP->getGpio(nr, FUNCTION_DHT11) != OFF_GPIO) { + auto dht11 = new Supla::Sensor::DHT(ConfigESP->getGpio(nr, FUNCTION_DHT11), DHT11); + + if (nr == 1) { + Supla::GUI::addConditionsTurnON(SENSOR_DHT11, dht11); + Supla::GUI::addConditionsTurnOFF(SENSOR_DHT11, dht11); + } } } #endif -#ifdef SUPLA_DS18B20 - if (ConfigESP->getGpio(FUNCTION_DS18B20) != OFF_GPIO) { - Supla::GUI::addDS18B20MultiThermometer(ConfigESP->getGpio(FUNCTION_DS18B20)); +#ifdef SUPLA_DHT22 + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_DHT22)->getValueInt(); nr++) { + if (ConfigESP->getGpio(nr, FUNCTION_DHT22) != OFF_GPIO) { + auto dht22 = new Supla::Sensor::DHT(ConfigESP->getGpio(nr, FUNCTION_DHT22), DHT22); + + if (nr == 1) { + Supla::GUI::addConditionsTurnON(SENSOR_DHT22, dht22); + Supla::GUI::addConditionsTurnOFF(SENSOR_DHT22, dht22); + } + } } #endif #ifdef SUPLA_SI7021_SONOFF if (ConfigESP->getGpio(FUNCTION_SI7021_SONOFF) != OFF_GPIO) { - new Supla::Sensor::Si7021Sonoff(ConfigESP->getGpio(FUNCTION_SI7021_SONOFF)); + auto si7021sonoff = new Supla::Sensor::Si7021Sonoff(ConfigESP->getGpio(FUNCTION_SI7021_SONOFF)); + Supla::GUI::addConditionsTurnON(SENSOR_SI7021_SONOFF, si7021sonoff); + Supla::GUI::addConditionsTurnOFF(SENSOR_SI7021_SONOFF, si7021sonoff); } #endif #ifdef SUPLA_HC_SR04 if (ConfigESP->getGpio(FUNCTION_TRIG) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_ECHO) != OFF_GPIO) { - new Supla::Sensor::HC_SR04(ConfigESP->getGpio(FUNCTION_TRIG), ConfigESP->getGpio(FUNCTION_ECHO)); + Supla::Sensor::HC_SR04 *hcsr04; + if (ConfigManager->get(KEY_HC_SR04_MAX_SENSOR_READ)->getValueInt() > 0) { + hcsr04 = new Supla::Sensor::HC_SR04(ConfigESP->getGpio(FUNCTION_TRIG), ConfigESP->getGpio(FUNCTION_ECHO), 0, + ConfigManager->get(KEY_HC_SR04_MAX_SENSOR_READ)->getValueInt(), + ConfigManager->get(KEY_HC_SR04_MAX_SENSOR_READ)->getValueInt(), 0); + } + else { + hcsr04 = new Supla::Sensor::HC_SR04(ConfigESP->getGpio(FUNCTION_TRIG), ConfigESP->getGpio(FUNCTION_ECHO)); + } + + Supla::GUI::addConditionsTurnON(SENSOR_HC_SR04, hcsr04); + Supla::GUI::addConditionsTurnOFF(SENSOR_HC_SR04, hcsr04); + } +#endif + +#ifdef SUPLA_MAX6675 + if (ConfigESP->getGpio(FUNCTION_CLK) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_CS) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_D0) != OFF_GPIO) { + auto thermocouple = + new Supla::Sensor::MAXThermocouple(ConfigESP->getGpio(FUNCTION_CLK), ConfigESP->getGpio(FUNCTION_CS), ConfigESP->getGpio(FUNCTION_D0)); + Supla::GUI::addConditionsTurnON(SENSOR_MAX6675, thermocouple); + Supla::GUI::addConditionsTurnOFF(SENSOR_MAX6675, thermocouple); + } +#endif + +#ifdef SUPLA_IMPULSE_COUNTER + if (ConfigManager->get(KEY_MAX_IMPULSE_COUNTER)->getValueInt() > 0) { + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_IMPULSE_COUNTER)->getValueInt(); nr++) { + if (ConfigESP->getGpio(nr, FUNCTION_IMPULSE_COUNTER) != OFF_GPIO) { + Supla::GUI::addImpulseCounter(ConfigESP->getGpio(nr, FUNCTION_IMPULSE_COUNTER), ConfigESP->getLevel(nr, FUNCTION_IMPULSE_COUNTER), + ConfigESP->getMemory(nr, FUNCTION_IMPULSE_COUNTER), + ConfigManager->get(KEY_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT)->getValueInt()); + } + } + } + +#endif + +#ifdef SUPLA_HLW8012 + if (ConfigESP->getGpio(FUNCTION_CF) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_CF1) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_SEL) != OFF_GPIO) { + Supla::GUI::addHLW8012(ConfigESP->getGpio(FUNCTION_CF), ConfigESP->getGpio(FUNCTION_CF1), ConfigESP->getGpio(FUNCTION_SEL)); } #endif #if defined(SUPLA_BME280) || defined(SUPLA_SI7021) || defined(SUPLA_SHT3x) || defined(SUPLA_HTU21D) || defined(SUPLA_SHT71) || \ - defined(SUPLA_BH1750) || defined(SUPLA_MAX44009) + defined(SUPLA_BH1750) || defined(SUPLA_MAX44009) || defined(SUPLA_OLED) || defined(SUPLA_MCP23017) if (ConfigESP->getGpio(FUNCTION_SDA) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_SCL) != OFF_GPIO) { Wire.begin(ConfigESP->getGpio(FUNCTION_SDA), ConfigESP->getGpio(FUNCTION_SCL)); #ifdef SUPLA_BME280 - switch (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_BME280).toInt()) { - case BME280_ADDRESS_0X76: - new Supla::Sensor::BME280(0x76, ConfigManager->get(KEY_ALTITUDE_BME280)->getValueInt()); - break; - case BME280_ADDRESS_0X77: - new Supla::Sensor::BME280(0x77, ConfigManager->get(KEY_ALTITUDE_BME280)->getValueInt()); - break; - case BME280_ADDRESS_0X76_AND_0X77: - new Supla::Sensor::BME280(0x76, ConfigManager->get(KEY_ALTITUDE_BME280)->getValueInt()); - new Supla::Sensor::BME280(0x77, ConfigManager->get(KEY_ALTITUDE_BME280)->getValueInt()); - break; + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_BME280).toInt()) { + Supla::Sensor::BME280 *bme280; + switch (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_BME280).toInt()) { + case BME280_ADDRESS_0X76: + bme280 = new Supla::Sensor::BME280(0x76, ConfigManager->get(KEY_ALTITUDE_BME280)->getValueInt()); + break; + case BME280_ADDRESS_0X77: + bme280 = new Supla::Sensor::BME280(0x77, ConfigManager->get(KEY_ALTITUDE_BME280)->getValueInt()); + break; + case BME280_ADDRESS_0X76_AND_0X77: + bme280 = new Supla::Sensor::BME280(0x76, ConfigManager->get(KEY_ALTITUDE_BME280)->getValueInt()); + new Supla::Sensor::BME280(0x77, ConfigManager->get(KEY_ALTITUDE_BME280)->getValueInt()); + break; + } + Supla::GUI::addConditionsTurnON(SENSOR_BME280, bme280); + Supla::GUI::addConditionsTurnOFF(SENSOR_BME280, bme280); } #endif #ifdef SUPLA_SHT3x - switch (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_SHT3x).toInt()) { - case SHT3x_ADDRESS_0X44: - new Supla::Sensor::SHT3x(0x44); - break; - case SHT3x_ADDRESS_0X45: - new Supla::Sensor::SHT3x(0x45); - break; - case SHT3x_ADDRESS_0X44_AND_0X45: - new Supla::Sensor::SHT3x(0x44); - new Supla::Sensor::SHT3x(0x45); - break; + + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_SHT3x).toInt()) { + Supla::Sensor::SHT3x *sht3x; + switch (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_SHT3x).toInt()) { + case SHT3x_ADDRESS_0X44: + sht3x = new Supla::Sensor::SHT3x(0x44); + break; + case SHT3x_ADDRESS_0X45: + sht3x = new Supla::Sensor::SHT3x(0x45); + break; + case SHT3x_ADDRESS_0X44_AND_0X45: + sht3x = new Supla::Sensor::SHT3x(0x44); + new Supla::Sensor::SHT3x(0x45); + break; + } + Supla::GUI::addConditionsTurnON(SENSOR_SHT3x, sht3x); + Supla::GUI::addConditionsTurnOFF(SENSOR_SHT3x, sht3x); } #endif #ifdef SUPLA_SI7021 - if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_SI7021).toInt()) { - new Supla::Sensor::Si7021(); + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_SI7021).toInt()) { + auto si7021 = new Supla::Sensor::Si7021(); + Supla::GUI::addConditionsTurnON(SENSOR_SI7021, si7021); + Supla::GUI::addConditionsTurnOFF(SENSOR_SI7021, si7021); } #endif - } -#endif -#ifdef SUPLA_MAX6675 - if (ConfigESP->getGpio(FUNCTION_CLK) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_CS) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_D0) != OFF_GPIO) { - new Supla::Sensor::MAX6675_K(ConfigESP->getGpio(FUNCTION_CLK), ConfigESP->getGpio(FUNCTION_CS), ConfigESP->getGpio(FUNCTION_D0)); - } +#ifdef SUPLA_MCP23017 + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_MCP23017).toInt()) { + if (!mcp1.begin(0)) + Serial.println(F("MCP23017 1 not found!")); // begin(uint8_t address) "Pin 100 - 115" + if (!mcp2.begin(1)) + Serial.println(F("MCP23017 2 not found!")); // begin(uint8_t address) "Pin 116 - 131" + if (!mcp3.begin(2)) + Serial.println(F("MCP23017 3 not found!")); // begin(uint8_t address) "Pin 132 - 147" + if (!mcp4.begin(3)) + Serial.println(F("MCP23017 4 not found!")); // begin(uint8_t address) "Pin 148 - 163" + } #endif -#ifdef SUPLA_IMPULSE_COUNTER - if (ConfigManager->get(KEY_MAX_IMPULSE_COUNTER)->getValueInt() > 0) { - for (nr = 1; nr <= ConfigManager->get(KEY_MAX_IMPULSE_COUNTER)->getValueInt(); nr++) { - if (ConfigESP->getGpio(FUNCTION_IMPULSE_COUNTER) != OFF_GPIO) { - Supla::GUI::addImpulseCounter(ConfigESP->getGpio(nr, FUNCTION_IMPULSE_COUNTER), ConfigESP->getLevel(nr, FUNCTION_IMPULSE_COUNTER), - ConfigESP->getMemory(nr, FUNCTION_IMPULSE_COUNTER), - ConfigManager->get(KEY_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT)->getValueInt()); - } +#ifdef SUPLA_OLED + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_OLED).toInt()) { + SuplaOled *oled = new SuplaOled(); + oled->addButtonOled(ConfigESP->getGpio(FUNCTION_CFG_BUTTON)); } +#endif } +#endif +#ifdef SUPLA_RGBW + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_RGBW)->getValueInt(); nr++) { + Supla::GUI::addRGBWLeds(nr); + } #endif Supla::GUI::begin(); diff --git a/src/GUI-Generic_Config.h b/src/GUI-Generic_Config.h index 6969251a..8f0992ce 100644 --- a/src/GUI-Generic_Config.h +++ b/src/GUI-Generic_Config.h @@ -1,7 +1,7 @@ #ifndef GUI_Generic_Config_h #define GUI_Generic_Config_h -#define USE_CUSTOM +// #define USE_CUSTOM // User configuration #ifdef USE_CUSTOM @@ -15,13 +15,14 @@ #define SUPLA_OTA // Language en - english, pl - polish (default if not defined UI_LANGUAGE), es- spanish, fr - french, de - german, -#define UI_LANGUAGE de +//#define UI_LANGUAGE de #define SUPLA_RELAY #define SUPLA_BUTTON #define SUPLA_LIMIT_SWITCH #define SUPLA_ROLLERSHUTTER #define SUPLA_CONFIG +#define SUPLA_LED // ##### 1Wire ##### #define SUPLA_DS18B20 @@ -33,6 +34,8 @@ #define SUPLA_BME280 #define SUPLA_SHT3x #define SUPLA_SI7021 +#define SUPLA_OLED +#define SUPLA_MCP23017 // #define SUPLA_HTU21D // 0x40 NOT SUPPORTED // #define SUPLA_SHT71 // 0x44 AND 0x45 NOT SUPPORTED // #define SUPLA_BH1750 // 0x23 AND 0x5C NOT SUPPORTED @@ -44,6 +47,27 @@ // ##### Other ##### #define SUPLA_HC_SR04 #define SUPLA_IMPULSE_COUNTER +#define SUPLA_HLW8012 +#define SUPLA_RGBW +#define SUPLA_PUSHOVER +#define SUPLA_DIRECT_LINKS +#endif // USE_CUSTOM + +#ifndef DEBUG_MODE +#define supla_lib_config_h_ // silences unnecessary debug messages "should be disabled by default" +#endif + +#if defined(SUPLA_ROLLERSHUTTER) || defined(SUPLA_OLED) || defined(SUPLA_RGBW) +#if !defined(SUPLA_BUTTON) +#define SUPLA_BUTTON +#endif #endif + +#if defined(SUPLA_ROLLERSHUTTER) || defined(SUPLA_PUSHOVER) || defined(SUPLA_DIRECT_LINKS) || defined(SUPLA_LED) +#if !defined(SUPLA_RELAY) +#define SUPLA_RELAY +#endif +#endif + #endif // GUI-Generic_Config_h diff --git a/src/GUIGenericCommon.cpp b/src/GUIGenericCommon.cpp new file mode 100644 index 00000000..30113cdf --- /dev/null +++ b/src/GUIGenericCommon.cpp @@ -0,0 +1,56 @@ +#include "GUIGenericCommon.h" +#include "SuplaDeviceGUI.h" + +uint8_t *HexToBytes(String _value) { + int size = 16; + uint8_t *buffer = (uint8_t *)malloc(sizeof(uint8_t) * (size)); + + for (int i = 0; i < size; i++) { + sscanf(&_value[i * 2], "%2hhx", &buffer[i]); + } + return buffer; +} + +int getCountSensorChannels() { + int maxFrame = 0; + for (auto element = Supla::Element::begin(); element != nullptr; element = element->next()) { + if (element->getChannel()) { + auto channel = element->getChannel(); + + if (channel->getChannelType() == SUPLA_CHANNELTYPE_THERMOMETER) { + maxFrame += 1; + } + + if (channel->getChannelType() == SUPLA_CHANNELTYPE_HUMIDITYANDTEMPSENSOR) { + maxFrame += 2; + } + } + + if (element->getSecondaryChannel()) { + auto channel = element->getSecondaryChannel(); + if (channel->getChannelType() == SUPLA_CHANNELTYPE_PRESSURESENSOR) { + maxFrame += 1; + } + } + } + + return maxFrame; +} + +uint32_t lowestRAM = 0; +uint32_t lowestFreeStack = 0; + +void checkRAM() { + uint32_t freeRAM = ESP.getFreeHeap(); + Serial.print(F("freeRAM: ")); + Serial.println(freeRAM); + if (freeRAM <= lowestRAM) { + lowestRAM = freeRAM; + } + uint32_t freeStack = ESP.getFreeContStack(); + Serial.print(F("freeStack: ")); + Serial.println(freeStack); + if (freeStack <= lowestFreeStack) { + lowestFreeStack = freeStack; + } +} \ No newline at end of file diff --git a/src/GUIGenericCommon.h b/src/GUIGenericCommon.h index 62672230..72f5c973 100644 --- a/src/GUIGenericCommon.h +++ b/src/GUIGenericCommon.h @@ -1,13 +1,18 @@ #ifndef GUI_GENERIC_COMMON_H #define GUI_GENERIC_COMMON_H +#include "language/common.h" #ifndef UI_LANGUAGE -#include "language/pl.h" -#else +#define UI_LANGUAGE pl +#endif #define QUOTE(x) QUOTE_1(x) #define QUOTE_1(x) #x #define INCLUDE_FILE(x) QUOTE(language/x.h) #include INCLUDE_FILE(UI_LANGUAGE) -#endif + +#include "SuplaDeviceGUI.h" + +uint8_t *HexToBytes(String _value); +int getCountSensorChannels(); #endif // GUI_GENERIC_COMMON_H diff --git a/src/Markup.cpp b/src/Markup.cpp index cd91f446..1d64c9a7 100644 --- a/src/Markup.cpp +++ b/src/Markup.cpp @@ -1,11 +1,21 @@ #include "Markup.h" #include "SuplaCommonPROGMEM.h" +void addForm(String& html, const String& method, const String& action) { + html += "
"; +} + +void addFormEnd(String& html) { + html += "
"; +} + void addFormHeader(String& html, const String& name) { html += F("
"); - html += F("

"); - html += name; - html += F("

"); + if (name != "\n") { + html += F("

"); + html += name; + html += F("

"); + } } void addFormHeaderEnd(String& html) { @@ -15,7 +25,7 @@ void addFormHeaderEnd(String& html) { void addTextBox(String& html, const String& input_id, const String& name, - const String& value_key, + const String& value, const String& placeholder, int minlength, int maxlength, @@ -36,7 +46,6 @@ void addTextBox(String& html, } html += F("' value='"); - String value = String(ConfigManager->get(value_key.c_str())->getValue()); if (value != placeholder) { html += value; } @@ -46,7 +55,7 @@ void addTextBox(String& html, html += F("'"); } if (maxlength > 0) { - html += F("' length='"); + html += F("' maxlength='"); html += maxlength; html += F("'"); } @@ -61,31 +70,155 @@ void addTextBox(String& html, html += F(" > "); + WebServer->sendHeader(); +} + +void addTextBox(String& html, + const String& input_id, + const String& name, + uint8_t value_key, + const String& placeholder, + int minlength, + int maxlength, + bool required, + bool readonly, + bool password) { + String value = String(ConfigManager->get(value_key)->getValue()); + return addTextBox(html, input_id, name, value, placeholder, minlength, maxlength, required, readonly, password); } void addTextBox( - String& html, const String& input_id, const String& name, const String& value_key, int minlength, int maxlength, bool required, bool readonly) { + String& html, const String& input_id, const String& name, uint8_t value_key, int minlength, int maxlength, bool required, bool readonly) { return addTextBox(html, input_id, name, value_key, "", minlength, maxlength, required, readonly, false); } -void addTextBoxPassword( - String& html, const String& input_id, const String& name, const String& value_key, int minlength, int maxlength, bool required) { +void addTextBox( + String& html, const String& input_id, const String& name, const String& value, int minlength, int maxlength, bool required, bool readonly) { + return addTextBox(html, input_id, name, value, "", minlength, maxlength, required, readonly, false); +} + +void addTextBoxPassword(String& html, const String& input_id, const String& name, uint8_t value_key, int minlength, int maxlength, bool required) { return addTextBox(html, input_id, name, value_key, "", minlength, maxlength, required, false, true); } -void addNumberBox(String& html, const String& input_id, const String& name, const String& value_key, uint16_t max) { +void addCheckBox(String& html, const String& input_id, const String& name, bool checked) { + html += F(""); + } + else { + html += F("'>"); + } + html += F(""); +} + +void addNumberBox(String& html, const String& input_id, const String& name, uint8_t value_key, int max) { html += F("= 0) { + html += F(" max='"); + html += String(max); + html += F("'"); + } + + html += F(" value='"); + html += String(ConfigManager->get(value_key)->getValue()); html += F("'>"); + WebServer->sendHeader(); +} + +void addNumberBox(String& html, const String& input_id, const String& name, const String& placeholder, bool required, const String& value) { + html += F(""); + WebServer->sendHeader(); +} + +void addLinkBox(String& html, const String& name, const String& url) { + html += F(""); + html += F(""); + html += F(""); + WebServer->sendHeader(); +} + +void addListGPIOBox(String& html, const String& input_id, const String& name, uint8_t function, uint8_t nr, bool underline) { + if (underline) { + html += F(""); - html += addListGPIOSelect(input_id.c_str(), function, nr); + addListGPIOSelect(html, input_id, function, nr); html += F(""); } -void addListBox(String& html, const String& input_id, const String& name, const char* const* array_P, uint8_t size, uint8_t selected) { +void addListBox(String& html, const String& input_id, const String& name, const char* const* array_P, uint8_t size, uint8_t selected, uint8_t nr) { html += F(""); } -String addListGPIOSelect(const char* input, uint8_t function, uint8_t nr) { - String page = ""; - page += F(""); + + for (uint8_t suported = 0; suported < size; suported++) { + html += F("
"); - return page; + addListBox(webContentBuffer, INPUT_CFG_MODE, S_CFG_MODE, CFG_MODE_P, 2, selected); + + addFormHeaderEnd(webContentBuffer); + addButtonSubmit(webContentBuffer, S_SAVE); + addFormEnd(webContentBuffer); + + addButton(webContentBuffer, S_RETURN, PATH_DEVICE_SETTINGS); + WebServer->sendHeaderEnd(); } diff --git a/src/SuplaWebPageConfig.h b/src/SuplaWebPageConfig.h index 7733bb33..c2d55089 100644 --- a/src/SuplaWebPageConfig.h +++ b/src/SuplaWebPageConfig.h @@ -19,7 +19,7 @@ class SuplaWebPageConfig { void handleConfigSave(); private: - String supla_webpage_config(int save); + void supla_webpage_config(int save); }; extern SuplaWebPageConfig *WebPageConfig; diff --git a/src/SuplaWebPageControl.cpp b/src/SuplaWebPageControl.cpp index 725a8ddf..d7153686 100644 --- a/src/SuplaWebPageControl.cpp +++ b/src/SuplaWebPageControl.cpp @@ -1,240 +1,435 @@ #include "SuplaWebPageControl.h" #include "SuplaDeviceGUI.h" #include "SuplaWebServer.h" -#include "SuplaCommonPROGMEM.h" #include "GUIGenericCommon.h" #include "Markup.h" SuplaWebPageControl *WebPageControl = new SuplaWebPageControl(); +#if defined(SUPLA_BUTTON) || defined(SUPLA_LIMIT_SWITCH) || defined(SUPLA_MCP23017) void SuplaWebPageControl::createWebPageControl() { String path; + +#if defined(SUPLA_BUTTON) || defined(SUPLA_MCP23017) path += PATH_START; path += PATH_CONTROL; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageControl::handleControl, this)); + WebServer->httpServer->on(path, std::bind(&SuplaWebPageControl::handleControl, this)); path = PATH_START; path += PATH_SAVE_CONTROL; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageControl::handleControlSave, this)); + WebServer->httpServer->on(path, std::bind(&SuplaWebPageControl::handleControlSave, this)); +#endif -#ifdef SUPLA_BUTTON - for (uint8_t i = 1; i <= MAX_GPIO; i++) { + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_MCP23017).toInt() != FUNCTION_OFF) { +#ifdef SUPLA_MCP23017 path = PATH_START; path += PATH_BUTTON_SET; - path += i; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageControl::handleButtonSet, this)); + WebServer->httpServer->on(path, HTTP_GET, std::bind(&SuplaWebPageControl::handleButtonSetMCP23017, this)); path = PATH_START; path += PATH_SAVE_BUTTON_SET; - path += i; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageControl::handleButtonSaveSet, this)); + WebServer->httpServer->on(path, HTTP_POST, std::bind(&SuplaWebPageControl::handleButtonSaveSetMCP23017, this)); +#endif + } + else { +#if defined(SUPLA_BUTTON) + for (uint8_t i = 1; i <= ConfigManager->get(KEY_MAX_BUTTON)->getValueInt(); i++) { + path = PATH_START; + path += PATH_BUTTON_SET; + path += i; + WebServer->httpServer->on(path, std::bind(&SuplaWebPageControl::handleButtonSet, this)); + + path = PATH_START; + path += PATH_SAVE_BUTTON_SET; + path += i; + WebServer->httpServer->on(path, std::bind(&SuplaWebPageControl::handleButtonSaveSet, this)); + } +#endif } + +#ifdef SUPLA_LIMIT_SWITCH + path = PATH_START; + path += PATH_SWITCH; + WebServer->httpServer->on(path, std::bind(&SuplaWebPageControl::handleLimitSwitch, this)); + path = PATH_START; + path += PATH_SAVE_SWITCH; + WebServer->httpServer->on(path, std::bind(&SuplaWebPageControl::handleLimitSwitchSave, this)); #endif } +#endif +#if defined(SUPLA_BUTTON) || defined(SUPLA_MCP23017) void SuplaWebPageControl::handleControl() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); + if (!WebServer->isLoggedIn()) { + return; } - WebServer->sendContent(supla_webpage_control(0)); + supla_webpage_control(0); } void SuplaWebPageControl::handleControlSave() { - // Serial.println(F("HTTP_POST - metoda handleControlSave")); - - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); - } - String key, input; - uint8_t nr, current_value, last_value; -#ifdef SUPLA_BUTTON - last_value = ConfigManager->get(KEY_MAX_BUTTON)->getValueInt(); - for (nr = 1; nr <= last_value; nr++) { - if (!WebServer->saveGPIO(INPUT_BUTTON_GPIO, FUNCTION_BUTTON, nr, INPUT_MAX_BUTTON)) { - WebServer->sendContent(supla_webpage_control(6)); - return; - } + if (!WebServer->isLoggedIn()) { + return; } - if (strcmp(WebServer->httpServer.arg(INPUT_MAX_BUTTON).c_str(), "") != 0) { - ConfigManager->set(KEY_MAX_BUTTON, WebServer->httpServer.arg(INPUT_MAX_BUTTON).c_str()); - } -#endif + uint8_t nr, last_value; -#ifdef SUPLA_LIMIT_SWITCH - last_value = ConfigManager->get(KEY_MAX_LIMIT_SWITCH)->getValueInt(); + last_value = ConfigManager->get(KEY_MAX_BUTTON)->getValueInt(); for (nr = 1; nr <= last_value; nr++) { - if (!WebServer->saveGPIO(INPUT_LIMIT_SWITCH_GPIO, FUNCTION_LIMIT_SWITCH, nr, INPUT_MAX_LIMIT_SWITCH)) { - WebServer->sendContent(supla_webpage_control(6)); - return; + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_MCP23017).toInt() != FUNCTION_OFF) { + if (!WebServer->saveGpioMCP23017(INPUT_BUTTON_GPIO, FUNCTION_BUTTON, nr, INPUT_MAX_BUTTON)) { + supla_webpage_control(6); + return; + } + } + else { + if (!WebServer->saveGPIO(INPUT_BUTTON_GPIO, FUNCTION_BUTTON, nr, INPUT_MAX_BUTTON)) { + supla_webpage_control(6); + return; + } } } - if (strcmp(WebServer->httpServer.arg(INPUT_MAX_LIMIT_SWITCH).c_str(), "") != 0) { - ConfigManager->set(KEY_MAX_LIMIT_SWITCH, WebServer->httpServer.arg(INPUT_MAX_LIMIT_SWITCH).c_str()); + if (strcmp(WebServer->httpServer->arg(INPUT_MAX_BUTTON).c_str(), "") != 0) { + ConfigManager->set(KEY_MAX_BUTTON, WebServer->httpServer->arg(INPUT_MAX_BUTTON).c_str()); } -#endif switch (ConfigManager->save()) { case E_CONFIG_OK: - // Serial.println(F("E_CONFIG_OK: Config save")); - WebServer->sendContent(supla_webpage_control(1)); + if (last_value >= ConfigManager->get(KEY_MAX_BUTTON)->getValueInt()) { + supla_webpage_control(1); + } + else { + supla_webpage_control(2); + ConfigESP->rebootESP(); + } break; case E_CONFIG_FILE_OPEN: - // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); - WebServer->sendContent(supla_webpage_control(2)); + supla_webpage_control(2); break; } } -String SuplaWebPageControl::supla_webpage_control(int save) { - uint8_t nr, suported, selected; - String pagebutton, key; +void SuplaWebPageControl::supla_webpage_control(int save) { + uint8_t nr, countFreeGpio; - pagebutton += WebServer->SuplaSaveResult(save); - pagebutton += WebServer->SuplaJavaScript(PATH_CONTROL); - pagebutton += F("
"); + WebServer->sendHeaderStart(); -#if (defined(SUPLA_BUTTON) && defined(SUPLA_RELAY)) || (defined(SUPLA_BUTTON) && defined(SUPLA_ROLLERSHUTTER)) - addFormHeader(pagebutton, String(S_GPIO_SETTINGS_FOR_BUTTONS)); - addNumberBox(pagebutton, INPUT_MAX_BUTTON, S_QUANTITY, KEY_MAX_BUTTON, ConfigESP->countFreeGpio(FUNCTION_BUTTON)); - for (nr = 1; nr <= ConfigManager->get(KEY_MAX_BUTTON)->getValueInt(); nr++) { - addListGPIOLinkBox(pagebutton, INPUT_BUTTON_GPIO, S_BUTTON, FUNCTION_BUTTON, PATH_BUTTON_SET, nr); + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_CONTROL); + addForm(webContentBuffer, F("post"), PATH_SAVE_CONTROL); + + addFormHeader(webContentBuffer, S_GPIO_SETTINGS_FOR_BUTTONS); + + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_MCP23017).toInt() != FUNCTION_OFF) { + countFreeGpio = 32; + } + else { + countFreeGpio = ConfigESP->countFreeGpio(FUNCTION_BUTTON); } - addFormHeaderEnd(pagebutton); -#endif -#ifdef SUPLA_LIMIT_SWITCH - addFormHeader(pagebutton, String(S_GPIO_SETTINGS_FOR_LIMIT_SWITCH)); - addNumberBox(pagebutton, INPUT_MAX_LIMIT_SWITCH, S_QUANTITY, KEY_MAX_LIMIT_SWITCH, ConfigESP->countFreeGpio(FUNCTION_LIMIT_SWITCH)); - for (nr = 1; nr <= ConfigManager->get(KEY_MAX_LIMIT_SWITCH)->getValueInt(); nr++) { - addListGPIOBox(pagebutton, INPUT_LIMIT_SWITCH_GPIO, S_LIMIT_SWITCH, FUNCTION_LIMIT_SWITCH, nr); + addNumberBox(webContentBuffer, INPUT_MAX_BUTTON, S_QUANTITY, KEY_MAX_BUTTON, countFreeGpio); + + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_BUTTON)->getValueInt(); nr++) { + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_MCP23017).toInt() != FUNCTION_OFF) { + addListMCP23017GPIOLinkBox(webContentBuffer, INPUT_BUTTON_GPIO, S_BUTTON, FUNCTION_BUTTON, PATH_BUTTON_SET, nr); + } + else { + addListGPIOLinkBox(webContentBuffer, INPUT_BUTTON_GPIO, S_BUTTON, FUNCTION_BUTTON, PATH_BUTTON_SET, nr); + } } - addFormHeaderEnd(pagebutton); -#endif + addFormHeaderEnd(webContentBuffer); - pagebutton += F("
"); - pagebutton += F("
"); - pagebutton += F(""); - return pagebutton; + addButtonSubmit(webContentBuffer, S_SAVE); + addFormEnd(webContentBuffer); + addButton(webContentBuffer, S_RETURN, PATH_DEVICE_SETTINGS); + + WebServer->sendHeaderEnd(); } +#endif -#if (defined(SUPLA_BUTTON) && defined(SUPLA_RELAY)) || (defined(SUPLA_BUTTON) && defined(SUPLA_ROLLERSHUTTER)) +#if defined(SUPLA_BUTTON) void SuplaWebPageControl::handleButtonSet() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); + if (!WebServer->isLoggedIn()) { + return; } - WebServer->sendContent(supla_webpage_button_set(0)); + supla_webpage_button_set(0); } void SuplaWebPageControl::handleButtonSaveSet() { // Serial.println(F("HTTP_POST - metoda handleRelaySaveSet")); - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); + if (!WebServer->isLoggedIn()) { + return; } - String readUrl, nr_button, key, input; - uint8_t place; + String readUrl, nr_button, input, path; + uint8_t place, key, gpio; - String path = PATH_START; + input.reserve(5); + readUrl.reserve(16); + nr_button.reserve(2); + path.reserve(16); + + path = PATH_START; path += PATH_SAVE_BUTTON_SET; - readUrl = WebServer->httpServer.uri(); + readUrl = WebServer->httpServer->uri(); place = readUrl.indexOf(path); nr_button = readUrl.substring(place + path.length(), place + path.length() + 3); - key = GPIO; - key += ConfigESP->getGpio(nr_button.toInt(), FUNCTION_BUTTON); + + gpio = ConfigESP->getGpio(nr_button.toInt(), FUNCTION_BUTTON); + key = KEY_GPIO + gpio; + + input = INPUT_BUTTON_INVERSED; + input += nr_button; + if (strcmp(WebServer->httpServer->arg(input).c_str(), "") != 0) { + ConfigManager->setElement(key, INVERSED_BUTTON, 1); + } + else { + ConfigManager->setElement(key, INVERSED_BUTTON, 0); + } input = INPUT_BUTTON_LEVEL; input += nr_button; - ConfigManager->setElement(key.c_str(), LEVEL, WebServer->httpServer.arg(input).toInt()); + if (strcmp(WebServer->httpServer->arg(input).c_str(), "") != 0) { + ConfigManager->setElement(key, PULL_UP_BUTTON, 1); + } + else { + ConfigManager->setElement(key, PULL_UP_BUTTON, 0); + } + + input = INPUT_BUTTON_EVENT; + input += nr_button; + ConfigManager->setElement(key, EVENT_BUTTON, WebServer->httpServer->arg(input).toInt()); + + input = INPUT_BUTTON_ACTION; + input += nr_button; + ConfigManager->setElement(key, ACTION_BUTTON, WebServer->httpServer->arg(input).toInt()); switch (ConfigManager->save()) { case E_CONFIG_OK: - // Serial.println(F("E_CONFIG_OK: Dane zapisane")); - WebServer->sendContent(supla_webpage_control(1)); + supla_webpage_button_set(1, nr_button.toInt()); break; - case E_CONFIG_FILE_OPEN: - // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); - WebServer->sendContent(supla_webpage_control(2)); + supla_webpage_button_set(2, nr_button.toInt()); break; } } -String SuplaWebPageControl::supla_webpage_button_set(int save) { - String readUrl, nr_button, key; - uint8_t place, selected, suported; +void SuplaWebPageControl::supla_webpage_button_set(int save, int nr) { + String path, readUrl, nr_button; + uint8_t place, selected; - String path = PATH_START; - path += PATH_BUTTON_SET; - readUrl = WebServer->httpServer.uri(); + path.reserve(10); + readUrl.reserve(11); + nr_button.reserve(2); - place = readUrl.indexOf(path); - nr_button = readUrl.substring(place + path.length(), place + path.length() + 3); + WebServer->sendHeaderStart(); + + if (nr != 0) { + webContentBuffer += SuplaJavaScript(PATH_BUTTON_SET + String(nr)); + nr_button = nr; + } + else { + path = PATH_START; + path += PATH_BUTTON_SET; + readUrl = WebServer->httpServer->uri(); - String page = ""; - page += WebServer->SuplaSaveResult(save); - page += WebServer->SuplaJavaScript(PATH_CONTROL); - uint8_t buttons = ConfigManager->get(KEY_MAX_BUTTON)->getValueInt(); - if (nr_button.toInt() <= buttons && ConfigESP->getGpio(nr_button.toInt(), FUNCTION_BUTTON) != OFF_GPIO) { - page += F("

"); - page += S_BUTTON_NR_SETTINGS; - page += F(" "); - page += nr_button; - page += F("

"); - page += F(""); - page += F("
"); + else { + if (!WebServer->saveGPIO(INPUT_LIMIT_SWITCH_GPIO, FUNCTION_LIMIT_SWITCH, nr, INPUT_MAX_LIMIT_SWITCH)) { + suplaWebpageLimitSwitch(6); + return; + } + } + } + + if (strcmp(WebServer->httpServer->arg(INPUT_MAX_LIMIT_SWITCH).c_str(), "") != 0) { + ConfigManager->set(KEY_MAX_LIMIT_SWITCH, WebServer->httpServer->arg(INPUT_MAX_LIMIT_SWITCH).c_str()); + } + + switch (ConfigManager->save()) { + case E_CONFIG_OK: + suplaWebpageLimitSwitch(1); + break; + case E_CONFIG_FILE_OPEN: + suplaWebpageLimitSwitch(2); + break; + } +} + +void SuplaWebPageControl::suplaWebpageLimitSwitch(int save) { + uint8_t nr, countFreeGpio; + + WebServer->sendHeaderStart(); + + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_SWITCH); + addForm(webContentBuffer, F("post"), PATH_SAVE_SWITCH); + + addFormHeader(webContentBuffer, S_GPIO_SETTINGS_FOR_LIMIT_SWITCH); + + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_MCP23017).toInt() != FUNCTION_OFF) { + countFreeGpio = 32; + } + else { + countFreeGpio = ConfigESP->countFreeGpio(FUNCTION_LIMIT_SWITCH); + } + + addNumberBox(webContentBuffer, INPUT_MAX_LIMIT_SWITCH, S_QUANTITY, KEY_MAX_LIMIT_SWITCH, countFreeGpio); + + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_LIMIT_SWITCH)->getValueInt(); nr++) { + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_MCP23017).toInt() != FUNCTION_OFF) { + addListMCP23017GPIOBox(webContentBuffer, INPUT_LIMIT_SWITCH_GPIO, S_LIMIT_SWITCH, FUNCTION_LIMIT_SWITCH, nr); + } + else { + addListGPIOBox(webContentBuffer, INPUT_LIMIT_SWITCH_GPIO, S_LIMIT_SWITCH, FUNCTION_LIMIT_SWITCH, nr); + } + } + addFormHeaderEnd(webContentBuffer); + + addButtonSubmit(webContentBuffer, S_SAVE); + addFormEnd(webContentBuffer); + addButton(webContentBuffer, S_RETURN, PATH_DEVICE_SETTINGS); + + WebServer->sendHeaderEnd(); +} +#endif + +#ifdef SUPLA_MCP23017 +void SuplaWebPageControl::handleButtonSetMCP23017() { + if (!WebServer->isLoggedIn()) { + return; + } + supla_webpage_button_set_MCP23017(0); +} + +void SuplaWebPageControl::supla_webpage_button_set_MCP23017(int save) { + uint8_t selected; + + WebServer->sendHeaderStart(); + + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_BUTTON_SET); + + addForm(webContentBuffer, F("post"), PATH_SAVE_BUTTON_SET); + addFormHeader(webContentBuffer, S_SETTINGS_FOR_BUTTONS); + + selected = ConfigESP->getPullUp(1, FUNCTION_BUTTON); + addCheckBox(webContentBuffer, INPUT_BUTTON_LEVEL, S_INTERNAL_PULL_UP, selected); + + selected = ConfigESP->getInversed(1, FUNCTION_BUTTON); + addCheckBox(webContentBuffer, INPUT_BUTTON_INVERSED, S_REVERSE_LOGIC, selected); + + selected = ConfigESP->getEvent(1, FUNCTION_BUTTON); + addListBox(webContentBuffer, INPUT_BUTTON_EVENT, S_REACTION_TO, TRIGGER_P, 3, selected); + + selected = ConfigESP->getAction(1, FUNCTION_BUTTON); + addListBox(webContentBuffer, INPUT_BUTTON_ACTION, S_ACTION, ACTION_P, 3, selected); + + addFormHeaderEnd(webContentBuffer); + addButtonSubmit(webContentBuffer, S_SAVE); + addFormEnd(webContentBuffer); + addButton(webContentBuffer, S_RETURN, PATH_CONTROL); + + WebServer->sendHeaderEnd(); +} + +void SuplaWebPageControl::handleButtonSaveSetMCP23017() { + if (!WebServer->isLoggedIn()) { + return; + } + + String input; + uint8_t key, gpio, pullup, inversed, event, action, address; + + input.reserve(10); + + input = INPUT_BUTTON_EVENT; + event = WebServer->httpServer->arg(input).toInt(); + + input = INPUT_BUTTON_LEVEL; + if (strcmp(WebServer->httpServer->arg(input).c_str(), "") != 0) { + pullup = 1; } else { - page += F("

"); - page += S_NO_BUTTON_NR; - page += F("

"); - page += nr_button; - page += F(""); - } - page += F("
"); - page += F("
"); - - return page; + pullup = 0; + } + + input = INPUT_BUTTON_INVERSED; + if (strcmp(WebServer->httpServer->arg(input).c_str(), "") != 0) { + inversed = 1; + } + else { + inversed = 0; + } + + input = INPUT_BUTTON_ACTION; + action = WebServer->httpServer->arg(input).toInt(); + + for (gpio = 0; gpio <= OFF_GPIO; gpio++) { + key = KEY_GPIO + gpio; + ConfigManager->setElement(key, PULL_UP_BUTTON, pullup); + ConfigManager->setElement(key, INVERSED_BUTTON, inversed); + ConfigManager->setElement(key, EVENT_BUTTON, event); + ConfigManager->setElement(key, ACTION_BUTTON, action); + } + switch (ConfigManager->save()) { + case E_CONFIG_OK: + supla_webpage_button_set_MCP23017(1); + break; + case E_CONFIG_FILE_OPEN: + supla_webpage_button_set_MCP23017(2); + break; + } } #endif diff --git a/src/SuplaWebPageControl.h b/src/SuplaWebPageControl.h index 88e17b98..32f24377 100644 --- a/src/SuplaWebPageControl.h +++ b/src/SuplaWebPageControl.h @@ -4,42 +4,54 @@ #include "SuplaWebServer.h" #include "SuplaDeviceGUI.h" -#define PATH_CONTROL "control" -#define PATH_SAVE_CONTROL "savecontrol" -#define PATH_BUTTON_SET "setbutton" -#define PATH_SAVE_BUTTON_SET "savesetbutton" -#define INPUT_TRIGGER "trs" -#define INPUT_BUTTON_SET "bts" -#define INPUT_BUTTON_GPIO "btg" -#define INPUT_BUTTON_LEVEL "icl" -#define INPUT_LIMIT_SWITCH_GPIO "lsg" -#define INPUT_MAX_BUTTON "mbt" -#define INPUT_MAX_LIMIT_SWITCH "mls" - -/*enum _trigger_button { - TRIGGER_PRESS, - TRIGGER_RELEASE, - TRIGGER_CHANGE -};*/ +#if defined(SUPLA_BUTTON) || defined(SUPLA_LIMIT_SWITCH) || defined(SUPLA_MCP23017) +#define PATH_CONTROL "control" +#define PATH_SWITCH "switch" +#define PATH_SAVE_SWITCH "saveswitch" +#define PATH_SAVE_CONTROL "savecontrol" +#define PATH_BUTTON_SET "setbutton" +#define PATH_SAVE_BUTTON_SET "savesetbutton" +#define INPUT_TRIGGER "trs" +#define INPUT_BUTTON_SET "bts" +#define INPUT_BUTTON_GPIO "btg" +#define INPUT_BUTTON_LEVEL "ibl" +#define INPUT_BUTTON_INVERSED "ibi" +#define INPUT_BUTTON_EVENT "icl" +#define INPUT_BUTTON_ACTION "bta" +#define INPUT_LIMIT_SWITCH_GPIO "lsg" +#define INPUT_MAX_BUTTON "mbt" +#define INPUT_MAX_LIMIT_SWITCH "mls" +#endif class SuplaWebPageControl { public: +#if defined(SUPLA_BUTTON) || defined(SUPLA_LIMIT_SWITCH) || defined(SUPLA_MCP23017) void createWebPageControl(); +#endif + +#if defined(SUPLA_BUTTON) || defined(SUPLA_MCP23017) void handleControl(); void handleControlSave(); + void supla_webpage_control(int save); +#endif -#if (defined(SUPLA_BUTTON) && defined(SUPLA_RELAY)) || (defined(SUPLA_RSUPLA_BUTTONELAY) || defined(SUPLA_ROLLERSHUTTER)) +#ifdef SUPLA_LIMIT_SWITCH + void handleLimitSwitch(); + void handleLimitSwitchSave(); + void suplaWebpageLimitSwitch(int save); +#endif + +#if defined(SUPLA_BUTTON) void handleButtonSet(); void handleButtonSaveSet(); + void supla_webpage_button_set(int save, int nr = 0); #endif - private: - String supla_webpage_control(int save); - -#ifdef SUPLA_BUTTON - String supla_webpage_button_set(int save); +#ifdef SUPLA_MCP23017 + void handleButtonSetMCP23017(); + void handleButtonSaveSetMCP23017(); + void supla_webpage_button_set_MCP23017(int save); #endif - }; extern SuplaWebPageControl* WebPageControl; diff --git a/src/SuplaWebPageDownload.cpp b/src/SuplaWebPageDownload.cpp new file mode 100644 index 00000000..51292f02 --- /dev/null +++ b/src/SuplaWebPageDownload.cpp @@ -0,0 +1,29 @@ +#include "SuplaWebPageDownload.h" +#include "SuplaDeviceGUI.h" +#include "FS.h" + +void createWebDownload() { + WebServer->httpServer->on(getURL(PATH_DOWNLOAD), handleDownload); +} + +void handleDownload() { + if (!WebServer->isLoggedIn()) { + return; + } + + File dataFile = SPIFFS.open(F(CONFIG_FILE_PATH), "r"); + + if (!dataFile) { + return; + } + + String str = F("attachment; filename=config_"); + str += ConfigManager->get(KEY_HOST_NAME)->getValue(); + str += Supla::Channel::reg_dev.SoftVer; + str += '_'; + str += F(".dat"); + + WebServer->httpServer->sendHeader(F("Content-Disposition"), str); + WebServer->httpServer->streamFile(dataFile, F("application/octet-stream")); + dataFile.close(); +} \ No newline at end of file diff --git a/src/SuplaWebPageDownload.h b/src/SuplaWebPageDownload.h new file mode 100644 index 00000000..947be49e --- /dev/null +++ b/src/SuplaWebPageDownload.h @@ -0,0 +1,10 @@ + +#ifndef SuplaWebPageDownload_h +#define SuplaWebPageDownload_h + +#define PATH_DOWNLOAD "download" + +void createWebDownload(); +void handleDownload(); + +#endif // ifndef SuplaWebPageDownload_h diff --git a/src/SuplaWebPageOther.cpp b/src/SuplaWebPageOther.cpp new file mode 100644 index 00000000..052c8ef6 --- /dev/null +++ b/src/SuplaWebPageOther.cpp @@ -0,0 +1,335 @@ +#include "SuplaWebPageOther.h" + +void createWebPageOther() { +#if defined(SUPLA_HC_SR04) || defined(SUPLA_IMPULSE_COUNTER) || defined(SUPLA_HLW8012) || defined(SUPLA_RGBW) || defined(SUPLA_PUSHOVER) + WebServer->httpServer->on(getURL(PATH_OTHER), handleOther); + WebServer->httpServer->on(getURL(PATH_SAVE_OTHER), handleOtherSave); + +#if defined(SUPLA_IMPULSE_COUNTER) + for (uint8_t i = 1; i <= ConfigManager->get(KEY_MAX_IMPULSE_COUNTER)->getValueInt(); i++) { + WebServer->httpServer->on(getURL(PATH_IMPULSE_COUNTER_SET, i), handleImpulseCounterSet); + WebServer->httpServer->on(getURL(PATH_SAVE_IMPULSE_COUNTER_SET, i), handleImpulseCounterSaveSet); + } +#endif + +#if defined(SUPLA_HLW8012) + if (ConfigESP->getGpio(FUNCTION_CF) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_CF1) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_SEL) != OFF_GPIO) { + WebServer->httpServer->on(getURL(PATH_HLW8012_CALIBRATE), handleHLW8012Calibrate); + WebServer->httpServer->on(getURL(PATH_SAVE_HLW8012_CALIBRATE), handleHLW8012CalibrateSave); + } +#endif +#endif +} + +#if defined(SUPLA_HC_SR04) || defined(SUPLA_IMPULSE_COUNTER) || defined(SUPLA_HLW8012) || defined(SUPLA_RGBW) || defined(SUPLA_PUSHOVER) +void handleOther() { + if (!WebServer->isLoggedIn()) { + return; + } + suplaWebPageOther(0); +} + +void handleOtherSave() { + if (!WebServer->isLoggedIn()) { + return; + } + + uint8_t nr, last_value; + +#ifdef SUPLA_HC_SR04 + if (!WebServer->saveGPIO(INPUT_TRIG_GPIO, FUNCTION_TRIG)) { + suplaWebPageOther(6); + return; + } + if (!WebServer->saveGPIO(INPUT_ECHO_GPIO, FUNCTION_ECHO)) { + suplaWebPageOther(6); + return; + } + ConfigManager->set(KEY_HC_SR04_MAX_SENSOR_READ, WebServer->httpServer->arg(INPUT_HC_SR04_MAX_SENSOR_READ).c_str()); +#endif + +#ifdef SUPLA_IMPULSE_COUNTER + // Supla::GUI::impulseCounter[0]->setCounter((unsigned long long)WebServer->httpServer->arg(INPUT_IMPULSE_COUNTER_CHANGE_VALUE).toInt()); + + last_value = ConfigManager->get(KEY_MAX_IMPULSE_COUNTER)->getValueInt(); + for (nr = 1; nr <= last_value; nr++) { + if (!WebServer->saveGPIO(INPUT_IMPULSE_COUNTER_GPIO, FUNCTION_IMPULSE_COUNTER, nr, INPUT_MAX_IMPULSE_COUNTER)) { + suplaWebPageOther(6); + return; + } + } + + if (strcmp(WebServer->httpServer->arg(INPUT_MAX_IMPULSE_COUNTER).c_str(), "") != 0) { + ConfigManager->set(KEY_MAX_IMPULSE_COUNTER, WebServer->httpServer->arg(INPUT_MAX_IMPULSE_COUNTER).c_str()); + } +#endif + +#ifdef SUPLA_RGBW + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_RGBW)->getValueInt(); nr++) { + if (!WebServer->saveGPIO(INPUT_RGBW_RED, FUNCTION_RGBW_RED, nr, INPUT_RGBW_MAX) || + !WebServer->saveGPIO(INPUT_RGBW_GREEN, FUNCTION_RGBW_GREEN, nr, INPUT_RGBW_MAX) || + !WebServer->saveGPIO(INPUT_RGBW_BLUE, FUNCTION_RGBW_BLUE, nr, INPUT_RGBW_MAX) || + !WebServer->saveGPIO(INPUT_RGBW_BRIGHTNESS, FUNCTION_RGBW_BRIGHTNESS, nr, INPUT_RGBW_MAX)) { + suplaWebPageOther(6); + return; + } + } + ConfigManager->set(KEY_MAX_RGBW, WebServer->httpServer->arg(INPUT_RGBW_MAX).c_str()); +#endif + +#ifdef SUPLA_HLW8012 + if (!WebServer->saveGPIO(INPUT_CF, FUNCTION_CF) || !WebServer->saveGPIO(INPUT_CF1, FUNCTION_CF1) || !WebServer->saveGPIO(INPUT_SEL, FUNCTION_SEL)) { + suplaWebPageOther(6); + return; + } +#endif + +#if defined(SUPLA_PUSHOVER) + if (strcmp(WebServer->httpServer->arg(INPUT_PUSHOVER_TOKEN).c_str(), "") != 0) { + ConfigManager->set(KEY_PUSHOVER_TOKEN, WebServer->httpServer->arg(INPUT_PUSHOVER_TOKEN).c_str()); + } + + if (strcmp(WebServer->httpServer->arg(INPUT_PUSHOVER_USER).c_str(), "") != 0) { + ConfigManager->set(KEY_PUSHOVER_USER, WebServer->httpServer->arg(INPUT_PUSHOVER_USER).c_str()); + } +#endif + + switch (ConfigManager->save()) { + case E_CONFIG_OK: + suplaWebPageOther(1); + break; + case E_CONFIG_FILE_OPEN: + // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); + suplaWebPageOther(2); + break; + } +} + +void suplaWebPageOther(int save) { + uint8_t nr, suported, selected; + + WebServer->sendHeaderStart(); + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_OTHER); + + addForm(webContentBuffer, F("post"), PATH_SAVE_OTHER); +#ifdef SUPLA_HC_SR04 + addFormHeader(webContentBuffer, String(S_GPIO_SETTINGS_FOR) + S_SPACE + S_HC_SR04); + addListGPIOBox(webContentBuffer, INPUT_TRIG_GPIO, F("TRIG"), FUNCTION_TRIG); + addListGPIOBox(webContentBuffer, INPUT_ECHO_GPIO, F("ECHO"), FUNCTION_ECHO); + addNumberBox(webContentBuffer, INPUT_HC_SR04_MAX_SENSOR_READ, S_DEPTH_CM, S_SENSOR_READING_DISTANCE, false, + ConfigManager->get(KEY_HC_SR04_MAX_SENSOR_READ)->getValue()); + addFormHeaderEnd(webContentBuffer); +#endif + +#ifdef SUPLA_IMPULSE_COUNTER + addFormHeader(webContentBuffer, String(S_GPIO_SETTINGS_FOR) + S_SPACE + S_IMPULSE_COUNTER); + addNumberBox(webContentBuffer, INPUT_MAX_IMPULSE_COUNTER, S_QUANTITY, KEY_MAX_IMPULSE_COUNTER, ConfigESP->countFreeGpio(FUNCTION_IMPULSE_COUNTER)); + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_IMPULSE_COUNTER)->getValueInt(); nr++) { + addListGPIOLinkBox(webContentBuffer, INPUT_IMPULSE_COUNTER_GPIO, F("IC GPIO"), FUNCTION_IMPULSE_COUNTER, PATH_IMPULSE_COUNTER_SET, nr); + } + addFormHeaderEnd(webContentBuffer); +#endif + +#ifdef SUPLA_HLW8012 + addFormHeader(webContentBuffer, String(S_GPIO_SETTINGS_FOR) + S_SPACE + S_HLW8012); + addListGPIOBox(webContentBuffer, INPUT_CF, F("CF"), FUNCTION_CF); + addListGPIOBox(webContentBuffer, INPUT_CF1, F("CF1"), FUNCTION_CF1); + addListGPIOBox(webContentBuffer, INPUT_SEL, F("SELi"), FUNCTION_SEL); + if (ConfigESP->getGpio(FUNCTION_CF) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_CF1) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_SEL) != OFF_GPIO) { + addLinkBox(webContentBuffer, S_CALIBRATION, PATH_HLW8012_CALIBRATE); + } + addFormHeaderEnd(webContentBuffer); +#endif + +#ifdef SUPLA_RGBW + addFormHeader(webContentBuffer, String(S_GPIO_SETTINGS_FOR) + S_SPACE + S_RGBW_RGB_DIMMER); + addNumberBox(webContentBuffer, INPUT_RGBW_MAX, S_QUANTITY, KEY_MAX_RGBW, ConfigESP->countFreeGpio()); + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_RGBW)->getValueInt(); nr++) { + addListGPIOBox(webContentBuffer, INPUT_RGBW_RED, F("RED"), FUNCTION_RGBW_RED, nr, false); + addListGPIOBox(webContentBuffer, INPUT_RGBW_GREEN, F("GREEN"), FUNCTION_RGBW_GREEN, nr, false); + addListGPIOBox(webContentBuffer, INPUT_RGBW_BLUE, F("BLUE"), FUNCTION_RGBW_BLUE, nr, false); + addListGPIOBox(webContentBuffer, INPUT_RGBW_BRIGHTNESS, F("WHITE / DIMMER"), FUNCTION_RGBW_BRIGHTNESS, nr, false); + } + addFormHeaderEnd(webContentBuffer); +#endif + +#if defined(SUPLA_PUSHOVER) + addFormHeader(webContentBuffer, String(S_SETTING_FOR) + S_SPACE + S_PUSHOVER); + addTextBox(webContentBuffer, INPUT_PUSHOVER_TOKEN, F("Token"), KEY_PUSHOVER_TOKEN, 0, MAX_TOKEN_SIZE, false); + addTextBox(webContentBuffer, INPUT_PUSHOVER_USER, F("Users"), KEY_PUSHOVER_USER, 0, MAX_USER_SIZE, false); + addFormHeaderEnd(webContentBuffer); +#endif + + addButtonSubmit(webContentBuffer, S_SAVE); + addFormEnd(webContentBuffer); + addButton(webContentBuffer, S_RETURN, PATH_DEVICE_SETTINGS); + WebServer->sendHeaderEnd(); +} +#endif + +#ifdef SUPLA_IMPULSE_COUNTER +void handleImpulseCounterSet() { + if (!WebServer->isLoggedIn()) { + return; + } + supla_impulse_counter_set(0); +} + +void handleImpulseCounterSaveSet() { + if (!WebServer->isLoggedIn()) { + return; + } + + String readUrl, nr, input; + uint8_t place; + + String path = PATH_START; + path += PATH_SAVE_IMPULSE_COUNTER_SET; + readUrl = WebServer->httpServer->uri(); + + place = readUrl.indexOf(path); + nr = readUrl.substring(place + path.length(), place + path.length() + 3); + uint8_t key = KEY_GPIO + ConfigESP->getGpio(nr.toInt(), FUNCTION_IMPULSE_COUNTER); + + input = INPUT_IMPULSE_COUNTER_PULL_UP; + input += nr; + + if (strcmp(WebServer->httpServer->arg(input).c_str(), "") != 0) { + ConfigManager->setElement(key, MEMORY, 1); + } + else { + ConfigManager->setElement(key, MEMORY, 0); + } + + input = INPUT_IMPULSE_COUNTER_RAISING_EDGE; + input += nr; + if (strcmp(WebServer->httpServer->arg(input).c_str(), "") != 0) { + ConfigManager->setElement(key, LEVEL_RELAY, 1); + } + else { + ConfigManager->setElement(key, LEVEL_RELAY, 0); + } + + ConfigManager->set(KEY_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT, WebServer->httpServer->arg(INPUT_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT).c_str()); + Supla::GUI::impulseCounter[nr.toInt() - 1]->setCounter((unsigned long long)WebServer->httpServer->arg(INPUT_IMPULSE_COUNTER_CHANGE_VALUE).toInt()); + Supla::Storage::ScheduleSave(2000); + + switch (ConfigManager->save()) { + case E_CONFIG_OK: + // Serial.println(F("E_CONFIG_OK: Dane zapisane")); + suplaWebPageOther(1); + break; + + case E_CONFIG_FILE_OPEN: + // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); + suplaWebPageOther(2); + break; + } +} + +void supla_impulse_counter_set(int save) { + String readUrl, nr; + uint8_t place, selected, suported; + + String path = PATH_START; + path += PATH_IMPULSE_COUNTER_SET; + readUrl = WebServer->httpServer->uri(); + + place = readUrl.indexOf(path); + nr = readUrl.substring(place + path.length(), place + path.length() + 3); + + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_OTHER); + + if (nr.toInt() <= ConfigManager->get(KEY_MAX_IMPULSE_COUNTER)->getValueInt() && + ConfigESP->getGpio(nr.toInt(), FUNCTION_IMPULSE_COUNTER) != OFF_GPIO) { + addForm(webContentBuffer, F("post"), PATH_SAVE_IMPULSE_COUNTER_SET + nr); + addFormHeader(webContentBuffer, S_IMPULSE_COUNTER_SETTINGS_NR + nr); + + selected = ConfigESP->getMemory(nr.toInt(), FUNCTION_IMPULSE_COUNTER); + addCheckBox(webContentBuffer, INPUT_IMPULSE_COUNTER_PULL_UP + nr, S_IMPULSE_COUNTER_PULL_UP, selected); + + selected = ConfigESP->getLevel(nr.toInt(), FUNCTION_IMPULSE_COUNTER); + addCheckBox(webContentBuffer, INPUT_IMPULSE_COUNTER_RAISING_EDGE + nr, S_IMPULSE_COUNTER_RAISING_EDGE, selected); + + addNumberBox(webContentBuffer, INPUT_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT, S_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT, KEY_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT); + + uint32_t count = Supla::GUI::impulseCounter[nr.toInt() - 1]->getCounter(); + addNumberBox(webContentBuffer, INPUT_IMPULSE_COUNTER_CHANGE_VALUE, S_IMPULSE_COUNTER_CHANGE_VALUE, F(""), false, String(count)); + + addFormHeaderEnd(webContentBuffer); + addButtonSubmit(webContentBuffer, S_SAVE); + addFormEnd(webContentBuffer); + } + + addButton(webContentBuffer, S_RETURN, PATH_OTHER); + WebServer->sendContent(); +} +#endif + +#if defined(SUPLA_HLW8012) +void handleHLW8012Calibrate() { + if (!WebServer->isLoggedIn()) { + return; + } + suplaWebpageHLW8012Calibrate(0); +} + +void handleHLW8012CalibrateSave() { + if (!WebServer->isLoggedIn()) { + return; + } + + double calibPower, calibVoltage = 0; + String input = INPUT_CALIB_POWER; + if (strcmp(WebServer->httpServer->arg(input).c_str(), "") != 0) { + calibPower = WebServer->httpServer->arg(input).toDouble(); + } + + input = INPUT_CALIB_VOLTAGE; + if (strcmp(WebServer->httpServer->arg(input).c_str(), "") != 0) { + calibVoltage = WebServer->httpServer->arg(input).toDouble(); + } + + if (calibPower && calibVoltage) { +#if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) + for (int i = 0; i < Supla::GUI::relay.size(); i++) { + Supla::GUI::relay[i]->turnOn(); + } +#endif + Supla::GUI::counterHLW8012->calibrate(calibPower, calibVoltage); + suplaWebpageHLW8012Calibrate(1); + } + else { + suplaWebpageHLW8012Calibrate(6); + } +} + +void suplaWebpageHLW8012Calibrate(uint8_t save) { + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_HLW8012_CALIBRATE); + + addFormHeader(webContentBuffer); + webContentBuffer += F("

Current Multi: "); + webContentBuffer += Supla::GUI::counterHLW8012->getCurrentMultiplier(); + webContentBuffer += F("
Voltage Multi: "); + webContentBuffer += Supla::GUI::counterHLW8012->getVoltageMultiplier(); + webContentBuffer += F("
Power Multi: "); + webContentBuffer += Supla::GUI::counterHLW8012->getPowerMultiplier(); + webContentBuffer += F("

"); + addFormHeaderEnd(webContentBuffer); + + addForm(webContentBuffer, F("post"), PATH_SAVE_HLW8012_CALIBRATE); + addFormHeader(webContentBuffer, S_CALIBRATION_SETTINGS); + addNumberBox(webContentBuffer, INPUT_CALIB_POWER, S_BULB_POWER_W, F("25"), true); + addNumberBox(webContentBuffer, INPUT_CALIB_VOLTAGE, S_VOLTAGE_V, F("230"), true); + addFormHeaderEnd(webContentBuffer); + + addButtonSubmit(webContentBuffer, S_CALIBRATION); + addFormEnd(webContentBuffer); + + addButton(webContentBuffer, S_RETURN, PATH_OTHER); + WebServer->sendContent(); +} +#endif diff --git a/src/SuplaWebPageOther.h b/src/SuplaWebPageOther.h new file mode 100644 index 00000000..79d0a01d --- /dev/null +++ b/src/SuplaWebPageOther.h @@ -0,0 +1,71 @@ +#ifndef SuplaWebPageOther_h +#define SuplaWebPageOther_h + +#include "SuplaDeviceGUI.h" +#include "SuplaWebServer.h" + +#define PATH_OTHER "other" +#define PATH_SAVE_OTHER "saveother" + +void createWebPageOther(); + +#if defined(SUPLA_HC_SR04) || defined(SUPLA_IMPULSE_COUNTER) || defined(SUPLA_HLW8012) || defined(SUPLA_RGBW) || defined(SUPLA_PUSHOVER) +void suplaWebPageOther(int save); +void handleOther(); +void handleOtherSave(); +#endif + +#if defined(SUPLA_IMPULSE_COUNTER) +#define INPUT_IMPULSE_COUNTER_GPIO "ic" +#define INPUT_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT "icdt" +#define INPUT_IMPULSE_COUNTER_PULL_UP "icpu" +#define INPUT_IMPULSE_COUNTER_RAISING_EDGE "icre" +#define INPUT_IMPULSE_COUNTER_CHANGE_VALUE "iccv" +#define INPUT_MAX_IMPULSE_COUNTER "imic" +void handleImpulseCounterSet(); +void handleImpulseCounterSaveSet(); +void supla_impulse_counter_set(int save); +#endif + +#if defined(SUPLA_HLW8012) +#define INPUT_CF "cf" +#define INPUT_CF1 "cf1" +#define INPUT_SEL "sel" + +#define PATH_HLW8012_CALIBRATE "calibrate" +#define PATH_SAVE_HLW8012_CALIBRATE "savecalibrate" + +#define INPUT_CALIB_POWER "power" +#define INPUT_CALIB_VOLTAGE "voltage" + +void handleHLW8012Calibrate(); +void handleHLW8012CalibrateSave(); +void suplaWebpageHLW8012Calibrate(uint8_t save); +#endif + +#ifdef SUPLA_RGBW +#define INPUT_RGBW_MAX "rgbwm" +#define INPUT_RGBW_RED "rgbwr" +#define INPUT_RGBW_GREEN "rgbwg" +#define INPUT_RGBW_BLUE "rgbwb" +#define INPUT_RGBW_BRIGHTNESS "rgbwbr" +#define INPUT_RGBW_COLOR_BRIGHTNESS "rgbwcb" +#endif + +#ifdef SUPLA_PUSHOVER +#define INPUT_PUSHOVER_TOKEN "pot" +#define INPUT_PUSHOVER_USER "pou" +#endif + +#ifdef SUPLA_HC_SR04 +#define INPUT_TRIG_GPIO "trig" +#define INPUT_ECHO_GPIO "echo" +#define INPUT_HC_SR04_MAX_SENSOR_READ "hsm" +#endif + +#ifdef SUPLA_IMPULSE_COUNTER +#define PATH_IMPULSE_COUNTER_SET "setcounter" +#define PATH_SAVE_IMPULSE_COUNTER_SET "savesetcounter" +#endif + +#endif // SuplaWebPageOther_h diff --git a/src/SuplaWebPageRelay.cpp b/src/SuplaWebPageRelay.cpp index 4e3bf9a5..d85843fc 100644 --- a/src/SuplaWebPageRelay.cpp +++ b/src/SuplaWebPageRelay.cpp @@ -4,233 +4,428 @@ #include "SuplaCommonPROGMEM.h" #include "GUIGenericCommon.h" #include "Markup.h" +#include "SuplaWebPageSensor.h" -#if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) SuplaWebPageRelay *WebPageRelay = new SuplaWebPageRelay(); SuplaWebPageRelay::SuplaWebPageRelay() { } +#if defined(SUPLA_RELAY) || defined(SUPLA_MCP23017) void SuplaWebPageRelay::createWebPageRelay() { String path; + path.reserve(11); + path += PATH_START; path += PATH_RELAY; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageRelay::handleRelay, this)); + WebServer->httpServer->on(path, HTTP_GET, std::bind(&SuplaWebPageRelay::handleRelay, this)); path = PATH_START; path += PATH_SAVE_RELAY; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageRelay::handleRelaySave, this)); + WebServer->httpServer->on(path, HTTP_POST, std::bind(&SuplaWebPageRelay::handleRelaySave, this)); - for (uint8_t i = 1; i <= MAX_GPIO; i++) { + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_MCP23017).toInt() != FUNCTION_OFF) { +#ifdef SUPLA_MCP23017 path = PATH_START; path += PATH_RELAY_SET; - path += i; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageRelay::handleRelaySet, this)); + WebServer->httpServer->on(path, HTTP_GET, std::bind(&SuplaWebPageRelay::handleRelaySetMCP23017, this)); path = PATH_START; path += PATH_SAVE_RELAY_SET; - path += i; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageRelay::handleRelaySaveSet, this)); + WebServer->httpServer->on(path, HTTP_POST, std::bind(&SuplaWebPageRelay::handleRelaySaveSetMCP23017, this)); +#endif + } + else { +#if defined(SUPLA_RELAY) + for (uint8_t i = 1; i <= ConfigManager->get(KEY_MAX_RELAY)->getValueInt(); i++) { + path = PATH_START; + path += PATH_RELAY_SET; + path += i; + WebServer->httpServer->on(path, HTTP_GET, std::bind(&SuplaWebPageRelay::handleRelaySet, this)); + + path = PATH_START; + path += PATH_SAVE_RELAY_SET; + path += i; + WebServer->httpServer->on(path, HTTP_POST, std::bind(&SuplaWebPageRelay::handleRelaySaveSet, this)); + } +#endif } } void SuplaWebPageRelay::handleRelay() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); + if (!WebServer->isLoggedIn()) { + return; } - WebServer->sendContent(supla_webpage_relay(0)); + supla_webpage_relay(0); } void SuplaWebPageRelay::handleRelaySave() { - // Serial.println(F("HTTP_POST - metoda handleRelaySave")); - - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); + if (!WebServer->isLoggedIn()) { + return; } uint8_t nr, last_value; last_value = ConfigManager->get(KEY_MAX_RELAY)->getValueInt(); for (nr = 1; nr <= last_value; nr++) { - if (!WebServer->saveGPIO(INPUT_RELAY_GPIO, FUNCTION_RELAY, nr, INPUT_MAX_RELAY)) { - WebServer->sendContent(supla_webpage_relay(6)); - return; + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_MCP23017).toInt() != FUNCTION_OFF) { + if (!WebServer->saveGpioMCP23017(INPUT_RELAY_GPIO, FUNCTION_RELAY, nr, INPUT_MAX_RELAY)) { + supla_webpage_relay(6); + return; + } + } + else { + if (!WebServer->saveGPIO(INPUT_RELAY_GPIO, FUNCTION_RELAY, nr, INPUT_MAX_RELAY)) { + supla_webpage_relay(6); + return; + } } } - if (strcmp(WebServer->httpServer.arg(INPUT_MAX_RELAY).c_str(), "") != 0) { - ConfigManager->set(KEY_MAX_RELAY, WebServer->httpServer.arg(INPUT_MAX_RELAY).c_str()); + if (strcmp(WebServer->httpServer->arg(INPUT_MAX_RELAY).c_str(), "") != 0) { + ConfigManager->set(KEY_MAX_RELAY, WebServer->httpServer->arg(INPUT_MAX_RELAY).c_str()); } switch (ConfigManager->save()) { case E_CONFIG_OK: - // Serial.println(F("E_CONFIG_OK: Config save")); - WebServer->sendContent(supla_webpage_relay(1)); + if (last_value >= ConfigManager->get(KEY_MAX_RELAY)->getValueInt()) { + supla_webpage_relay(1); + } + else { + supla_webpage_relay(2); + ConfigESP->rebootESP(); + } break; case E_CONFIG_FILE_OPEN: - // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); - WebServer->sendContent(supla_webpage_relay(2)); + supla_webpage_relay(2); break; } } -String SuplaWebPageRelay::supla_webpage_relay(int save) { - String key; - uint8_t selected, suported, nr; - - String pagerelay = ""; - pagerelay += WebServer->SuplaSaveResult(save); - pagerelay += WebServer->SuplaJavaScript(PATH_RELAY); - pagerelay += F("
"); - addFormHeader(pagerelay, String(S_GPIO_SETTINGS_FOR_RELAYS)); - addNumberBox(pagerelay, INPUT_MAX_RELAY, S_QUANTITY, KEY_MAX_RELAY, ConfigESP->countFreeGpio(FUNCTION_RELAY)); +void SuplaWebPageRelay::supla_webpage_relay(int save) { + uint8_t nr, countFreeGpio; + + WebServer->sendHeaderStart(); + + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_RELAY); + + addForm(webContentBuffer, F("post"), PATH_SAVE_RELAY); + addFormHeader(webContentBuffer, S_GPIO_SETTINGS_FOR_RELAYS); + + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_MCP23017).toInt() != FUNCTION_OFF) { + countFreeGpio = 32; + } + else { + countFreeGpio = ConfigESP->countFreeGpio(FUNCTION_RELAY); + } + + addNumberBox(webContentBuffer, INPUT_MAX_RELAY, S_QUANTITY, KEY_MAX_RELAY, countFreeGpio); + for (nr = 1; nr <= ConfigManager->get(KEY_MAX_RELAY)->getValueInt(); nr++) { - addListGPIOLinkBox(pagerelay, INPUT_RELAY_GPIO, S_RELAY, FUNCTION_RELAY, PATH_RELAY_SET, nr); - } - addFormHeaderEnd(pagerelay); - pagerelay += F("
"); - pagerelay += F("
"); - pagerelay += F(""); - return pagerelay; + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_MCP23017).toInt() != FUNCTION_OFF) { + addListMCP23017GPIOLinkBox(webContentBuffer, INPUT_RELAY_GPIO, S_RELAY, FUNCTION_RELAY, PATH_RELAY_SET, nr); + } + else { + addListGPIOLinkBox(webContentBuffer, INPUT_RELAY_GPIO, S_RELAY, FUNCTION_RELAY, PATH_RELAY_SET, nr); + } + } + addFormHeaderEnd(webContentBuffer); + + addButtonSubmit(webContentBuffer, S_SAVE); + addFormEnd(webContentBuffer); + addButton(webContentBuffer, S_RETURN, PATH_DEVICE_SETTINGS); + + WebServer->sendHeaderEnd(); } +#endif +#if defined(SUPLA_RELAY) void SuplaWebPageRelay::handleRelaySet() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); + if (!WebServer->isLoggedIn()) { + return; } - WebServer->sendContent(supla_webpage_relay_set(0)); + supla_webpage_relay_set(0); } void SuplaWebPageRelay::handleRelaySaveSet() { - // Serial.println(F("HTTP_POST - metoda handleRelaySaveSet")); - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); + if (!WebServer->isLoggedIn()) { + return; } - String readUrl, nr_relay, key, input; - uint8_t place; + String readUrl, nr_relay, input, path; + uint8_t place, key, gpio; + + input.reserve(6); + readUrl.reserve(11); + nr_relay.reserve(2); + path.reserve(15); - String path = PATH_START; + path = PATH_START; path += PATH_SAVE_RELAY_SET; - readUrl = WebServer->httpServer.uri(); + readUrl = WebServer->httpServer->uri(); place = readUrl.indexOf(path); nr_relay = readUrl.substring(place + path.length(), place + path.length() + 3); - key = GPIO; - key += ConfigESP->getGpio(nr_relay.toInt(), FUNCTION_RELAY); + + gpio = ConfigESP->getGpio(nr_relay.toInt(), FUNCTION_RELAY); + key = KEY_GPIO + gpio; input = INPUT_RELAY_MEMORY; input += nr_relay; - ConfigManager->setElement(key.c_str(), MEMORY, WebServer->httpServer.arg(input).toInt()); + ConfigManager->setElement(key, MEMORY, WebServer->httpServer->arg(input).toInt()); input = INPUT_RELAY_LEVEL; input += nr_relay; - ConfigManager->setElement(key.c_str(), LEVEL, WebServer->httpServer.arg(input).toInt()); + ConfigManager->setElement(key, LEVEL_RELAY, WebServer->httpServer->arg(input).toInt()); + + input = INPUT_CONDITIONS_SENSOR_TYPE; + ConfigManager->setElement(KEY_CONDITIONS_SENSOR_TYPE, (nr_relay.toInt() - 1), WebServer->httpServer->arg(input).toInt()); + input = INPUT_CONDITIONS_TYPE; + ConfigManager->setElement(KEY_CONDITIONS_TYPE, (nr_relay.toInt() - 1), WebServer->httpServer->arg(input).toInt()); + input = INPUT_CONDITIONS_MIN; + ConfigManager->setElement(KEY_CONDITIONS_MIN, (nr_relay.toInt() - 1), WebServer->httpServer->arg(input).c_str()); + input = INPUT_CONDITIONS_MAX; + ConfigManager->setElement(KEY_CONDITIONS_MAX, (nr_relay.toInt() - 1), WebServer->httpServer->arg(input).c_str()); + +#if defined(SUPLA_LED) + input = INPUT_LED; + input += nr_relay; + if (!WebServer->saveGPIO(input, FUNCTION_LED, nr_relay.toInt())) { + supla_webpage_relay_set(6, nr_relay.toInt()); + return; + } + + if (ConfigESP->getGpio(nr_relay.toInt(), FUNCTION_LED) != OFF_GPIO) { + gpio = ConfigESP->getGpio(nr_relay.toInt(), FUNCTION_LED); + key = KEY_GPIO + gpio; + input = INPUT_LEVEL_LED; + input += nr_relay; + + ConfigManager->setElement(key, INVERSED_BUTTON, WebServer->httpServer->arg(input).toInt()); + } + +#endif + +#if defined(SUPLA_PUSHOVER) + if (nr_relay.toInt() <= MAX_PUSHOVER_MESSAGE) { + input = INPUT_PUSHOVER; + ConfigManager->setElement(KEY_PUSHOVER, (nr_relay.toInt() - 1), WebServer->httpServer->arg(input).toInt()); + input = INPUT_PUSHOVER_MESSAGE; + ConfigManager->setElement(KEY_PUSHOVER_MASSAGE, (nr_relay.toInt() - 1), WebServer->httpServer->arg(input).c_str()); + } +#endif + +#if defined(SUPLA_DIRECT_LINKS) + if (nr_relay.toInt() <= MAX_DIRECT_LINKS_SIZE) { + input = INPUT_DIRECT_LINK_ON; + ConfigManager->setElement(KEY_DIRECT_LINKS_ON, (nr_relay.toInt() - 1), WebServer->httpServer->arg(input).c_str()); + input = INPUT_DIRECT_LINK_OFF; + ConfigManager->setElement(KEY_DIRECT_LINKS_OFF, (nr_relay.toInt() - 1), WebServer->httpServer->arg(input).c_str()); + } +#endif switch (ConfigManager->save()) { case E_CONFIG_OK: - // Serial.println(F("E_CONFIG_OK: Dane zapisane")); - WebServer->sendContent(supla_webpage_relay(1)); + supla_webpage_relay_set(1, nr_relay.toInt()); break; - case E_CONFIG_FILE_OPEN: - // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); - WebServer->sendContent(supla_webpage_relay(2)); + supla_webpage_relay_set(2, nr_relay.toInt()); break; } } -String SuplaWebPageRelay::supla_webpage_relay_set(int save) { - String readUrl, nr_relay, key; - uint8_t place, selected, suported; +void SuplaWebPageRelay::supla_webpage_relay_set(int save, int nr) { + String path, readUrl, nr_relay, massage; + uint8_t place, selected; - String path = PATH_START; - path += PATH_RELAY_SET; - readUrl = WebServer->httpServer.uri(); + path.reserve(10); + readUrl.reserve(11); + nr_relay.reserve(2); + massage.reserve(MAX_DIRECT_LINKS_SIZE); - place = readUrl.indexOf(path); - nr_relay = readUrl.substring(place + path.length(), place + path.length() + 3); + WebServer->sendHeaderStart(); + webContentBuffer += SuplaSaveResult(save); - String page = ""; - page += WebServer->SuplaSaveResult(save); - page += WebServer->SuplaJavaScript(PATH_RELAY); - uint8_t relays = ConfigManager->get(KEY_MAX_RELAY)->getValueInt(); - if (nr_relay.toInt() <= relays && ConfigESP->getGpio(nr_relay.toInt(), FUNCTION_RELAY) != OFF_GPIO) { - page += F("

"); - page += S_RELAY_NR_SETTINGS; - page += F(" "); - page += nr_relay; - page += F("

"); - page += F(""); - page += F(""); - page += F("
"); + if (nr != 0) { + webContentBuffer += SuplaJavaScript(PATH_RELAY_SET + String(nr)); + nr_relay = nr; } else { - page += F("

"); - page += S_NO_RELAY_NR; - page += F(" "); - page += nr_relay; - page += F("

"); - } - page += F("
"); - page += F("
"); - - return page; + path = PATH_START; + path += PATH_RELAY_SET; + readUrl = WebServer->httpServer->uri(); + + place = readUrl.indexOf(path); + nr_relay = readUrl.substring(place + path.length(), place + path.length() + 3); + webContentBuffer += SuplaJavaScript(String(PATH_RELAY_SET + nr_relay)); + } + + addForm(webContentBuffer, F("post"), PATH_SAVE_RELAY_SET + nr_relay); + addFormHeader(webContentBuffer, S_RELAY_NR_SETTINGS + nr_relay); + + selected = ConfigESP->getLevel(nr_relay.toInt(), FUNCTION_RELAY); + addListBox(webContentBuffer, INPUT_RELAY_LEVEL + nr_relay, S_STATE_CONTROL, LEVEL_P, 2, selected); + + selected = ConfigESP->getMemory(nr_relay.toInt(), FUNCTION_RELAY); + addListBox(webContentBuffer, INPUT_RELAY_MEMORY + nr_relay, S_REACTION_AFTER_RESET, MEMORY_P, 3, selected); + addFormHeaderEnd(webContentBuffer); + +#if defined(SUPLA_LED) + addFormHeader(webContentBuffer, S_RELAY_ACTIVATION_STATUS); + + addListGPIOBox(webContentBuffer, INPUT_LED + nr_relay, S_LED, FUNCTION_LED, nr_relay.toInt()); + + selected = ConfigESP->getInversed(nr_relay.toInt(), FUNCTION_LED); + addListBox(webContentBuffer, INPUT_LEVEL_LED + nr_relay, S_STATE_CONTROL, LEVEL_P, 2, selected); + + addFormHeaderEnd(webContentBuffer); +#endif + +#if defined(SUPLA_PUSHOVER) + if (nr_relay.toInt() < MAX_PUSHOVER_MESSAGE) { + addFormHeader(webContentBuffer, S_PUSHOVER); + + selected = ConfigManager->get(KEY_PUSHOVER)->getElement(nr_relay.toInt() - 1).toInt(); + addListBox(webContentBuffer, INPUT_PUSHOVER, S_STATE, STATE_P, 2, selected); + + massage = ConfigManager->get(KEY_PUSHOVER_MASSAGE)->getElement(nr_relay.toInt() - 1).c_str(); + addTextBox(webContentBuffer, INPUT_PUSHOVER_MESSAGE, S_MESSAGE, massage, 0, 16, false); + addFormHeaderEnd(webContentBuffer); + } +#endif + +#if defined(SUPLA_DIRECT_LINKS) + if (nr_relay.toInt() <= MAX_DIRECT_LINK) { + addFormHeader(webContentBuffer, S_DIRECT_LINKS); + massage = ConfigManager->get(KEY_DIRECT_LINKS_ON)->getElement(nr_relay.toInt() - 1).c_str(); + addTextBox(webContentBuffer, INPUT_DIRECT_LINK_ON, S_ON, massage, F("xx/xxxxxxxxx/turn-on"), 0, MAX_DIRECT_LINKS_SIZE, false); + + massage = ConfigManager->get(KEY_DIRECT_LINKS_OFF)->getElement(nr_relay.toInt() - 1).c_str(); + addTextBox(webContentBuffer, INPUT_DIRECT_LINK_OFF, S_OFF, massage, F("xx/xxxxxxxxx/turn-off"), 0, MAX_DIRECT_LINKS_SIZE, false); + + addFormHeaderEnd(webContentBuffer); + } +#endif + + addFormHeader(webContentBuffer, S_CONDITIONING); + selected = ConfigManager->get(KEY_CONDITIONS_SENSOR_TYPE)->getElement(nr_relay.toInt() - 1).toInt(); + addListBox(webContentBuffer, INPUT_CONDITIONS_SENSOR_TYPE, S_SENSOR, SENSOR_LIST_P, COUNT_SENSOR_LIST, selected); + + selected = ConfigManager->get(KEY_CONDITIONS_TYPE)->getElement(nr_relay.toInt() - 1).toInt(); + addListBox(webContentBuffer, INPUT_CONDITIONS_TYPE, S_CONDITION, CONDITIONS_TYPE_P, 4, selected); + + String value = ConfigManager->get(KEY_CONDITIONS_MIN)->getElement(nr_relay.toInt() - 1); + addNumberBox(webContentBuffer, INPUT_CONDITIONS_MIN, S_ON, S_SWITCH_ON_VALUE, false, value); + value = ConfigManager->get(KEY_CONDITIONS_MAX)->getElement(nr_relay.toInt() - 1); + addNumberBox(webContentBuffer, INPUT_CONDITIONS_MAX, S_OFF, S_SWITCH_OFF_VALUE, false, value); + addFormHeaderEnd(webContentBuffer); + + addButtonSubmit(webContentBuffer, S_SAVE); + addFormEnd(webContentBuffer); + addButton(webContentBuffer, S_RETURN, PATH_RELAY); + + WebServer->sendHeaderEnd(); +} +#endif + +#ifdef SUPLA_MCP23017 +void SuplaWebPageRelay::handleRelaySetMCP23017() { + if (!WebServer->isLoggedIn()) { + return; + } + supla_webpage_relay_set_MCP23017(0); +} + +void SuplaWebPageRelay::supla_webpage_relay_set_MCP23017(int save) { + uint8_t selected; + String massage, name, input; + input.reserve(9); + + WebServer->sendHeaderStart(); + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_RELAY_SET); + + addForm(webContentBuffer, F("post"), PATH_SAVE_RELAY_SET); + addFormHeader(webContentBuffer, S_SETTINGS_FOR_RELAYS); + + selected = ConfigESP->getLevel(1, FUNCTION_RELAY); + input = INPUT_RELAY_LEVEL; + addListBox(webContentBuffer, input, S_STATE_CONTROL, LEVEL_P, 2, selected); + + selected = ConfigESP->getMemory(1, FUNCTION_RELAY); + input = INPUT_RELAY_MEMORY; + addListBox(webContentBuffer, input, S_REACTION_AFTER_RESET, MEMORY_P, 3, selected); + addFormHeaderEnd(webContentBuffer); + +#if defined(SUPLA_PUSHOVER) + addFormHeader(webContentBuffer, S_PUSHOVER); + for (uint8_t i = 0; i < MAX_PUSHOVER_MESSAGE; i++) { + selected = ConfigManager->get(KEY_PUSHOVER)->getElement(i).toInt(); + name = S_STATE; + name =+ S_SPACE; + name += i + 1; + input = INPUT_PUSHOVER; + input += i; + addListBox(webContentBuffer, input, name, STATE_P, 2, selected); + + massage = ConfigManager->get(KEY_PUSHOVER_MASSAGE)->getElement(i).c_str(); + name = S_MESSAGE; + name += S_SPACE; + name += i + 1; + input = INPUT_PUSHOVER_MESSAGE; + input += i; + addTextBox(webContentBuffer, input, name, massage, 0, 16, false); + } + addFormHeaderEnd(webContentBuffer); +#endif + + addButtonSubmit(webContentBuffer, S_SAVE); + addFormEnd(webContentBuffer); + addButton(webContentBuffer, S_RETURN, PATH_RELAY); + + WebServer->sendHeaderEnd(); +} + +void SuplaWebPageRelay::handleRelaySaveSetMCP23017() { + if (!WebServer->isLoggedIn()) { + return; + } + + String input; + uint8_t key, gpio, memory, level, address; + + input.reserve(9); + + input = INPUT_RELAY_MEMORY; + memory = WebServer->httpServer->arg(input).toInt(); + + input = INPUT_RELAY_LEVEL; + level = WebServer->httpServer->arg(input).toInt(); + + for (gpio = 0; gpio <= OFF_GPIO; gpio++) { + key = KEY_GPIO + gpio; + ConfigManager->setElement(key, MEMORY, memory); + ConfigManager->setElement(key, LEVEL_RELAY, level); + } + +#if defined(SUPLA_PUSHOVER) + for (uint8_t i = 0; i < MAX_PUSHOVER_MESSAGE; i++) { + input = INPUT_PUSHOVER; + input += i; + ConfigManager->setElement(KEY_PUSHOVER, i, WebServer->httpServer->arg(input).toInt()); + input = INPUT_PUSHOVER_MESSAGE; + input += i; + ConfigManager->setElement(KEY_PUSHOVER_MASSAGE, i, WebServer->httpServer->arg(input).c_str()); + } +#endif + + switch (ConfigManager->save()) { + case E_CONFIG_OK: + supla_webpage_relay_set_MCP23017(1); + break; + case E_CONFIG_FILE_OPEN: + supla_webpage_relay_set_MCP23017(2); + break; + } } #endif diff --git a/src/SuplaWebPageRelay.h b/src/SuplaWebPageRelay.h index 88c71aa5..8fc6cdcc 100644 --- a/src/SuplaWebPageRelay.h +++ b/src/SuplaWebPageRelay.h @@ -1,9 +1,11 @@ #ifndef SuplaWebPageRelay_h #define SuplaWebPageRelay_h -#include "SuplaDeviceGUI.h" #include "SuplaWebServer.h" +#define INPUT_ADRESS_MCP23017 "iam" + +#if defined(SUPLA_RELAY) || defined(SUPLA_MCP23017) #define PATH_RELAY "relay" #define PATH_SAVE_RELAY "saverelay" #define PATH_RELAY_SET "setrelay" @@ -15,28 +17,68 @@ #define INPUT_RELAY_DURATION "ird" #define INPUT_ROLLERSHUTTER "irsr" -enum _memory_relay { +#define INPUT_CONDITIONS_SENSOR_TYPE "cst" +#define INPUT_CONDITIONS_TYPE "ct" +#define INPUT_CONDITIONS_MIN "cmi" +#define INPUT_CONDITIONS_MAX "cma" + +#endif + +enum _memory_relay +{ MEMORY_RELAY_OFF, MEMORY_RELAY_ON, MEMORY_RELAY_RESTORE }; - -#if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) + +enum _conditions +{ + CONDITIONS_DS18B20 = 1, + CONDITIONS_DHT_TEMP, + CONDITIONS_DHT_HUMI, +}; + +#if defined(SUPLA_PUSHOVER) +#define INPUT_PUSHOVER "po" +#define INPUT_PUSHOVER_MESSAGE "pm" +#endif + +#if defined(SUPLA_DIRECT_LINKS) +#define INPUT_DIRECT_LINK_ON "dlo" +#define INPUT_DIRECT_LINK_OFF "dlof" +#endif class SuplaWebPageRelay { public: SuplaWebPageRelay(); + +#if defined(SUPLA_RELAY) || defined(SUPLA_MCP23017) void createWebPageRelay(); void handleRelay(); void handleRelaySave(); + void supla_webpage_relay(int save); +#endif + +#if defined(SUPLA_RELAY) void handleRelaySet(); void handleRelaySaveSet(); - private: - String supla_webpage_relay_set(int save); - String supla_webpage_relay(int save); + void supla_webpage_relay_set(int save, int nr = 0); +#endif + +#ifdef SUPLA_MCP23017 + void supla_webpage_relay_set_MCP23017(int save); + void handleRelaySaveSetMCP23017(); + void handleRelaySetMCP23017(); +#endif + +#if defined(SUPLA_LED) +#define PATH_LED "led" +#define PATH_SAVE_LED "saveled" +#define INPUT_LED "led" +#define INPUT_LEVEL_LED "ill" +#endif }; extern SuplaWebPageRelay* WebPageRelay; -#endif #endif // SuplaWebPageRelay_h diff --git a/src/SuplaWebPageSensor.cpp b/src/SuplaWebPageSensor.cpp index 7fe08b19..505a5f53 100644 --- a/src/SuplaWebPageSensor.cpp +++ b/src/SuplaWebPageSensor.cpp @@ -17,108 +17,80 @@ void SuplaWebPageSensor::createWebPageSensor() { #if defined(SUPLA_DS18B20) || defined(SUPLA_DHT11) || defined(SUPLA_DHT22) || defined(SUPLA_SI7021_SONOFF) path = PATH_START; path += PATH_1WIRE; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handle1Wire, this)); + WebServer->httpServer->on(path, std::bind(&SuplaWebPageSensor::handle1Wire, this)); path = PATH_START; path += PATH_SAVE_1WIRE; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handle1WireSave, this)); + WebServer->httpServer->on(path, std::bind(&SuplaWebPageSensor::handle1WireSave, this)); #ifdef SUPLA_DS18B20 path = PATH_START; path += PATH_MULTI_DS; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handleSearchDS, this)); + WebServer->httpServer->on(path, std::bind(&SuplaWebPageSensor::handleSearchDS, this)); path = PATH_START; path += PATH_SAVE_MULTI_DS; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handleDSSave, this)); + WebServer->httpServer->on(path, std::bind(&SuplaWebPageSensor::handleDSSave, this)); #endif #endif -#if defined(SUPLA_BME280) || defined(SUPLA_SHT30) || defined(SUPLA_SI7021) +#if defined(SUPLA_BME280) || defined(SUPLA_SHT3x) || defined(SUPLA_SI7021) || defined(SUPLA_OLED) || defined(SUPLA_MCP23017) path = PATH_START; path += PATH_I2C; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handlei2c, this)); + WebServer->httpServer->on(path, std::bind(&SuplaWebPageSensor::handlei2c, this)); path = PATH_START; path += PATH_SAVE_I2C; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handlei2cSave, this)); + WebServer->httpServer->on(path, std::bind(&SuplaWebPageSensor::handlei2cSave, this)); #endif #if defined(SUPLA_MAX6675) path = PATH_START; path += PATH_SPI; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handleSpi, this)); + WebServer->httpServer->on(path, std::bind(&SuplaWebPageSensor::handleSpi, this)); path = PATH_START; path += PATH_SAVE_SPI; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handleSpiSave, this)); -#endif - -#if defined(SUPLA_HC_SR04) || defined(SUPLA_IMPULSE_COUNTER) - path = PATH_START; - path += PATH_OTHER; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handleOther, this)); - path = PATH_START; - path += PATH_SAVE_OTHER; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handleOtherSave, this)); - -#if defined(SUPLA_IMPULSE_COUNTER) - for (uint8_t i = 1; i <= MAX_GPIO; i++) { - path = PATH_START; - path += PATH_IMPULSE_COUNTER_SET; - path += i; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handleImpulseCounterSet, this)); - - path = PATH_START; - path += PATH_SAVE_IMPULSE_COUNTER_SET; - path += i; - WebServer->httpServer.on(path, std::bind(&SuplaWebPageSensor::handleImpulseCounterSaveSet, this)); - } -#endif - + WebServer->httpServer->on(path, std::bind(&SuplaWebPageSensor::handleSpiSave, this)); #endif } + #ifdef SUPLA_DS18B20 void SuplaWebPageSensor::handleSearchDS() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); + if (!WebServer->isLoggedIn()) { + return; } - WebServer->sendContent(supla_webpage_search(0)); + supla_webpage_search(0); } void SuplaWebPageSensor::handleDSSave() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); + if (!WebServer->isLoggedIn()) { + return; } for (uint8_t i = 0; i < ConfigManager->get(KEY_MULTI_MAX_DS18B20)->getValueInt(); i++) { - String ds_key = KEY_DS; - String ds_name_key = KEY_DS_NAME; - ds_key += i; - ds_name_key += i; + String dsAddr = INPUT_DS18B20_ADDR; + String dsName = INPUT_DS18B20_NAME; + dsAddr += i; + dsName += i; - String ds = F("dschlid"); - String ds_name = F("dsnameid"); - ds += i; - ds_name += i; + ConfigManager->setElement(KEY_ADDR_DS18B20, i, WebServer->httpServer->arg(dsAddr).c_str()); - ConfigManager->set(ds_key.c_str(), WebServer->httpServer.arg(ds).c_str()); - ConfigManager->set(ds_name_key.c_str(), WebServer->httpServer.arg(ds_name).c_str()); + if (strcmp(WebServer->httpServer->arg(dsName).c_str(), "") != 0) { + ConfigManager->setElement(KEY_NAME_SENSOR, i, WebServer->httpServer->arg(dsName).c_str()); + } - Supla::GUI::sensorDS[i]->setDeviceAddress(ConfigManager->get(ds_key.c_str())->getValueBin(MAX_DS18B20_ADDRESS)); + Supla::GUI::sensorDS[i]->setDeviceAddress(HexToBytes(ConfigManager->get(KEY_ADDR_DS18B20)->getElement(i))); } switch (ConfigManager->save()) { case E_CONFIG_OK: // Serial.println(F("E_CONFIG_OK: Config save")); - WebServer->sendContent(supla_webpage_search(1)); + supla_webpage_search(1); // WebServer->rebootESP(); break; case E_CONFIG_FILE_OPEN: // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); - WebServer->sendContent(supla_webpage_search(2)); + supla_webpage_search(2); break; } } -String SuplaWebPageSensor::supla_webpage_search(int save) { - String content = ""; +void SuplaWebPageSensor::supla_webpage_search(int save) { uint8_t count = 0; uint8_t pin = ConfigESP->getGpio(FUNCTION_DS18B20); @@ -128,26 +100,28 @@ String SuplaWebPageSensor::supla_webpage_search(int save) { char strAddr[64]; uint8_t i; - content += WebServer->SuplaSaveResult(save); - content += WebServer->SuplaJavaScript(PATH_MULTI_DS); - content += F("
"); - if (ConfigESP->getGpio(FUNCTION_DS18B20) < OFF_GPIO || !Supla::GUI::sensorDS.empty()) { - content += F("
"); - this->showDS18B20(content); - content += F("
"); - content += F("
"); - } - content += F("
"); - content += F("
"); - content += F("

"); - content += S_FOUND; - content += F(" DS18b20

"); + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_MULTI_DS); + webContentBuffer += F("
"); + if (ConfigESP->getGpio(FUNCTION_DS18B20) < OFF_GPIO) { + webContentBuffer += F(""); + this->showDS18B20(); + webContentBuffer += F(""); + webContentBuffer += F("
"); + } + webContentBuffer += F("
"); + webContentBuffer += F("
"); + webContentBuffer += F("

"); + webContentBuffer += S_FOUND; + webContentBuffer += S_SPACE; + webContentBuffer += S_DS18B20; + webContentBuffer += F("

"); sensors.setOneWire(&ow); sensors.begin(); if (sensors.isParasitePowerMode()) { @@ -166,14 +140,15 @@ String SuplaWebPageSensor::supla_webpage_search(int save) { address[7]); supla_log(LOG_DEBUG, "Index %d - address %s", i, strAddr); - content += F(""); + webContentBuffer += F("' value='"); + webContentBuffer += String(strAddr); + webContentBuffer += F("' maxlength="); + webContentBuffer += MAX_DS18B20_ADDRESS_HEX; + webContentBuffer += F(" readonly>
"); - content += F("
"); - content += F(""); - content += F("

"); - content += F("
"); - - return content; + webContentBuffer += F(""); + } + webContentBuffer += F(""); + webContentBuffer += F("
"); + webContentBuffer += F(""); + webContentBuffer += F("

"); + webContentBuffer += F("

"); + WebServer->sendContent(); } -void SuplaWebPageSensor::showDS18B20(String &content, bool readonly) { +void SuplaWebPageSensor::showDS18B20(bool readonly) { if (ConfigESP->getGpio(FUNCTION_DS18B20) != OFF_GPIO) { - content += F("
"); - content += F("

"); - content += S_TEMPERATURE; - content += F("

"); + webContentBuffer += F("
"); + webContentBuffer += F("

"); + webContentBuffer += S_TEMPERATURE; + webContentBuffer += F("

"); for (uint8_t i = 0; i < ConfigManager->get(KEY_MULTI_MAX_DS18B20)->getValueInt(); i++) { - String ds_key = KEY_DS; - String ds_name_key = KEY_DS_NAME; - ds_key += i; - ds_name_key += i; - double temp = Supla::GUI::sensorDS[i]->getValue(); - content += F(""); - content += F(""); + webContentBuffer += F(""); + webContentBuffer += F(" °C "); + webContentBuffer += F(""); delay(0); } - content += F("
"); + webContentBuffer += F("
"); } } #endif #if defined(SUPLA_DS18B20) || defined(SUPLA_DHT11) || defined(SUPLA_DHT22) || defined(SUPLA_SI7021_SONOFF) void SuplaWebPageSensor::handle1Wire() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); + if (!WebServer->isLoggedIn()) { + return; } - WebServer->sendContent(supla_webpage_1wire(0)); + supla_webpage_1wire(0); } void SuplaWebPageSensor::handle1WireSave() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); + if (!WebServer->isLoggedIn()) { + return; } - String key, input; - uint8_t nr, current_value, last_value; + uint8_t nr, last_value; #ifdef SUPLA_DHT11 last_value = ConfigManager->get(KEY_MAX_DHT11)->getValueInt(); for (nr = 1; nr <= last_value; nr++) { if (!WebServer->saveGPIO(INPUT_DHT11_GPIO, FUNCTION_DHT11, nr, INPUT_MAX_DHT11)) { - WebServer->sendContent(supla_webpage_1wire(6)); + supla_webpage_1wire(6); return; } } - if (strcmp(WebServer->httpServer.arg(INPUT_MAX_DHT11).c_str(), "") != 0) { - ConfigManager->set(KEY_MAX_DHT11, WebServer->httpServer.arg(INPUT_MAX_DHT11).c_str()); + if (strcmp(WebServer->httpServer->arg(INPUT_MAX_DHT11).c_str(), "") != 0) { + ConfigManager->set(KEY_MAX_DHT11, WebServer->httpServer->arg(INPUT_MAX_DHT11).c_str()); } #endif @@ -288,264 +257,314 @@ void SuplaWebPageSensor::handle1WireSave() { last_value = ConfigManager->get(KEY_MAX_DHT22)->getValueInt(); for (nr = 1; nr <= last_value; nr++) { if (!WebServer->saveGPIO(INPUT_DHT22_GPIO, FUNCTION_DHT22, nr, INPUT_MAX_DHT22)) { - WebServer->sendContent(supla_webpage_1wire(6)); + supla_webpage_1wire(6); return; } } - if (strcmp(WebServer->httpServer.arg(INPUT_MAX_DHT22).c_str(), "") != 0) { - ConfigManager->set(KEY_MAX_DHT22, WebServer->httpServer.arg(INPUT_MAX_DHT22).c_str()); + if (strcmp(WebServer->httpServer->arg(INPUT_MAX_DHT22).c_str(), "") != 0) { + ConfigManager->set(KEY_MAX_DHT22, WebServer->httpServer->arg(INPUT_MAX_DHT22).c_str()); } #endif #ifdef SUPLA_DS18B20 if (!WebServer->saveGPIO(INPUT_MULTI_DS_GPIO, FUNCTION_DS18B20)) { - WebServer->sendContent(supla_webpage_1wire(6)); + supla_webpage_1wire(6); return; } - if (strcmp(WebServer->httpServer.arg(INPUT_MAX_DS18B20).c_str(), "") > 0) { - ConfigManager->set(KEY_MULTI_MAX_DS18B20, WebServer->httpServer.arg(INPUT_MAX_DS18B20).c_str()); + if (strcmp(WebServer->httpServer->arg(INPUT_MAX_DS18B20).c_str(), "") > 0) { + ConfigManager->set(KEY_MULTI_MAX_DS18B20, WebServer->httpServer->arg(INPUT_MAX_DS18B20).c_str()); } #endif #ifdef SUPLA_SI7021_SONOFF if (!WebServer->saveGPIO(INPUT_SI7021_SONOFF, FUNCTION_SI7021_SONOFF)) { - WebServer->sendContent(supla_webpage_1wire(6)); + supla_webpage_1wire(6); return; } #endif switch (ConfigManager->save()) { case E_CONFIG_OK: - WebServer->sendContent(supla_webpage_1wire(1)); + supla_webpage_1wire(1); break; case E_CONFIG_FILE_OPEN: // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); - WebServer->sendContent(supla_webpage_1wire(2)); + supla_webpage_1wire(2); break; } } -String SuplaWebPageSensor::supla_webpage_1wire(int save) { - uint8_t nr, suported, selected; - uint16_t max; - String page, key; - page += WebServer->SuplaSaveResult(save); - page += WebServer->SuplaJavaScript(PATH_1WIRE); - page += F("
"); +void SuplaWebPageSensor::supla_webpage_1wire(int save) { + uint8_t nr, max; + + WebServer->sendHeaderStart(); + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_1WIRE); + webContentBuffer += F(""); #ifdef SUPLA_DHT11 - addFormHeader(page, String(S_GPIO_SETTINGS_FOR) + " DHT11"); - addNumberBox(page, INPUT_MAX_DHT11, S_QUANTITY, KEY_MAX_DHT11, ConfigESP->countFreeGpio(FUNCTION_DHT11)); + addFormHeader(webContentBuffer, String(S_GPIO_SETTINGS_FOR) + S_SPACE + S_DHT11); + addNumberBox(webContentBuffer, INPUT_MAX_DHT11, S_QUANTITY, KEY_MAX_DHT11, ConfigESP->countFreeGpio(FUNCTION_DHT11)); for (nr = 1; nr <= ConfigManager->get(KEY_MAX_DHT11)->getValueInt(); nr++) { - addListGPIOBox(page, INPUT_DHT11_GPIO, "DHT11", FUNCTION_DHT11, nr); + addListGPIOBox(webContentBuffer, INPUT_DHT11_GPIO, S_DHT11, FUNCTION_DHT11, nr); } - addFormHeaderEnd(page); + addFormHeaderEnd(webContentBuffer); #endif #ifdef SUPLA_DHT22 - addFormHeader(page, String(S_GPIO_SETTINGS_FOR) + " DHT22"); - addNumberBox(page, INPUT_MAX_DHT22, S_QUANTITY, KEY_MAX_DHT22, ConfigESP->countFreeGpio(FUNCTION_DHT22)); + addFormHeader(webContentBuffer, String(S_GPIO_SETTINGS_FOR) + S_SPACE + S_DHT22); + addNumberBox(webContentBuffer, INPUT_MAX_DHT22, S_QUANTITY, KEY_MAX_DHT22, ConfigESP->countFreeGpio(FUNCTION_DHT22)); for (nr = 1; nr <= ConfigManager->get(KEY_MAX_DHT22)->getValueInt(); nr++) { - addListGPIOBox(page, INPUT_DHT22_GPIO, "DHT22", FUNCTION_DHT22, nr); + addListGPIOBox(webContentBuffer, INPUT_DHT22_GPIO, S_DHT22, FUNCTION_DHT22, nr); } - addFormHeaderEnd(page); + addFormHeaderEnd(webContentBuffer); #endif #ifdef SUPLA_SI7021_SONOFF - addFormHeader(page, String(S_GPIO_SETTINGS_FOR) + " Si7021 Sonoff"); - addListGPIOBox(page, INPUT_SI7021_SONOFF, "Si7021 Sonoff", FUNCTION_SI7021_SONOFF); - addFormHeaderEnd(page); + addFormHeader(webContentBuffer, String(S_GPIO_SETTINGS_FOR) + S_SPACE + S_SI7021_SONOFF); + addListGPIOBox(webContentBuffer, INPUT_SI7021_SONOFF, S_SI7021_SONOFF, FUNCTION_SI7021_SONOFF); + addFormHeaderEnd(webContentBuffer); #endif #ifdef SUPLA_DS18B20 - addFormHeader(page, String(S_GPIO_SETTINGS_FOR) + " Multi DS18B20"); - addNumberBox(page, INPUT_MAX_DS18B20, S_QUANTITY, KEY_MULTI_MAX_DS18B20, MAX_DS18B20); + addFormHeader(webContentBuffer, String(S_GPIO_SETTINGS_FOR) + S_SPACE + S_MULTI_DS18B20); + addNumberBox(webContentBuffer, INPUT_MAX_DS18B20, S_QUANTITY, KEY_MULTI_MAX_DS18B20, MAX_DS18B20); if (ConfigManager->get(KEY_MULTI_MAX_DS18B20)->getValueInt() > 1) { - addListGPIOLinkBox(page, INPUT_MULTI_DS_GPIO, "MULTI DS18B20", FUNCTION_DS18B20, PATH_MULTI_DS); + addListGPIOLinkBox(webContentBuffer, INPUT_MULTI_DS_GPIO, S_MULTI_DS18B20, FUNCTION_DS18B20, PATH_MULTI_DS); } else { - addListGPIOBox(page, INPUT_MULTI_DS_GPIO, "MULTI DS18B20", FUNCTION_DS18B20); + addListGPIOBox(webContentBuffer, INPUT_MULTI_DS_GPIO, S_MULTI_DS18B20, FUNCTION_DS18B20); } - addFormHeaderEnd(page); + addFormHeaderEnd(webContentBuffer); #endif - page += F("
"); - page += F("
"); - page += F("
"); - page += F("
"); - page += F("
"); - page += F(""); - return page; + webContentBuffer += F(""); + webContentBuffer += F("
"); + webContentBuffer += F("

"); + + WebServer->sendHeaderEnd(); } #endif -#if defined(SUPLA_BME280) || defined(SUPLA_SHT30) || defined(SUPLA_SI7021) +#if defined(SUPLA_BME280) || defined(SUPLA_SI7021) || defined(SUPLA_SHT3x) || defined(SUPLA_OLED) || defined(SUPLA_MCP23017) void SuplaWebPageSensor::handlei2c() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); + if (!WebServer->isLoggedIn()) { + return; } - WebServer->sendContent(supla_webpage_i2c(0)); + supla_webpage_i2c(0); } void SuplaWebPageSensor::handlei2cSave() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); + if (!WebServer->isLoggedIn()) { + return; } - String key, input; - uint8_t nr, current_value, last_value; + String input; + uint8_t key; -#if defined(SUPLA_BME280) || defined(SUPLA_SI7021) || defined(SUPLA_SHT3x) if (!WebServer->saveGPIO(INPUT_SDA_GPIO, FUNCTION_SDA)) { - WebServer->sendContent(supla_webpage_i2c(6)); + supla_webpage_i2c(6); return; } if (!WebServer->saveGPIO(INPUT_SCL_GPIO, FUNCTION_SCL)) { - WebServer->sendContent(supla_webpage_i2c(6)); + supla_webpage_i2c(6); return; } -#endif #ifdef SUPLA_BME280 key = KEY_ACTIVE_SENSOR; input = INPUT_BME280; - if (strcmp(WebServer->httpServer.arg(input).c_str(), "") != 0) { - ConfigManager->setElement(KEY_ACTIVE_SENSOR, SENSOR_BME280, WebServer->httpServer.arg(input).toInt()); + if (strcmp(WebServer->httpServer->arg(input).c_str(), "") != 0) { + ConfigManager->setElement(KEY_ACTIVE_SENSOR, SENSOR_I2C_BME280, WebServer->httpServer->arg(input).toInt()); } key = KEY_ALTITUDE_BME280; input = INPUT_ALTITUDE_BME280; - if (strcmp(WebServer->httpServer.arg(INPUT_ALTITUDE_BME280).c_str(), "") != 0) { - ConfigManager->set(key.c_str(), WebServer->httpServer.arg(input).c_str()); + if (strcmp(WebServer->httpServer->arg(INPUT_ALTITUDE_BME280).c_str(), "") != 0) { + ConfigManager->set(key, WebServer->httpServer->arg(input).c_str()); } #endif #ifdef SUPLA_SHT3x key = KEY_ACTIVE_SENSOR; input = INPUT_SHT3x; - if (strcmp(WebServer->httpServer.arg(input).c_str(), "") != 0) { - ConfigManager->setElement(KEY_ACTIVE_SENSOR, SENSOR_SHT3x, WebServer->httpServer.arg(input).toInt()); + if (strcmp(WebServer->httpServer->arg(input).c_str(), "") != 0) { + ConfigManager->setElement(KEY_ACTIVE_SENSOR, SENSOR_I2C_SHT3x, WebServer->httpServer->arg(input).toInt()); } #endif #ifdef SUPLA_SI7021 key = KEY_ACTIVE_SENSOR; input = INPUT_SI7021; - if (strcmp(WebServer->httpServer.arg(input).c_str(), "") != 0) { - ConfigManager->setElement(KEY_ACTIVE_SENSOR, SENSOR_SI7021, WebServer->httpServer.arg(input).toInt()); + if (strcmp(WebServer->httpServer->arg(input).c_str(), "") != 0) { + ConfigManager->setElement(KEY_ACTIVE_SENSOR, SENSOR_I2C_SI7021, WebServer->httpServer->arg(input).toInt()); + } +#endif + +#ifdef SUPLA_OLED + key = KEY_ACTIVE_SENSOR; + input = INPUT_OLED; + if (strcmp(WebServer->httpServer->arg(input).c_str(), "") != 0) { + ConfigManager->setElement(KEY_ACTIVE_SENSOR, SENSOR_I2C_OLED, WebServer->httpServer->arg(input).toInt()); + } + + if (!WebServer->saveGPIO(INPUT_BUTTON_GPIO, FUNCTION_CFG_BUTTON)) { + supla_webpage_i2c(6); + return; + } + + input = INPUT_OLED_ANIMATION; + ConfigManager->set(KEY_OLED_ANIMATION, WebServer->httpServer->arg(input).c_str()); + input = INPUT_OLED_BRIGHTNESS; + ConfigManager->set(KEY_OLED_BACK_LIGHT_TIME, WebServer->httpServer->arg(input).c_str()); + + for (uint8_t i = 0; i < getCountSensorChannels(); i++) { + input = INPUT_OLED_NAME; + input += i; + if (strcmp(WebServer->httpServer->arg(input).c_str(), "") != 0) { + ConfigManager->setElement(KEY_NAME_SENSOR, i, WebServer->httpServer->arg(input).c_str()); + } + } +#endif + +#ifdef SUPLA_MCP23017 + key = KEY_ACTIVE_SENSOR; + input = INPUT_MCP23017; + if (strcmp(WebServer->httpServer->arg(input).c_str(), "") != 0) { + ConfigManager->setElement(KEY_ACTIVE_SENSOR, SENSOR_I2C_MCP23017, WebServer->httpServer->arg(input).toInt()); + + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_MCP23017).toInt()) { + ConfigESP->clearFunctionGpio(FUNCTION_RELAY); + ConfigESP->clearFunctionGpio(FUNCTION_BUTTON); + } } #endif switch (ConfigManager->save()) { case E_CONFIG_OK: - WebServer->sendContent(supla_webpage_i2c(1)); + supla_webpage_i2c(1); break; case E_CONFIG_FILE_OPEN: // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); - WebServer->sendContent(supla_webpage_i2c(2)); + supla_webpage_i2c(2); break; } } -String SuplaWebPageSensor::supla_webpage_i2c(int save) { - uint8_t nr, suported, selected, size; - String page, key; - page += WebServer->SuplaSaveResult(save); - page += WebServer->SuplaJavaScript(PATH_I2C); - page += F("
"); +void SuplaWebPageSensor::supla_webpage_i2c(int save) { + uint8_t selected; -#if defined(SUPLA_BME280) || defined(SUPLA_SI7021) || defined(SUPLA_SHT3x) - addFormHeader(page, String(S_GPIO_SETTINGS_FOR) + " i2c"); - addListGPIOBox(page, INPUT_SDA_GPIO, "SDA", FUNCTION_SDA); - addListGPIOBox(page, INPUT_SCL_GPIO, "SCL", FUNCTION_SCL); + WebServer->sendHeaderStart(); + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_I2C); + + addForm(webContentBuffer, F("post"), PATH_SAVE_I2C); + addFormHeader(webContentBuffer, String(S_GPIO_SETTINGS_FOR) + S_SPACE + S_I2C); + addListGPIOBox(webContentBuffer, INPUT_SDA_GPIO, S_SDA, FUNCTION_SDA); + addListGPIOBox(webContentBuffer, INPUT_SCL_GPIO, S_SCL, FUNCTION_SCL); + addFormHeaderEnd(webContentBuffer); if (ConfigESP->getGpio(FUNCTION_SDA) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_SCL) != OFF_GPIO) { #ifdef SUPLA_BME280 - selected = ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_BME280).toInt(); - addListBox(page, INPUT_BME280, "BME280 adres", BME280_P, 4, selected); - addNumberBox(page, INPUT_ALTITUDE_BME280, S_ALTITUDE_ABOVE_SEA_LEVEL, KEY_ALTITUDE_BME280, 1500); + selected = ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_BME280).toInt(); + addFormHeader(webContentBuffer); + addListBox(webContentBuffer, INPUT_BME280, S_ADDRESS_BMPE280, BME280_P, 4, selected); + addNumberBox(webContentBuffer, INPUT_ALTITUDE_BME280, S_ALTITUDE_ABOVE_SEA_LEVEL, KEY_ALTITUDE_BME280, 1500); + addFormHeaderEnd(webContentBuffer); #endif #ifdef SUPLA_SHT3x - selected = ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_SHT3x).toInt(); - addListBox(page, INPUT_SHT3x, "SHT3x", SHT3x_P, 4, selected); + selected = ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_SHT3x).toInt(); + addFormHeader(webContentBuffer); + addListBox(webContentBuffer, INPUT_SHT3x, S_SHT3X, SHT3x_P, 4, selected); + addFormHeaderEnd(webContentBuffer); #endif #ifdef SUPLA_SI7021 - selected = ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_SI7021).toInt(); - addListBox(page, INPUT_SI7021, "Si7021", STATE_P, 2, selected); + selected = ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_SI7021).toInt(); + addFormHeader(webContentBuffer); + addListBox(webContentBuffer, INPUT_SI7021, S_SI702, STATE_P, 2, selected); + addFormHeaderEnd(webContentBuffer); #endif - } - addFormHeaderEnd(page); + +#ifdef SUPLA_OLED + addFormHeader(webContentBuffer); + + selected = ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_OLED).toInt(); + addListBox(webContentBuffer, INPUT_OLED, S_OLED, OLED_P, 4, selected); + + if (ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_OLED).toInt()) { + String name, sensorName, input; + + addListGPIOBox(webContentBuffer, INPUT_BUTTON_GPIO, S_OLED_BUTTON, FUNCTION_CFG_BUTTON); + selected = ConfigManager->get(KEY_OLED_ANIMATION)->getValueInt(); + addListBox(webContentBuffer, INPUT_OLED_ANIMATION, S_CONTROL, OLED_CONTROLL_P, 3, selected); + addNumberBox(webContentBuffer, INPUT_OLED_BRIGHTNESS, S_BACKLIGHT_S, KEY_OLED_BACK_LIGHT_TIME, 99); + + for (uint8_t i = 0; i < getCountSensorChannels(); i++) { + sensorName = String(ConfigManager->get(KEY_NAME_SENSOR)->getElement(i)); + input = INPUT_OLED_NAME; + input += i; + name = S_SCREEN; + name += i + 1; + addTextBox(webContentBuffer, input, name, sensorName, 0, MAX_DS18B20_NAME, false); + } + } + addFormHeaderEnd(webContentBuffer); +#endif + +#ifdef SUPLA_MCP23017 + selected = ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_MCP23017).toInt(); + addFormHeader(webContentBuffer); + addListBox(webContentBuffer, INPUT_MCP23017, S_MCP23017, STATE_P, 2, selected); + addFormHeaderEnd(webContentBuffer); #endif + } - page += F("
"); - page += F("
"); - page += F("
"); - page += F("
"); - page += F("
"); - page += F(""); - return page; + addButtonSubmit(webContentBuffer, S_SAVE); + addFormEnd(webContentBuffer); + + addButton(webContentBuffer, S_RETURN, PATH_DEVICE_SETTINGS); + WebServer->sendHeaderEnd(); } #endif #if defined(SUPLA_MAX6675) void SuplaWebPageSensor::handleSpi() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); + if (!WebServer->isLoggedIn()) { + return; } - WebServer->sendContent(supla_webpage_spi(0)); + supla_webpage_spi(0); } void SuplaWebPageSensor::handleSpiSave() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); + if (!WebServer->isLoggedIn()) { + return; } - String key, input; - uint8_t nr, current_value, last_value; + String input; + uint8_t key; #if defined(SUPLA_MAX6675) if (!WebServer->saveGPIO(INPUT_CLK_GPIO, FUNCTION_CLK)) { - WebServer->sendContent(supla_webpage_spi(6)); + supla_webpage_spi(6); return; } if (!WebServer->saveGPIO(INPUT_CS_GPIO, FUNCTION_CS)) { - WebServer->sendContent(supla_webpage_spi(6)); + supla_webpage_spi(6); return; } if (!WebServer->saveGPIO(INPUT_D0_GPIO, FUNCTION_D0)) { - WebServer->sendContent(supla_webpage_spi(6)); + supla_webpage_spi(6); return; } #endif @@ -553,307 +572,54 @@ void SuplaWebPageSensor::handleSpiSave() { #ifdef SUPLA_MAX6675 key = KEY_ACTIVE_SENSOR; input = INPUT_MAX6675; - if (strcmp(WebServer->httpServer.arg(input).c_str(), "") != 0) { - ConfigManager->setElement(KEY_ACTIVE_SENSOR, SENSOR_MAX6675, WebServer->httpServer.arg(input).toInt()); + if (strcmp(WebServer->httpServer->arg(input).c_str(), "") != 0) { + ConfigManager->setElement(KEY_ACTIVE_SENSOR, SENSOR_I2C_MAX6675, WebServer->httpServer->arg(input).toInt()); } #endif switch (ConfigManager->save()) { case E_CONFIG_OK: - WebServer->sendContent(supla_webpage_spi(1)); + supla_webpage_spi(1); break; case E_CONFIG_FILE_OPEN: // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); - WebServer->sendContent(supla_webpage_spi(2)); + supla_webpage_spi(2); break; } } -String SuplaWebPageSensor::supla_webpage_spi(int save) { +void SuplaWebPageSensor::supla_webpage_spi(int save) { uint8_t nr, suported, selected; - String page, key; - page += WebServer->SuplaSaveResult(save); - page += WebServer->SuplaJavaScript(PATH_SPI); - page += F("
"); + + WebServer->sendHeaderStart(); + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_SPI); + webContentBuffer += F(""); #if defined(SUPLA_MAX6675) - addFormHeader(page, String(S_GPIO_SETTINGS_FOR) + " SPI"); - addListGPIOBox(page, INPUT_CLK_GPIO, "CLK", FUNCTION_CLK); - addListGPIOBox(page, INPUT_CS_GPIO, "CS", FUNCTION_CS); - addListGPIOBox(page, INPUT_D0_GPIO, "D0", FUNCTION_D0); + addFormHeader(webContentBuffer, String(S_GPIO_SETTINGS_FOR) + S_SPACE + S_SPI); + addListGPIOBox(webContentBuffer, INPUT_CLK_GPIO, S_CLK, FUNCTION_CLK); + addListGPIOBox(webContentBuffer, INPUT_CS_GPIO, S_CS, FUNCTION_CS); + addListGPIOBox(webContentBuffer, INPUT_D0_GPIO, S_D0, FUNCTION_D0); if (ConfigESP->getGpio(FUNCTION_CLK) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_CS) != OFF_GPIO && ConfigESP->getGpio(FUNCTION_D0) != OFF_GPIO) { - selected = ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_MAX6675).toInt(); - addListBox(page, INPUT_MAX6675, "MAX6675", STATE_P, 2, selected); - } - addFormHeaderEnd(page); -#endif - page += F("
"); - page += F("
"); - page += F("
"); - page += F("
"); - page += F("
"); - page += F(""); - return page; -} -#endif - -#if defined(SUPLA_HC_SR04) || defined(SUPLA_IMPULSE_COUNTER) -void SuplaWebPageSensor::handleOther() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); - } - WebServer->sendContent(supla_webpage_other(0)); -} - -void SuplaWebPageSensor::handleOtherSave() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); - } - - String key, input; - uint8_t nr, current_value, last_value; - -#ifdef SUPLA_HC_SR04 - if (!WebServer->saveGPIO(INPUT_TRIG_GPIO, FUNCTION_TRIG)) { - WebServer->sendContent(supla_webpage_other(6)); - return; - } - if (!WebServer->saveGPIO(INPUT_ECHO_GPIO, FUNCTION_ECHO)) { - WebServer->sendContent(supla_webpage_other(6)); - return; - } -#endif - -#ifdef SUPLA_IMPULSE_COUNTER - // Supla::GUI::impulseCounter[0]->setCounter((unsigned long long)WebServer->httpServer.arg(INPUT_IMPULSE_COUNTER_CHANGE_VALUE).toInt()); - - last_value = ConfigManager->get(KEY_MAX_IMPULSE_COUNTER)->getValueInt(); - for (nr = 1; nr <= last_value; nr++) { - if (!WebServer->saveGPIO(INPUT_IMPULSE_COUNTER_GPIO, FUNCTION_IMPULSE_COUNTER, nr, INPUT_MAX_IMPULSE_COUNTER)) { - WebServer->sendContent(supla_webpage_other(6)); - return; - } - } - - if (strcmp(WebServer->httpServer.arg(INPUT_MAX_IMPULSE_COUNTER).c_str(), "") != 0) { - ConfigManager->set(KEY_MAX_IMPULSE_COUNTER, WebServer->httpServer.arg(INPUT_MAX_IMPULSE_COUNTER).c_str()); - } -#endif - - switch (ConfigManager->save()) { - case E_CONFIG_OK: - WebServer->sendContent(supla_webpage_other(1)); - break; - case E_CONFIG_FILE_OPEN: - // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); - WebServer->sendContent(supla_webpage_other(2)); - break; - } -} - -String SuplaWebPageSensor::supla_webpage_other(int save) { - uint8_t nr, suported, selected; - String page, key; - page += WebServer->SuplaSaveResult(save); - page += WebServer->SuplaJavaScript(PATH_OTHER); - page += F("
"); - -#ifdef SUPLA_HC_SR04 - addFormHeader(page, String(S_GPIO_SETTINGS_FOR) + " HC-SR04"); - addListGPIOBox(page, INPUT_TRIG_GPIO, "TRIG", FUNCTION_TRIG); - addListGPIOBox(page, INPUT_ECHO_GPIO, "ECHO", FUNCTION_ECHO); - addFormHeaderEnd(page); -#endif - -#ifdef SUPLA_IMPULSE_COUNTER - addFormHeader(page, String(S_GPIO_SETTINGS_FOR) + " " + S_IMPULSE_COUNTER); - addNumberBox(page, INPUT_MAX_IMPULSE_COUNTER, S_QUANTITY, KEY_MAX_IMPULSE_COUNTER, ConfigESP->countFreeGpio(FUNCTION_IMPULSE_COUNTER)); - for (nr = 1; nr <= ConfigManager->get(KEY_MAX_IMPULSE_COUNTER)->getValueInt(); nr++) { - addListGPIOLinkBox(page, INPUT_IMPULSE_COUNTER_GPIO, "IC GPIO", FUNCTION_IMPULSE_COUNTER, PATH_IMPULSE_COUNTER_SET, nr); - } - addFormHeaderEnd(page); -#endif - page += F("
"); - page += F("
"); - page += F("
"); - page += F("
"); - page += F("
"); - page += F(""); - return page; + selected = ConfigManager->get(KEY_ACTIVE_SENSOR)->getElement(SENSOR_I2C_MAX6675).toInt(); + addListBox(webContentBuffer, INPUT_MAX6675, S_MAX6675_MAX31855, STATE_P, 2, selected); + } + addFormHeaderEnd(webContentBuffer); +#endif + webContentBuffer += F(""); + webContentBuffer += F("
"); + webContentBuffer += F("

"); + WebServer->sendHeaderEnd(); } #endif - -#ifdef SUPLA_IMPULSE_COUNTER -void SuplaWebPageSensor::handleImpulseCounterSet() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); - } - WebServer->sendContent(supla_impulse_counter_set(0)); -} - -void SuplaWebPageSensor::handleImpulseCounterSaveSet() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!WebServer->httpServer.authenticate(WebServer->www_username, WebServer->www_password)) - return WebServer->httpServer.requestAuthentication(); - } - - String readUrl, nr, key, input; - uint8_t place; - - String path = PATH_START; - path += PATH_SAVE_IMPULSE_COUNTER_SET; - readUrl = WebServer->httpServer.uri(); - - place = readUrl.indexOf(path); - nr = readUrl.substring(place + path.length(), place + path.length() + 3); - key = GPIO; - key += ConfigESP->getGpio(nr.toInt(), FUNCTION_IMPULSE_COUNTER); - - input = INPUT_IMPULSE_COUNTER_PULL_UP; - input += nr; - ConfigManager->setElement(key.c_str(), MEMORY, WebServer->httpServer.arg(input).toInt()); - - input = INPUT_IMPULSE_COUNTER_RAISING_EDGE; - input += nr; - ConfigManager->setElement(key.c_str(), LEVEL, WebServer->httpServer.arg(input).toInt()); - - ConfigManager->set(KEY_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT, WebServer->httpServer.arg(INPUT_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT).c_str()); - Supla::GUI::impulseCounter[nr.toInt() - 1]->setCounter((unsigned long long)WebServer->httpServer.arg(INPUT_IMPULSE_COUNTER_CHANGE_VALUE).toInt()); - - switch (ConfigManager->save()) { - case E_CONFIG_OK: - // Serial.println(F("E_CONFIG_OK: Dane zapisane")); - WebServer->sendContent(supla_webpage_other(1)); - break; - - case E_CONFIG_FILE_OPEN: - // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); - WebServer->sendContent(supla_webpage_other(2)); - break; - } -} - -String SuplaWebPageSensor::supla_impulse_counter_set(int save) { - String readUrl, nr, key; - uint8_t place, selected, suported; - - String path = PATH_START; - path += PATH_IMPULSE_COUNTER_SET; - readUrl = WebServer->httpServer.uri(); - - place = readUrl.indexOf(path); - nr = readUrl.substring(place + path.length(), place + path.length() + 3); - - String page = ""; - page += WebServer->SuplaSaveResult(save); - page += WebServer->SuplaJavaScript(PATH_OTHER); - uint8_t relays = ConfigManager->get(KEY_MAX_IMPULSE_COUNTER)->getValueInt(); - if (nr.toInt() <= relays && ConfigESP->getGpio(nr.toInt(), FUNCTION_IMPULSE_COUNTER) != OFF_GPIO) { - page += F("

"); - page += S_IMPULSE_COUNTER_SETTINGS_NR; - page += F(" "); - page += nr; - page += F("

"); - page += F(""); - page += F(""); - addNumberBox(page, INPUT_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT, S_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT, KEY_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT, 99999999); - page += F(""); - - page += F("
"); - } - else { - page += F("

"); - page += S_NO_IMPULSE_COUNTER_NR; - page += F(" "); - page += nr; - page += F("

"); - } - page += F("
"); - page += F("
"); - - return page; -} -#endif \ No newline at end of file diff --git a/src/SuplaWebPageSensor.h b/src/SuplaWebPageSensor.h index 76c6e7c2..a366606e 100644 --- a/src/SuplaWebPageSensor.h +++ b/src/SuplaWebPageSensor.h @@ -3,6 +3,7 @@ #include "SuplaDeviceGUI.h" #include "SuplaWebServer.h" +#include "SuplaWebPageControl.h" #define PATH_MULTI_DS "multids" #define PATH_SAVE_MULTI_DS "savemultids" @@ -12,46 +13,39 @@ #define PATH_SAVE_I2C "savei2c" #define PATH_SPI "spi" #define PATH_SAVE_SPI "savespi" -#define PATH_OTHER "other" -#define PATH_SAVE_OTHER "saveother" -#define PATH_IMPULSE_COUNTER_SET "setimpulsecounter" -#define PATH_SAVE_IMPULSE_COUNTER_SET "savesetimpulsecounter" -#define INPUT_MULTI_DS_GPIO "mdsg" -#define INPUT_DHT11_GPIO "dht11" -#define INPUT_DHT22_GPIO "dht22" -#define INPUT_SDA_GPIO "sdag" -#define INPUT_SCL_GPIO "sclg" -#define INPUT_BME280 "bme280" -#define INPUT_ALTITUDE_BME280 "abme280" -#define INPUT_SHT3x "sht30" -#define INPUT_SI7021 "si7021" -#define INPUT_SI7021_SONOFF "si7021sonoff" -#define INPUT_TRIG_GPIO "trig" -#define INPUT_ECHO_GPIO "echo" -#define INPUT_MAX_DHT11 "mdht11" -#define INPUT_MAX_DHT22 "mdht22" -#define INPUT_MAX_DS18B20 "maxds" -#define INPUT_CLK_GPIO "clk" -#define INPUT_CS_GPIO "cs" -#define INPUT_D0_GPIO "d0" -#define INPUT_MAX6675 "max6675" -#define INPUT_IMPULSE_COUNTER_GPIO "ic" -#define INPUT_IMPULSE_COUNTER_DEBOUNCE_TIMEOUT "icdt" -#define INPUT_IMPULSE_COUNTER_PULL_UP "icpu" -#define INPUT_IMPULSE_COUNTER_RAISING_EDGE "icre" -#define INPUT_IMPULSE_COUNTER_CHANGE_VALUE "iccv" -#define INPUT_MAX_IMPULSE_COUNTER "imic" - -#if defined(SUPLA_BME280) || defined(SUPLA_SHT30) || defined(SUPLA_SI7021) || defined(SUPLA_MAX6675) -enum _sensor +#define INPUT_MULTI_DS_GPIO "mdsg" +#define INPUT_DHT11_GPIO "dht11" +#define INPUT_DHT22_GPIO "dht22" +#define INPUT_SDA_GPIO "sdag" +#define INPUT_SCL_GPIO "sclg" +#define INPUT_BME280 "bme280" +#define INPUT_ALTITUDE_BME280 "abme280" +#define INPUT_SHT3x "sht30" +#define INPUT_SI7021 "si7021" +#define INPUT_OLED "oled" +#define INPUT_OLED_ANIMATION "oleda" +#define INPUT_OLED_BRIGHTNESS "oledb" +#define INPUT_MCP23017 "mcp" +#define INPUT_SI7021_SONOFF "si7021sonoff" +#define INPUT_MAX_DHT11 "mdht11" +#define INPUT_MAX_DHT22 "mdht22" +#define INPUT_MAX_DS18B20 "maxds" +#define INPUT_CLK_GPIO "clk" +#define INPUT_CS_GPIO "cs" +#define INPUT_D0_GPIO "d0" +#define INPUT_MAX6675 "max6675" +#define INPUT_OLED_NAME "ion" + +enum _sensorI2C { - SENSOR_BME280, - SENSOR_SHT3x, - SENSOR_SI7021, - SENSOR_MAX6675 + SENSOR_I2C_BME280, + SENSOR_I2C_SHT3x, + SENSOR_I2C_SI7021, + SENSOR_I2C_MAX6675, + SENSOR_I2C_OLED, + SENSOR_I2C_MCP23017 }; -#endif #ifdef SUPLA_BME280 enum _bmeAdress @@ -76,20 +70,20 @@ class SuplaWebPageSensor { SuplaWebPageSensor(); void createWebPageSensor(); -#if defined(SUPLA_DS18B20) || defined(SUPLA_DHT11) || defined(SUPLA_DHT22) || defined(SUPLA_SI7021_SONOFF) - void handle1Wire(); - void handle1WireSave(); -#endif - #ifdef SUPLA_DS18B20 +#define INPUT_DS18B20_ADDR "dsaddr" +#define INPUT_DS18B20_NAME "dsname" + void handleSearchDS(); void handleDSSave(); - void showDS18B20(String& content, bool readonly = false); + void showDS18B20(bool readonly = false); + void supla_webpage_search(int save); #endif -#if defined(SUPLA_BME280) || defined(SUPLA_SHT30) || defined(SUPLA_SI7021) +#if defined(SUPLA_BME280) || defined(SUPLA_SI7021) || defined(SUPLA_SHT3x) || defined(SUPLA_OLED) || defined(SUPLA_MCP23017) void handlei2c(); void handlei2cSave(); + void supla_webpage_i2c(int save); #endif #if defined(SUPLA_MAX6675) @@ -97,33 +91,14 @@ class SuplaWebPageSensor { void handleSpiSave(); #endif -#if defined(SUPLA_HC_SR04) || defined(SUPLA_IMPULSE_COUNTER) - void handleOther(); - void handleOtherSave(); - - void handleImpulseCounterSet(); - void handleImpulseCounterSaveSet(); - String supla_impulse_counter_set(int save); -#endif - - private: #if defined(SUPLA_DS18B20) || defined(SUPLA_DHT11) || defined(SUPLA_DHT22) || defined(SUPLA_SI7021_SONOFF) - String supla_webpage_1wire(int save); -#ifdef SUPLA_DS18B20 - String supla_webpage_search(int save); -#endif -#endif - -#if defined(SUPLA_BME280) || defined(SUPLA_SHT30) || defined(SUPLA_SI7021) - String supla_webpage_i2c(int save); + void handle1Wire(); + void handle1WireSave(); + void supla_webpage_1wire(int save); #endif #if defined(SUPLA_MAX6675) - String supla_webpage_spi(int save); -#endif - -#if defined(SUPLA_HC_SR04) || defined(SUPLA_IMPULSE_COUNTER) - String supla_webpage_other(int save); + void supla_webpage_spi(int save); #endif }; diff --git a/src/SuplaWebPageTools.cpp b/src/SuplaWebPageTools.cpp new file mode 100644 index 00000000..c8d1f2a4 --- /dev/null +++ b/src/SuplaWebPageTools.cpp @@ -0,0 +1,38 @@ +#include "SuplaWebPageTools.h" +#include "SuplaDeviceGUI.h" + +void createWebTools() { + WebServer->httpServer->on(getURL(PATH_TOOLS), handleTools); + + WebServer->httpServer->on(getURL(PATH_FACTORY_RESET), [&]() { + if (!WebServer->isLoggedIn()) { + return; + } + WebServer->httpServer->sendHeader("Location", "/"); + WebServer->httpServer->send(303); + WebServer->supla_webpage_start(0); + ConfigESP->factoryReset(true); + }); +} + +void handleTools() { + if (!WebServer->isLoggedIn()) { + return; + } + + addFormHeader(webContentBuffer, S_TOOLS); + //#ifdef SUPLA_BUTTON + addButton(webContentBuffer, S_SAVE_CONFIGURATION, PATH_DOWNLOAD); + //#endif + //#ifdef SUPLA_BUTTON + addButton(webContentBuffer, S_LOAD_CONFIGURATION, PATH_UPLOAD); + //#endif +#ifdef SUPLA_OTA + addButton(webContentBuffer, S_UPDATE, PATH_UPDATE_HENDLE); +#endif + addButton(webContentBuffer, S_RESTORE_FACTORY_SETTING, PATH_FACTORY_RESET); + addFormHeaderEnd(webContentBuffer); + addButton(webContentBuffer, S_RETURN, ""); + + WebServer->sendContent(); +} diff --git a/src/SuplaWebPageTools.h b/src/SuplaWebPageTools.h new file mode 100644 index 00000000..ab4f4029 --- /dev/null +++ b/src/SuplaWebPageTools.h @@ -0,0 +1,11 @@ + +#ifndef SuplaWebPageTools_h +#define SuplaWebPageTools_h + +#define PATH_TOOLS "tools" +#define PATH_FACTORY_RESET "factoryreset" + +void createWebTools(); +void handleTools(); + +#endif // ifndef SuplaWebPageTools_h diff --git a/src/SuplaWebPageUpload.cpp b/src/SuplaWebPageUpload.cpp new file mode 100644 index 00000000..70063f3e --- /dev/null +++ b/src/SuplaWebPageUpload.cpp @@ -0,0 +1,88 @@ +#include "SuplaWebPageUpload.h" +#include "SuplaDeviceGUI.h" +#include "FS.h" + +static const char uploadIndex[] PROGMEM = + R"(
+
+ {g}

+ +
)"; + +File dataFile; + +void createWebUpload() { + // WebServer->httpServer->on(F("/upload"), HTTP_GET, handleUpload); + WebServer->httpServer->on(getURL(PATH_UPLOAD), HTTP_GET, []() { handleUpload(); }); + // WebServer->httpServer->on(F("/upload"), HTTP_POST, handleFileUpload); + WebServer->httpServer->on( + getURL(PATH_UPLOAD), HTTP_POST, + []() { + if (WebServer->httpServer->hasArg("generateGUIDandAUTHKEY")) { + if (WebServer->httpServer->arg("generateGUIDandAUTHKEY") == "1") { + ConfigManager->setGUIDandAUTHKEY(); + ConfigManager->save(); + } + } + WebServer->httpServer->send(200); + }, + handleFileUpload); +} + +void handleUpload(int save) { + if (!WebServer->isLoggedIn()) { + return; + } + + String upload = FPSTR(uploadIndex); + upload.replace("{g}", S_GENERATE_GUID_AND_KEY); + upload.replace("{u}", S_UPLOAD); + + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(PATH_UPLOAD); + webContentBuffer += F("
"); + webContentBuffer += F("

"); + webContentBuffer += S_LOAD_CONFIGURATION; + webContentBuffer += F("

"); + webContentBuffer += F("
"); + webContentBuffer += upload; + webContentBuffer += F("
"); + webContentBuffer += F("

"); + + WebServer->sendContent(); +} + +void handleFileUpload() { + if (!WebServer->isLoggedIn()) { + return; + } + + if (SPIFFS.begin()) { + HTTPUpload& upload = WebServer->httpServer->upload(); + + if (upload.status == UPLOAD_FILE_START) { + dataFile = SPIFFS.open(CONFIG_FILE_PATH, "w"); + } + else if (upload.status == UPLOAD_FILE_WRITE) { + if (dataFile) + dataFile.write(upload.buf, upload.currentSize); + } + else if (upload.status == UPLOAD_FILE_END) { + if (dataFile) { + dataFile.close(); + // WebServer->httpServer->sendHeader("Location", "/upload"); + // WebServer->httpServer->send(303); + ConfigManager->load(); + handleUpload(1); + } + else { + handleUpload(6); + // WebServer->httpServer->send(500, "text/plain", "500: couldn't create file"); + } + } + } +} diff --git a/src/SuplaWebPageUpload.h b/src/SuplaWebPageUpload.h new file mode 100644 index 00000000..b7635fe6 --- /dev/null +++ b/src/SuplaWebPageUpload.h @@ -0,0 +1,11 @@ + +#ifndef SuplaWebPageUpload_h +#define SuplaWebPageUpload_h + +#define PATH_UPLOAD "upload" + +void createWebUpload(); +void handleUpload(int save = 0); +void handleFileUpload(); + +#endif // ifndef SuplaWebPageUpload_h diff --git a/src/SuplaWebServer.cpp b/src/SuplaWebServer.cpp index 7dd25519..6af0f3c7 100644 --- a/src/SuplaWebServer.cpp +++ b/src/SuplaWebServer.cpp @@ -22,92 +22,92 @@ #include "SuplaWebPageSensor.h" #include "SuplaCommonPROGMEM.h" #include "SuplaTemplateBoard.h" -#include "GUIGenericCommon.h" - #include "Markup.h" +#include "SuplaWebPageOther.h" + +String webContentBuffer; SuplaWebServer::SuplaWebServer() { + httpServer = new ESP8266WebServer(80); +#ifdef SUPLA_OTA + httpUpdater = new ESP8266HTTPUpdateServer(); +#endif } void SuplaWebServer::begin() { this->createWebServer(); - strcpy(this->www_username, ConfigManager->get(KEY_LOGIN)->getValue()); - strcpy(this->www_password, ConfigManager->get(KEY_LOGIN_PASS)->getValue()); - - httpServer.onNotFound(std::bind(&SuplaWebServer::handleNotFound, this)); - httpServer.begin(); + httpServer->onNotFound(std::bind(&SuplaWebServer::handleNotFound, this)); + httpServer->begin(); } void SuplaWebServer::iterateAlways() { - httpServer.handleClient(); + httpServer->handleClient(); } void SuplaWebServer::createWebServer() { String path = PATH_START; - httpServer.on(path, HTTP_GET, std::bind(&SuplaWebServer::handle, this)); + httpServer->on(path, HTTP_GET, std::bind(&SuplaWebServer::handle, this)); path = PATH_START; - httpServer.on(path, std::bind(&SuplaWebServer::handleSave, this)); + httpServer->on(path, std::bind(&SuplaWebServer::handleSave, this)); path = PATH_START; path += PATH_REBOT; - httpServer.on(path, std::bind(&SuplaWebServer::supla_webpage_reboot, this)); + httpServer->on(path, std::bind(&SuplaWebServer::supla_webpage_reboot, this)); path = PATH_START; path += PATH_DEVICE_SETTINGS; - httpServer.on(path, std::bind(&SuplaWebServer::handleDeviceSettings, this)); + httpServer->on(path, std::bind(&SuplaWebServer::handleDeviceSettings, this)); path = PATH_START; path += PATH_SAVE_BOARD; - httpServer.on(path, std::bind(&SuplaWebServer::handleBoardSave, this)); + httpServer->on(path, std::bind(&SuplaWebServer::handleBoardSave, this)); -#if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) +#if defined(SUPLA_RELAY) || defined(SUPLA_MCP23017) WebPageRelay->createWebPageRelay(); #endif -#if defined(SUPLA_BUTTON) || defined(SUPLA_LIMIT_SWITCH) +#if defined(SUPLA_BUTTON) || defined(SUPLA_LIMIT_SWITCH) || defined(SUPLA_MCP23017) WebPageControl->createWebPageControl(); #endif -#if defined(SUPLA_DS18B20) || defined(SUPLA_DHT11) || defined(SUPLA_DHT22) || defined(SUPLA_SI7021_SONOFF) || defined(SUPLA_BME280) || \ - defined(SUPLA_SHT3x) || defined(SUPLA_SI7021) || defined(SUPLA_MAX6675) || defined(SUPLA_HC_SR04) || defined(SUPLA_IMPULSE_COUNTER) WebPageSensor->createWebPageSensor(); -#endif #ifdef SUPLA_CONFIG WebPageConfig->createWebPageConfig(); #endif #ifdef SUPLA_OTA - httpUpdater.setup(&httpServer, this->www_username, this->www_password); + httpUpdater->setup(httpServer, ConfigManager->get(KEY_LOGIN)->getValue(), ConfigManager->get(KEY_LOGIN_PASS)->getValue()); #endif + + createWebDownload(); + createWebUpload(); + createWebTools(); + createWebPageOther(); } void SuplaWebServer::handle() { - // Serial.println(F("HTTP_GET - metoda handle")); - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!httpServer.authenticate(this->www_username, this->www_password)) - return httpServer.requestAuthentication(); + if (!isLoggedIn()) { + return; } - this->sendContent(supla_webpage_start(0)); + supla_webpage_start(0); } void SuplaWebServer::handleSave() { - // Serial.println(F("HTTP_POST - metoda handleSave")); - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!httpServer.authenticate(this->www_username, this->www_password)) - return httpServer.requestAuthentication(); + if (!isLoggedIn()) { + return; } - if (strcmp(httpServer.arg(PATH_REBOT).c_str(), "1") == 0) { + if (strcmp(httpServer->arg(PATH_REBOT).c_str(), "1") == 0) { ConfigESP->rebootESP(); return; } - ConfigManager->set(KEY_WIFI_SSID, httpServer.arg(INPUT_WIFI_SSID).c_str()); - ConfigManager->set(KEY_WIFI_PASS, httpServer.arg(INPUT_WIFI_PASS).c_str()); - ConfigManager->set(KEY_SUPLA_SERVER, httpServer.arg(INPUT_SERVER).c_str()); - ConfigManager->set(KEY_SUPLA_EMAIL, httpServer.arg(INPUT_EMAIL).c_str()); - ConfigManager->set(KEY_HOST_NAME, httpServer.arg(INPUT_HOSTNAME).c_str()); - ConfigManager->set(KEY_LOGIN, httpServer.arg(INPUT_MODUL_LOGIN).c_str()); - ConfigManager->set(KEY_LOGIN_PASS, httpServer.arg(INPUT_MODUL_PASS).c_str()); + ConfigManager->set(KEY_WIFI_SSID, httpServer->arg(INPUT_WIFI_SSID).c_str()); + ConfigManager->set(KEY_WIFI_PASS, httpServer->arg(INPUT_WIFI_PASS).c_str()); + ConfigManager->set(KEY_SUPLA_SERVER, httpServer->arg(INPUT_SERVER).c_str()); + ConfigManager->set(KEY_SUPLA_EMAIL, httpServer->arg(INPUT_EMAIL).c_str()); + ConfigManager->set(KEY_HOST_NAME, httpServer->arg(INPUT_HOSTNAME).c_str()); + ConfigManager->set(KEY_LOGIN, httpServer->arg(INPUT_MODUL_LOGIN).c_str()); + ConfigManager->set(KEY_LOGIN_PASS, httpServer->arg(INPUT_MODUL_PASS).c_str()); #ifdef SUPLA_ROLLERSHUTTER - if (strcmp(WebServer->httpServer.arg(INPUT_ROLLERSHUTTER).c_str(), "") != 0) { - ConfigManager->set(KEY_MAX_ROLLERSHUTTER, httpServer.arg(INPUT_ROLLERSHUTTER).c_str()); + if (strcmp(WebServer->httpServer->arg(INPUT_ROLLERSHUTTER).c_str(), "") != 0) { + ConfigManager->set(KEY_MAX_ROLLERSHUTTER, httpServer->arg(INPUT_ROLLERSHUTTER).c_str()); } #endif @@ -115,382 +115,246 @@ void SuplaWebServer::handleSave() { case E_CONFIG_OK: // Serial.println(F("E_CONFIG_OK: Dane zapisane")); if (ConfigESP->configModeESP == NORMAL_MODE) { - this->sendContent(supla_webpage_start(1)); + supla_webpage_start(1); ConfigESP->rebootESP(); } else { - this->sendContent(supla_webpage_start(7)); + supla_webpage_start(7); } break; case E_CONFIG_FILE_OPEN: // Serial.println(F("E_CONFIG_FILE_OPEN: Couldn't open file")); - this->sendContent(supla_webpage_start(4)); + supla_webpage_start(4); break; } } void SuplaWebServer::handleDeviceSettings() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!httpServer.authenticate(www_username, www_password)) - return httpServer.requestAuthentication(); + if (!isLoggedIn()) { + return; } - this->sendContent(deviceSettings(0)); + deviceSettings(0); } -String SuplaWebServer::supla_webpage_start(int save) { - String content = F(""); - content += SuplaSaveResult(save); - content += SuplaJavaScript(); - content += F("
"); +void SuplaWebServer::supla_webpage_start(int save) { + WebServer->sendHeaderStart(); + + webContentBuffer += SuplaSaveResult(save); + webContentBuffer += SuplaJavaScript(); - addFormHeader(content, S_SETTING_WIFI_SSID); - addTextBox(content, INPUT_WIFI_SSID, S_WIFI_SSID, KEY_WIFI_SSID, 0, MAX_SSID, true); - addTextBoxPassword(content, INPUT_WIFI_PASS, S_WIFI_PASS, KEY_WIFI_PASS, MIN_PASSWORD, MAX_PASSWORD, true); - addTextBox(content, INPUT_HOSTNAME, S_HOST_NAME, KEY_HOST_NAME, 0, MAX_HOSTNAME, true); - addFormHeaderEnd(content); + addForm(webContentBuffer, F("post")); + addFormHeader(webContentBuffer, S_SETTING_WIFI_SSID); + addTextBox(webContentBuffer, INPUT_WIFI_SSID, S_WIFI_SSID, KEY_WIFI_SSID, 0, MAX_SSID, true); + addTextBoxPassword(webContentBuffer, INPUT_WIFI_PASS, S_WIFI_PASS, KEY_WIFI_PASS, MIN_PASSWORD, MAX_PASSWORD, true); + addTextBox(webContentBuffer, INPUT_HOSTNAME, S_HOST_NAME, KEY_HOST_NAME, 0, MAX_HOSTNAME, true); + addFormHeaderEnd(webContentBuffer); - addFormHeader(content, S_SETTING_SUPLA); - addTextBox(content, INPUT_SERVER, S_SUPLA_SERVER, KEY_SUPLA_SERVER, DEFAULT_SERVER, 0, MAX_SUPLA_SERVER, true); - addTextBox(content, INPUT_EMAIL, S_SUPLA_EMAIL, KEY_SUPLA_EMAIL, DEFAULT_EMAIL, 0, MAX_EMAIL, true); - addFormHeaderEnd(content); + addFormHeader(webContentBuffer, S_SETTING_SUPLA); + addTextBox(webContentBuffer, INPUT_SERVER, S_SUPLA_SERVER, KEY_SUPLA_SERVER, DEFAULT_SERVER, 0, MAX_SUPLA_SERVER, true); + addTextBox(webContentBuffer, INPUT_EMAIL, S_SUPLA_EMAIL, KEY_SUPLA_EMAIL, DEFAULT_EMAIL, 0, MAX_EMAIL, true); + addFormHeaderEnd(webContentBuffer); - addFormHeader(content, S_SETTING_ADMIN); - addTextBox(content, INPUT_MODUL_LOGIN, S_LOGIN, KEY_LOGIN, 0, MAX_MLOGIN, true); - addTextBoxPassword(content, INPUT_MODUL_PASS, S_LOGIN_PASS, KEY_LOGIN_PASS, MIN_PASSWORD, MAX_MPASSWORD, true); - addFormHeaderEnd(content); + addFormHeader(webContentBuffer, S_SETTING_ADMIN); + addTextBox(webContentBuffer, INPUT_MODUL_LOGIN, S_LOGIN, KEY_LOGIN, 0, MAX_MLOGIN, true); + addTextBoxPassword(webContentBuffer, INPUT_MODUL_PASS, S_LOGIN_PASS, KEY_LOGIN_PASS, MIN_PASSWORD, MAX_MPASSWORD, true); + addFormHeaderEnd(webContentBuffer); #ifdef SUPLA_ROLLERSHUTTER uint8_t maxrollershutter = ConfigManager->get(KEY_MAX_RELAY)->getValueInt(); if (maxrollershutter >= 2) { - content += F("
"); - content += F("

"); - content += S_ROLLERSHUTTERS; - content += F("

"); - content += F(""); - content += F("
"); + addFormHeader(webContentBuffer, S_ROLLERSHUTTERS); + addNumberBox(webContentBuffer, INPUT_ROLLERSHUTTER, S_QUANTITY, KEY_MAX_ROLLERSHUTTER, (maxrollershutter / 2)); + addFormHeaderEnd(webContentBuffer); } #endif -#ifdef SUPLA_DS18B20 - WebPageSensor->showDS18B20(content, true); -#endif + addButtonSubmit(webContentBuffer, S_SAVE); + addFormEnd(webContentBuffer); - content += F("
"); - content += F("
"); - content += F(""); - content += F("

"); -#ifdef SUPLA_OTA - content += F(""); - content += F("

"); -#endif - content += F("
"); - content += F("
"); - return content; + addButton(webContentBuffer, S_DEVICE_SETTINGS, PATH_DEVICE_SETTINGS); + addButton(webContentBuffer, S_TOOLS, PATH_TOOLS); + + WebServer->sendHeaderEnd(); } void SuplaWebServer::supla_webpage_reboot() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!httpServer.authenticate(www_username, www_password)) - return httpServer.requestAuthentication(); + if (!isLoggedIn()) { + return; } - this->sendContent(supla_webpage_start(2)); + supla_webpage_start(2); ConfigESP->rebootESP(); } -String SuplaWebServer::deviceSettings(int save) { - String content = ""; - - content += WebServer->SuplaSaveResult(save); - content += WebServer->SuplaJavaScript(PATH_DEVICE_SETTINGS); - content += F("
"); - content += F("

"); - content += S_TEMPLATE_BOARD; - content += F("

"); - content += F(""); - content += F("


"); - content += F("
"); - content += F("

"); - content += S_DEVICE_SETTINGS; - content += F("

"); - content += F("
"); - content += F("
"); - -#if defined(SUPLA_RELAY) || defined(SUPLA_ROLLERSHUTTER) - content += F(""); - content += F("

"); + addListBox(webContentBuffer, INPUT_BOARD, S_TYPE, BOARD_P, MAX_MODULE, selected); + addFormHeaderEnd(webContentBuffer); + addButtonSubmit(webContentBuffer, S_SAVE); + addFormEnd(webContentBuffer); + + addFormHeader(webContentBuffer, S_DEVICE_SETTINGS); +#if defined(SUPLA_RELAY) + addButton(webContentBuffer, S_RELAYS, PATH_RELAY); #endif #ifdef SUPLA_BUTTON - content += F(""); - content += F("

"); + addButton(webContentBuffer, S_BUTTONS, PATH_CONTROL); +#endif + +#ifdef SUPLA_LIMIT_SWITCH + addButton(webContentBuffer, S_LIMIT_SWITCHES, PATH_SWITCH); #endif #if defined(SUPLA_DS18B20) || defined(SUPLA_DHT11) || defined(SUPLA_DHT22) || defined(SUPLA_SI7021_SONOFF) - content += F(""); - content += F("

"); + addButton(webContentBuffer, S_SENSORS_1WIRE, PATH_1WIRE); #endif -#if defined(SUPLA_BME280) || defined(SUPLA_SHT30) || defined(SUPLA_SI7021) - content += F(""); - content += F("

"); +#if defined(SUPLA_BME280) || defined(SUPLA_SI7021) || defined(SUPLA_SHT3x) || defined(SUPLA_OLED) || defined(SUPLA_MCP23017) + addButton(webContentBuffer, S_SENSORS_I2C, PATH_I2C); #endif #if defined(SUPLA_MAX6675) - content += F(""); - content += F("

"); + addButton(webContentBuffer, S_SENSORS_SPI, PATH_SPI); #endif -#if defined(SUPLA_HC_SR04) || defined(SUPLA_IMPULSE_COUNTER) - content += F(""); - content += F("

"); +#if defined(SUPLA_HC_SR04) || defined(SUPLA_IMPULSE_COUNTER) || defined(SUPLA_HLW8012) || defined(SUPLA_PUSHOVER) + addButton(webContentBuffer, S_SENSORS_OTHER, PATH_OTHER); #endif #ifdef SUPLA_CONFIG - content += F(""); - content += F("

"); + addButton(webContentBuffer, S_LED_BUTTON_CFG, PATH_CONFIG); #endif - content += F("
"); - content += F(""); - content += F(""); + addFormHeaderEnd(webContentBuffer); + addButton(webContentBuffer, S_RETURN, ""); - return content; + WebServer->sendHeaderEnd(); } void SuplaWebServer::handleBoardSave() { - if (ConfigESP->configModeESP == NORMAL_MODE) { - if (!httpServer.authenticate(this->www_username, this->www_password)) - return httpServer.requestAuthentication(); + if (!isLoggedIn()) { + return; } String input = INPUT_BOARD; - if (strcmp(WebServer->httpServer.arg(input).c_str(), "") != 0) { - ConfigManager->set(KEY_BOARD, httpServer.arg(input).c_str()); + if (strcmp(WebServer->httpServer->arg(input).c_str(), "") != 0) { + ConfigManager->set(KEY_BOARD, httpServer->arg(input).c_str()); int nr; - String key; + uint8_t key; for (nr = 0; nr <= 17; nr++) { - key = GPIO; - key += nr; - ConfigManager->set(key.c_str(), "0,0,0,0,0"); + key = KEY_GPIO + nr; + ConfigManager->set(key, ""); } - chooseTemplateBoard(WebServer->httpServer.arg(input).toInt()); + chooseTemplateBoard(WebServer->httpServer->arg(input).toInt()); } switch (ConfigManager->save()) { case E_CONFIG_OK: - WebServer->sendContent(deviceSettings(1)); + deviceSettings(1); break; case E_CONFIG_FILE_OPEN: - WebServer->sendContent(deviceSettings(2)); + deviceSettings(2); break; } } -const String SuplaWebServer::SuplaFavicon() { - // return F("\n"); - return F(""); -} +void SuplaWebServer::sendHeaderStart() { + chunkedSendHeader = true; + tcpCleanup(); + httpServer->sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate")); + httpServer->sendHeader(F("Pragma"), F("no-cache")); + httpServer->sendHeader(F("Expires"), F("-1")); + httpServer->setContentLength(CONTENT_LENGTH_UNKNOWN); + httpServer->chunkedResponseModeStart(200, F("text/html")); -const String SuplaWebServer::SuplaIconEdit() { - return F( - ""); -} + httpServer->sendContent_P(HTTP_META); + httpServer->sendContent_P(HTTP_FAVICON); + httpServer->sendContent_P(HTTP_STYLE); + httpServer->sendContent_P(HTTP_LOGO); -const String SuplaWebServer::SuplaJavaScript(String java_return) { - String java_script = - F("\n"); -#ifdef SUPLA_OTA - java_script += - F("\n"); -#endif - return java_script; -} + String summary = FPSTR(HTTP_SUMMARY); -const String SuplaWebServer::SuplaSaveResult(int save) { - if (save == 0) - return F(""); - String saveresult = ""; - saveresult += F("
"); - if (save == 1) { - saveresult += S_DATA_SAVED; - } - else if (save == 2) { - saveresult += S_RESTART_MODULE; - } - else if (save == 3) { - saveresult += S_DATA_ERASED_RESTART_DEVICE; - } - else if (save == 4) { - saveresult += S_WRITE_ERROR_UNABLE_TO_READ_FILE_FS_PARTITION_MISSING; - } - else if (save == 5) { - saveresult += S_DATA_SAVED_RESTART_MODULE; - } - else if (save == 6) { - saveresult += S_WRITE_ERROR_BAD_DATA; - } - else if (save == 7) { - saveresult += F("data saved"); - } - saveresult += F("
"); - return saveresult; + summary.replace(F("{h}"), ConfigManager->get(KEY_HOST_NAME)->getValue()); + summary.replace(F("{s}"), ConfigESP->getLastStatusSupla()); + summary.replace(F("{v}"), Supla::Channel::reg_dev.SoftVer); + summary.replace(F("{g}"), ConfigManager->get(KEY_SUPLA_GUID)->getValueHex(SUPLA_GUID_SIZE)); + summary.replace(F("{m}"), ConfigESP->getMacAddress(true)); + summary.replace(F("{f}"), String(ESP.getFreeHeap() / 1024.0)); + + httpServer->sendContent(summary); + httpServer->sendContent_P(HTTP_COPYRIGHT); } -void SuplaWebServer::sendContent(const String content) { - // httpServer.send(200, "text/html", ""); - const int bufferSize = 1000; - String _buffer; - int bufferCounter = 0; - int fileSize = content.length(); +void SuplaWebServer::sendHeader() { + if (!chunkedSendHeader) + return; + + if (!webContentBuffer.isEmpty()) { + httpServer->sendContent(webContentBuffer); + webContentBuffer.clear(); + webContentBuffer = String(); + delay(0); + } #ifdef DEBUG_MODE - Serial.print("Content size: "); - Serial.println(fileSize); + Serial.printf_P(PSTR("Content size=%d\n"), webContentBuffer.length()); + Serial.printf_P(PSTR("Sent INDEX...Free mem=%d\n"), ESP.getFreeHeap()); #endif +} - httpServer.setContentLength(fileSize); - httpServer.chunkedResponseModeStart(200, "text/html"); +void SuplaWebServer::sendHeaderEnd() { + if (!chunkedSendHeader) + return; - httpServer.sendContent_P(HTTP_META); - httpServer.sendContent_P(HTTP_STYLE); - httpServer.sendContent_P(HTTP_LOGO); + sendHeader(); + httpServer->sendContent_P(HTTP_RBT); + httpServer->chunkedResponseFinalize(); - String summary = FPSTR(HTTP_SUMMARY); + tcpCleanup(); + httpServer->client().flush(); + httpServer->client().stop(); + chunkedSendHeader = false; - summary.replace("{h}", ConfigManager->get(KEY_HOST_NAME)->getValue()); - summary.replace("{s}", ConfigESP->getLastStatusSupla()); - summary.replace("{v}", Supla::Channel::reg_dev.SoftVer); - summary.replace("{g}", ConfigManager->get(KEY_SUPLA_GUID)->getValueHex(SUPLA_GUID_SIZE)); - summary.replace("{m}", ConfigESP->getMacAddress(true)); - httpServer.sendContent(summary); - httpServer.sendContent_P(HTTP_COPYRIGHT); - - // httpServer.send(200, "text/html", ""); - for (int i = 0; i < fileSize; i++) { - _buffer += content[i]; - bufferCounter++; - - if (bufferCounter >= bufferSize) { - httpServer.sendContent(_buffer); - yield(); - bufferCounter = 0; - _buffer = ""; - } - } - if (bufferCounter > 0) { - httpServer.sendContent(_buffer); - yield(); - bufferCounter = 0; - _buffer = ""; - } +#ifdef DEBUG_MODE + checkRAM(); +#endif +} - httpServer.chunkedResponseFinalize(); +void SuplaWebServer::sendContent() { + sendHeaderStart(); + sendHeader(); + sendHeaderEnd(); } void SuplaWebServer::handleNotFound() { - httpServer.sendHeader("Location", "/", true); - httpServer.send(302, "text/plane", ""); + httpServer->sendHeader("Location", "/", true); + + supla_webpage_reboot(); +} + +bool SuplaWebServer::isLoggedIn() { + if (ConfigESP->configModeESP == NORMAL_MODE) { + if (!httpServer->authenticate(ConfigManager->get(KEY_LOGIN)->getValue(), ConfigManager->get(KEY_LOGIN_PASS)->getValue())) + httpServer->requestAuthentication(); + return true; + } + return true; } bool SuplaWebServer::saveGPIO(const String& _input, uint8_t function, uint8_t nr, const String& input_max) { - uint8_t current_value; - String key, input; + uint8_t gpio, _gpio, _function, _nr, current_value, key; + String input; input = _input; + if (nr != 0) { input += nr; } @@ -498,20 +362,31 @@ bool SuplaWebServer::saveGPIO(const String& _input, uint8_t function, uint8_t nr nr = 1; } - key = GPIO; - key += WebServer->httpServer.arg(input).toInt(); - - if (ConfigESP->getGpio(nr, function) != WebServer->httpServer.arg(input).toInt() || WebServer->httpServer.arg(input).toInt() == OFF_GPIO) { - ConfigESP->clearGpio(ConfigESP->getGpio(nr, function), function); + if (strcmp(WebServer->httpServer->arg(input).c_str(), "") == 0) { + return true; } - if (WebServer->httpServer.arg(input).toInt() != OFF_GPIO) { - if (ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == FUNCTION_OFF) { - ConfigESP->setGpio(WebServer->httpServer.arg(input).toInt(), nr, function, 1); + gpio = ConfigESP->getGpio(nr, function); + _gpio = WebServer->httpServer->arg(input).toInt(); + + key = KEY_GPIO + _gpio; + _function = ConfigManager->get(key)->getElement(FUNCTION).toInt(); + _nr = ConfigManager->get(key)->getElement(NR).toInt(); + + if (_gpio == OFF_GPIO) + ConfigESP->clearGpio(gpio, function); + + if (_gpio != OFF_GPIO) { + if (_function == FUNCTION_OFF && _nr == FUNCTION_OFF) { + ConfigESP->clearGpio(gpio, function); + ConfigESP->clearGpio(_gpio, function); + ConfigESP->setGpio(_gpio, nr, function); } - else if (ConfigESP->getGpio(nr, function) == WebServer->httpServer.arg(input).toInt() && - ConfigManager->get(key.c_str())->getElement(FUNCTION).toInt() == function) { - ConfigESP->setGpio(WebServer->httpServer.arg(input).toInt(), nr, function, ConfigESP->getLevel(nr, function)); + else if (gpio == _gpio && _function == function && _nr == nr) { + ConfigESP->setGpio(_gpio, nr, function); + } + else if (function == FUNCTION_CFG_BUTTON) { + ConfigESP->setGpio(_gpio, FUNCTION_CFG_BUTTON); } else { return false; @@ -519,10 +394,63 @@ bool SuplaWebServer::saveGPIO(const String& _input, uint8_t function, uint8_t nr } if (input_max != "\n") { - current_value = WebServer->httpServer.arg(input_max).toInt(); - if (ConfigManager->get(key.c_str())->getElement(NR).toInt() > current_value) { - ConfigESP->clearGpio(ConfigESP->getGpio(nr, function), function); + current_value = WebServer->httpServer->arg(input_max).toInt(); + if (ConfigManager->get(key)->getElement(NR).toInt() > current_value) { + ConfigESP->clearGpio(gpio, function); + } + } + return true; +} + +bool SuplaWebServer::saveGpioMCP23017(const String& _input, uint8_t function, uint8_t nr, const String& input_max) { + uint8_t key, address, _address, gpio, _gpio, _function, _nr; + String input = _input + nr; + + if (strcmp(WebServer->httpServer->arg(input).c_str(), "") == 0) { + return true; + } + + address = ConfigESP->getAdressMCP23017(nr, function); + if (nr <= 16) + _address = WebServer->httpServer->arg(String(INPUT_ADRESS_MCP23017) + 1).toInt(); + if (nr >= 17) + _address = WebServer->httpServer->arg(String(INPUT_ADRESS_MCP23017) + 17).toInt(); + + gpio = ConfigESP->getGpioMCP23017(nr, function); + _gpio = WebServer->httpServer->arg(input).toInt(); + + key = KEY_GPIO + _gpio; + _function = ConfigManager->get(key)->getElement(ConfigESP->getFunctionMCP23017(_address)).toInt(); + _nr = ConfigManager->get(key)->getElement(ConfigESP->getNrMCP23017(_address)).toInt(); + + if (_gpio == OFF_GPIO || _address == OFF_MCP23017) + ConfigESP->clearGpioMCP23017(gpio, nr, function); + + if (_gpio != OFF_GPIO && _address != OFF_MCP23017) { + if (_function == FUNCTION_OFF && _nr == FUNCTION_OFF) { + ConfigESP->clearGpioMCP23017(gpio, nr, function); + ConfigESP->clearGpioMCP23017(_gpio, nr, function); + ConfigESP->setGpioMCP23017(_gpio, _address, nr, function); + } + else if (gpio == _gpio && function == _function && nr == _nr) { + ConfigESP->setGpioMCP23017(_gpio, _address, nr, function); + } + else { + return false; } } return true; -} \ No newline at end of file +} + +#if defined(ESP8266) + +struct tcp_pcb; +extern struct tcp_pcb* tcp_tw_pcbs; +extern "C" void tcp_abort(struct tcp_pcb* pcb); + +void tcpCleanup() { + while (tcp_tw_pcbs != NULL) { + tcp_abort(tcp_tw_pcbs); + } +} +#endif diff --git a/src/SuplaWebServer.h b/src/SuplaWebServer.h index 50e5a11d..816fa2d8 100644 --- a/src/SuplaWebServer.h +++ b/src/SuplaWebServer.h @@ -26,15 +26,6 @@ #include "SuplaConfigManager.h" -#define GUI_BLUE "#005c96" -#define GUI_GREEN "#00D151" - -#define DEFAULT_LOGIN "admin" -#define DEFAULT_PASSWORD "password" - -#define MAX_GPIO 13 -#define OFF_GPIO 17 - #define PATH_START "/" #define PATH_SAVE_LOGIN "savelogin" #define PATH_REBOT "rbt" @@ -53,28 +44,29 @@ #define INPUT_ROLLERSHUTTER "irsr" #define INPUT_BOARD "board" +extern String webContentBuffer; + class SuplaWebServer : public Supla::Element { public: SuplaWebServer(); void begin(); + void supla_webpage_start(int save); - char www_username[MAX_MLOGIN]; - char www_password[MAX_MPASSWORD]; + bool chunkedSendHeader = false; + void sendHeaderStart(); + void sendHeader(); + void sendHeaderEnd(); - const String SuplaFavicon(); - const String SuplaIconEdit(); - const String SuplaJavaScript(String java_return = PATH_START); - const String SuplaSaveResult(int save); + void sendContent(); - void sendContent(const String content); + ESP8266WebServer* httpServer; - ESP8266WebServer httpServer = {80}; - #ifdef SUPLA_OTA - ESP8266HTTPUpdateServer httpUpdater; + ESP8266HTTPUpdateServer* httpUpdater; #endif - - bool saveGPIO(const String& input, uint8_t function, uint8_t nr = 0, const String& input_max = "\n"); + bool isLoggedIn(); + bool saveGPIO(const String& _input, uint8_t function, uint8_t nr = 0, const String& input_max = "\n"); + bool saveGpioMCP23017(const String& _input, uint8_t function, uint8_t nr = 0, const String& input_max = "\n"); private: void iterateAlways(); @@ -87,12 +79,21 @@ class SuplaWebServer : public Supla::Element { void handleLoginSettings(); void createWebServer(); - String supla_webpage_start(int save); void supla_webpage_reboot(); - String deviceSettings(int save); - String loginSettings(); + void deviceSettings(int save); void handleNotFound(); }; +#if defined(ESP8266) +#include +#endif +#if defined(ESP8266) + +struct tcp_pcb; +extern struct tcp_pcb* tcp_tw_pcbs; +extern "C" void tcp_abort(struct tcp_pcb* pcb); + +void tcpCleanup(); +#endif #endif // SuplaWebServer_h diff --git a/src/language/common.h b/src/language/common.h new file mode 100644 index 00000000..9d2e647d --- /dev/null +++ b/src/language/common.h @@ -0,0 +1,36 @@ + +#ifndef LANGUAGE_COMMON_H_ +#define LANGUAGE_COMMON_H_ + +#define S_SPACE " " + +//### Category codes not translated ### +#define S_I2C "i2c" +#define S_SPI "SPI" + +//### Device codes not translated ### +#define S_LED "LED" +#define S_HC_SR04 "HC-SR04" +#define S_HLW8012 "HLW8012" +#define S_RGBW_RGB_DIMMER "RGBW/RGB/DIMMER" +#define S_OLED "OLED" +#define S_BME280 "BME280" +#define S_DS18B20 "DS18B20" +#define S_MULTI_DS18B20 "Multi DS18B20" +#define S_DHT11 "DHT11" +#define S_DHT22 "DHT22" +#define S_SHT3X "SHT3x" +#define S_SI702 "Si7021" +#define S_SI7021_SONOFF "Si7021 Sonoff" +#define S_MCP23017 "MCP23017" +#define S_MAX6675 "MAX6675" +#define S_MAX6675_MAX31855 "MAX6675/MAX31855" +#define S_PUSHOVER "Pushover" +//### PIN codes not translated ### +#define S_D0 "D0" +#define S_SDA "SDA" +#define S_SCL "SCL" +#define S_CLK "CLK" +#define S_CS "CS" + +#endif /* LANGUAGE_COMMON_H_ */ diff --git a/src/language/de.h b/src/language/de.h index 68858331..4fb3c7f3 100644 --- a/src/language/de.h +++ b/src/language/de.h @@ -1,6 +1,9 @@ #ifndef _LANGUAGE_DE_S_H_ #define _LANGUAGE_DE_S_H_ +#define S_LANG "de" + +#define S_SETTING_FOR "Einstellungen für" #define S_SETTING_WIFI_SSID "WIFI Einstellung" #define S_WIFI_SSID "WIFI Name" #define S_WIFI_PASS "Passwort" @@ -14,6 +17,10 @@ #define S_ROLLERSHUTTERS "Schalousien" #define S_SAVE "Speichern" #define S_DEVICE_SETTINGS "Gerät Einstellung" +#define S_TOOLS "Werkzeuge" +#define S_SAVE_CONFIGURATION "Konfiguration speichern" +#define S_LOAD_CONFIGURATION "Konfiguration laden" +#define S_RESTORE_FACTORY_SETTING "Stellen Sie die Werkseinstellungen wieder her" #define S_UPDATE "Aktualisierung" #define S_RESTART "Neustart" #define S_RETURN "Zurück" @@ -39,6 +46,7 @@ #define S_BUTTON_NR_SETTINGS "Parameter für Tasten Nr." #define S_NO_BUTTON_NR "Kein Tasten Nr." #define S_REACTION_TO "Reaktion auf" +#define S_ACTION "Action" #define S_GPIO_SETTINGS_FOR_LIMIT_SWITCH "GPIO-Einstellungen für Endschalter" #define S_LIMIT_SWITCH "ENDSCHALTER" #define S_GPIO_SETTINGS_FOR "GPIO-Parameter für" @@ -81,6 +89,7 @@ //#### SuplaCommonPROGMEM.h #### #define S_OFF "AUS" #define S_ON "EINGESCHALTET" +#define S_TOGGLE "TOGGLE" #define S_LOW "NIEDRIG" #define S_HIGH "HOCH" #define S_POSITION_MEMORY "LETZTEN STAND ZURÜCKSETZEN" @@ -89,6 +98,16 @@ #define S_REACTION_ON_CHANGE "STATUS ÄNDERUNG" #define S_CFG_10_PRESSES "10 MAL DRÜCKEN" #define S_5SEK_HOLD "5 SEKUNDEN GEDRÜCKT HALTEN" +#define S_NORMAL "NORMAL" +#define S_SLOW "SCHLEPPEND" +#define S_MANUALLY "MANUELL" +#define S_ON_CH_VAL_OFF_HEATING "EIN < Kanalwert > AUS - Heizung" +#define S_ON_CH_VAL_OFF_COOLING "EIN > Kanalwert < AUS - Kühlung" +#define S_ON_2CH_VAL_OFF_HUMIDIFICATION "EIN < 2. Kanalwert > AUS - Befeuchtung" +#define S_ON_2CH_VAL_OFF_DRYING "EIN > 2. Kanalwert < AUS - Trocknen" + +//#### SuplaWebServer.cpp #### +#define S_LIMIT_SWITCHES "GRENZSCHALTER" //#### SuplaTemplateBoard.h #### #define S_ABSENT "ABWESEND" @@ -100,6 +119,48 @@ #define S_IMPULSE_COUNTER_PULL_UP "Pull-up" #define S_IMPULSE_COUNTER_CHANGE_VALUE "Wertänderung" #define S_IMPULSE_COUNTER_SETTINGS_NR "Einstellung Impulszähler Nr." -#define S_NO_IMPULSE_COUNTER_NR "Fehlender Impulszähler Nr." +#define S_CONTROL "Steuerung" +#define S_OLED_BUTTON "OLED-Taste" +#define S_SCREEN "Bildschirm" +#define S_BACKLIGHT_S "Hintergrundbeleuchtung [s]" +#define S_ADDRESS_BMPE280 "Adresse BME280" + +//#### SuplaWebPageUpload.cpp #### +#define S_GENERATE_GUID_AND_KEY "Generieren GUID & AUTHKEY" +#define S_UPLOAD "Hochladen" + +//#### SuplaWebPageControl.cpp #### +#define S_SETTINGS_FOR_BUTTONS "Einstellungen für Schaltflächen" +#define S_REVERSE_LOGIC "Umgekehrte Logik" +#define S_INTERNAL_PULL_UP "Interner Klimmzug" + +//#### SuplaWebPageOther.cpp #### +#define S_CALIBRATION "Kalibrierung" +#define S_CALIBRATION_SETTINGS "Kalibrierungseinstellungen" +#define S_BULB_POWER_W "Lampenleistung [W]" +#define S_VOLTAGE_V "Spannung [V]" +#define S_DEPTH_CM "Tiefe [cm]" +#define S_SENSOR_READING_DISTANCE "Sensorleseabstand" + +//#### SuplaWebPageRelay.cpp #### +#define S_RELAY_ACTIVATION_STATUS "Relaisaktivierungsstatus" +#define S_STATE "Zustand" +#define S_MESSAGE "Botschaft" +#define S_DIRECT_LINKS "Direkte Links" +#define S_CONDITIONING "Konditionierung" +#define S_SENSOR "Sensor" +#define S_CONDITION "Bedingung" +#define S_SWITCH_ON_VALUE "Einschaltwert" +#define S_SWITCH_OFF_VALUE "Ausschaltwert" +#define S_SETTINGS_FOR_RELAYS "Einstellungen für Relais" + +//#### SuplaHTTPUpdateServer.cpp #### +#define S_FLASH_MEMORY_SIZE "Flash-Speichergröße" +#define S_SKETCH_LOADED_SIZE "Skizze geladene Größe" +#define S_SKETCH_UPLOAD_MAX_SIZE "Skizze Hochladen Max Größe" +#define S_UPDATE_FIRMWARE "Firmware aktualisieren" +#define S_UPDATE_SUCCESS_REBOOTING "Update Erfolg! Neustart ..." +#define S_WARNING "WARNUNG" +#define S_ONLY_2_STEP_OTA "Verwenden Sie nur das 2-Schritt-OTA-Update. Benutzen" #endif // _LANGUAGE_DE_S_H_ diff --git a/src/language/en.h b/src/language/en.h index 08814beb..5dd0fcb0 100644 --- a/src/language/en.h +++ b/src/language/en.h @@ -1,6 +1,9 @@ #ifndef _LANGUAGE_EN_S_H_ #define _LANGUAGE_EN_S_H_ +#define S_LANG "en" + +#define S_SETTING_FOR "Setting for" #define S_SETTING_WIFI_SSID "Setting WIFI" #define S_WIFI_SSID "Name WIFI" #define S_WIFI_PASS "Password" @@ -14,6 +17,10 @@ #define S_ROLLERSHUTTERS "Roller shuter" #define S_SAVE "Save" #define S_DEVICE_SETTINGS "Device settings" +#define S_TOOLS "Tools" +#define S_SAVE_CONFIGURATION "Save configuration" +#define S_LOAD_CONFIGURATION "Load configuration" +#define S_RESTORE_FACTORY_SETTING "Restore factory settings" #define S_UPDATE "Update" #define S_RESTART "Restart" #define S_RETURN "Return" @@ -24,7 +31,7 @@ #define S_SENSORS_1WIRE "SENSORS 1Wire" #define S_SENSORS_I2C "SENSORS i2c" #define S_SENSORS_SPI "SENSORS SPI" -#define S_SENSORS_OTHER "SENSORY OTHER" +#define S_SENSORS_OTHER "SENSORS OTHER" #define S_LED_BUTTON_CFG "LED, BUTTON CONFIG" #define S_CFG_MODE "CFG mode" #define S_QUANTITY "QUANTITY" @@ -39,9 +46,10 @@ #define S_BUTTON_NR_SETTINGS "Setting button nr." #define S_NO_BUTTON_NR "No button nr." #define S_REACTION_TO "Reaction to" +#define S_ACTION "Action" #define S_GPIO_SETTINGS_FOR_LIMIT_SWITCH "GPIO settings for limit switch" #define S_LIMIT_SWITCH "LIMIT SWITCH" -#define S_GPIO_SETTINGS_FOR "GPIO settigs for" +#define S_GPIO_SETTINGS_FOR "GPIO settings for" #define S_FOUND "Found" #define S_NO_SENSORS_CONNECTED "No sensor conected" #define S_SAVE_FOUND "Save found" @@ -53,7 +61,7 @@ #define S_DATA_SAVED "Data saved" #define S_RESTART_MODULE "Restart module" #define S_DATA_ERASED_RESTART_DEVICE "Data erased - restart module" -#define S_WRITE_ERROR_UNABLE_TO_READ_FILE_FS_PARTITION_MISSING "Write error - ubable to read file FS. Partition missing" +#define S_WRITE_ERROR_UNABLE_TO_READ_FILE_FS_PARTITION_MISSING "Write error - unable to read file FS. Partition missing" #define S_DATA_SAVED_RESTART_MODULE "Data saved - restart module" #define S_WRITE_ERROR_BAD_DATA "Write error - bad data" @@ -81,6 +89,7 @@ //#### SuplaCommonPROGMEM.h #### #define S_OFF "OFF" #define S_ON "ON" +#define S_TOGGLE "TOGGLE" #define S_LOW "LOW" #define S_HIGH "HIGH" #define S_POSITION_MEMORY "POSITION MEMORY" @@ -88,7 +97,17 @@ #define S_REACTION_ON_RELEASE "ON RELEASE" #define S_REACTION_ON_CHANGE "ON CHANGE" #define S_CFG_10_PRESSES "10 ON PRESSES" -#define S_5SEK_HOLD "5 SEK HOLD" +#define S_5SEK_HOLD "5 SEC HOLD" +#define S_NORMAL "NORMAL" +#define S_SLOW "SLOW" +#define S_MANUALLY "MANUALLY" +#define S_ON_CH_VAL_OFF_HEATING "ON < channel value > OFF - heating" +#define S_ON_CH_VAL_OFF_COOLING "ON > channel value < OFF - cooling" +#define S_ON_2CH_VAL_OFF_HUMIDIFICATION "ON < 2nd channel value > OFF - humidification" +#define S_ON_2CH_VAL_OFF_DRYING "ON > 2nd channel value < OFF - drying" + +//#### SuplaWebServer.cpp #### +#define S_LIMIT_SWITCHES "LIMIT SWITCHES" //#### SuplaTemplateBoard.h #### #define S_ABSENT "ABSENT" @@ -100,6 +119,48 @@ #define S_IMPULSE_COUNTER_PULL_UP "Pull up" #define S_IMPULSE_COUNTER_CHANGE_VALUE "Change value" #define S_IMPULSE_COUNTER_SETTINGS_NR "Settings IC nr." -#define S_NO_IMPULSE_COUNTER_NR "No IC nr." +#define S_CONTROL "Control" +#define S_OLED_BUTTON "OLED button" +#define S_SCREEN "Screen" +#define S_BACKLIGHT_S "Backlight [s]" +#define S_ADDRESS_BMPE280 "Address BME280" + +//#### SuplaWebPageUpload.cpp #### +#define S_GENERATE_GUID_AND_KEY "Generate GUID & AUTHKEY" +#define S_UPLOAD "Upload" + +//#### SuplaWebPageControl.cpp #### +#define S_SETTINGS_FOR_BUTTONS "Settings for buttons" +#define S_REVERSE_LOGIC "Reverse logic" +#define S_INTERNAL_PULL_UP "Internal pull-up" + +//#### SuplaWebPageOther.cpp #### +#define S_CALIBRATION "Calibration" +#define S_CALIBRATION_SETTINGS "Calibration settings" +#define S_BULB_POWER_W "Bulb power [W]" +#define S_VOLTAGE_V "Voltage [V]" +#define S_DEPTH_CM "Depth [cm]" +#define S_SENSOR_READING_DISTANCE "sensor reading distance" + +//#### SuplaWebPageRelay.cpp #### +#define S_RELAY_ACTIVATION_STATUS "Relay activation status" +#define S_STATE "State" +#define S_MESSAGE "Message" +#define S_DIRECT_LINKS "Direct links" +#define S_CONDITIONING "Conditioning" +#define S_SENSOR "Sensor" +#define S_CONDITION "Condition" +#define S_SWITCH_ON_VALUE "switch-on value" +#define S_SWITCH_OFF_VALUE "switch-off value" +#define S_SETTINGS_FOR_RELAYS "Settings for relays" + +//#### SuplaHTTPUpdateServer.cpp #### +#define S_FLASH_MEMORY_SIZE "Flash Memory Size" +#define S_SKETCH_LOADED_SIZE "Sketch Loaded Size" +#define S_SKETCH_UPLOAD_MAX_SIZE "Sketch Upload Max Size" +#define S_UPDATE_FIRMWARE "Update Firmware" +#define S_UPDATE_SUCCESS_REBOOTING "Update Success! Rebooting..." +#define S_WARNING "WARNING" +#define S_ONLY_2_STEP_OTA "only use 2-step OTA update. Use" #endif // _LANGUAGE_EN_S_H_ diff --git a/src/language/es.h b/src/language/es.h index d010616a..a75c0f1a 100644 --- a/src/language/es.h +++ b/src/language/es.h @@ -1,9 +1,12 @@ #ifndef _LANGUAGE_ES_S_H_ #define _LANGUAGE_ES_S_H_ +#define S_LANG "es" + // Translated by EñE Partycja Gajek-Nowrot // Checked by elmaya +#define S_SETTING_FOR "Configuraciones para" #define S_SETTING_WIFI_SSID "Configuración de WIFI" #define S_WIFI_SSID "Nombre WIFI" #define S_WIFI_PASS "Contraseña" @@ -17,6 +20,10 @@ #define S_ROLLERSHUTTERS "Persianas" #define S_SAVE "Guardar" #define S_DEVICE_SETTINGS "Configuración de dispositivo" +#define S_TOOLS "Instrumentos" +#define S_SAVE_CONFIGURATION "Guardar configuración" +#define S_LOAD_CONFIGURATION "Cargar configuración" +#define S_RESTORE_FACTORY_SETTING "Restaurar la configuración de fábrica" #define S_UPDATE "Actualización" #define S_RESTART "Reiniciar" #define S_RETURN "Volver" @@ -42,6 +49,7 @@ #define S_BUTTON_NR_SETTINGS "Parámetros del botón no." #define S_NO_BUTTON_NR "Falta del botón no." #define S_REACTION_TO "Reacción a" +#define S_ACTION "Action" #define S_GPIO_SETTINGS_FOR_LIMIT_SWITCH "Parámetros GPIO para el sensor de apertura" #define S_LIMIT_SWITCH "INTERRUPTOR DE LÍMITE" #define S_GPIO_SETTINGS_FOR "Parámetros GPIO para" @@ -84,6 +92,7 @@ //#### SuplaCommonPROGMEM.h #### #define S_OFF "APAGADO" #define S_ON "ENCENDIDO" +#define S_TOGGLE "TOGGLE" #define S_LOW "BAJO" #define S_HIGH "ALTO" #define S_POSITION_MEMORY "RECORDAR ESTADO" @@ -92,6 +101,16 @@ #define S_REACTION_ON_CHANGE "AL CAMBIAR DE ESTADO" #define S_CFG_10_PRESSES "AL PRESIONAR 10 VECES" #define S_5SEK_HOLD "AL MANTENER PRESIONADO 5 SEGUNDOS" +#define S_NORMAL "NORMAL" +#define S_SLOW "LENTO" +#define S_MANUALLY "A MANO" +#define S_ON_CH_VAL_OFF_HEATING "ON < valor del cana l> OFF - calefacción" +#define S_ON_CH_VAL_OFF_COOLING "ON > valor del canal < OFF - refrigeración" +#define S_ON_2CH_VAL_OFF_HUMIDIFICATION "ON < valor del segundo canal > OFF - humidificación" +#define S_ON_2CH_VAL_OFF_DRYING "ON > valor del segundo canal < OFF - secado" + +//#### SuplaWebServer.cpp #### +#define S_LIMIT_SWITCHES "FINALES DE CARRERA" //#### SuplaTemplateBoard.h #### #define S_ABSENT "BRAK" @@ -103,6 +122,48 @@ #define S_IMPULSE_COUNTER_PULL_UP "Pull-up" #define S_IMPULSE_COUNTER_CHANGE_VALUE "Cambia el valor" #define S_IMPULSE_COUNTER_SETTINGS_NR "Configuración contador de impulsos no." -#define S_NO_IMPULSE_COUNTER_NR "Falta de contador de impulsos no." +#define S_CONTROL "Control" +#define S_OLED_BUTTON "Botón OLED" +#define S_SCREEN "Pantalla" +#define S_BACKLIGHT_S "Luz de fondo [s]" +#define S_ADDRESS_BMPE280 "Dirección BME280" + +//#### SuplaWebPageUpload.cpp #### +#define S_GENERATE_GUID_AND_KEY "Generar GUID & AUTHKEY" +#define S_UPLOAD "Subir" + +//#### SuplaWebPageControl.cpp #### +#define S_SETTINGS_FOR_BUTTONS "Configuración de botones" +#define S_REVERSE_LOGIC "Lógica inversa" +#define S_INTERNAL_PULL_UP "Pull-up interno" + +//#### SuplaWebPageOther.cpp #### +#define S_CALIBRATION "Calibración" +#define S_CALIBRATION_SETTINGS "Configuraciones de calibracion" +#define S_BULB_POWER_W "Potencia de la bombilla [W]" +#define S_VOLTAGE_V "Voltaje [V]" +#define S_DEPTH_CM "Profundidad [cm]" +#define S_SENSOR_READING_DISTANCE "distancia de lectura del sensor" + +//#### SuplaWebPageRelay.cpp #### +#define S_RELAY_ACTIVATION_STATUS "Estado de activación del relé" +#define S_STATE "Expresar" +#define S_MESSAGE "Mensaje" +#define S_DIRECT_LINKS "Enlaces directos" +#define S_CONDITIONING "Acondicionamiento" +#define S_SENSOR "Sensor" +#define S_CONDITION "Condición" +#define S_SWITCH_ON_VALUE "valor de encendido" +#define S_SWITCH_OFF_VALUE "valor de desconexión" +#define S_SETTINGS_FOR_RELAYS "Configuraciones para relés" + +//#### SuplaHTTPUpdateServer.cpp #### +#define S_FLASH_MEMORY_SIZE "Tamaño de la memoria flash" +#define S_SKETCH_LOADED_SIZE "Tamaño de boceto cargado" +#define S_SKETCH_UPLOAD_MAX_SIZE "Tamaño máx de carga de bocetos" +#define S_UPDATE_FIRMWARE "Actualice el firmware" +#define S_UPDATE_SUCCESS_REBOOTING "¡Actualización exitosa! Reiniciando ..." +#define S_WARNING "ADVERTENCIA" +#define S_ONLY_2_STEP_OTA "solo use la actualización OTA de 2 pasos. Usar" #endif // _LANGUAGE_ES_S_H_ diff --git a/src/language/fr.h b/src/language/fr.h index 411644a8..0beb7cd0 100644 --- a/src/language/fr.h +++ b/src/language/fr.h @@ -1,7 +1,10 @@ #ifndef _LANGUAGE_FR_S_H_ #define _LANGUAGE_FR_S_H_ +#define S_LANG "fr" + // translated by Fryga +#define S_SETTING_FOR "Paramètres pour " #define S_SETTING_WIFI_SSID "Mettre à jour du WIFI" #define S_WIFI_SSID "Nom du WIFI" #define S_WIFI_PASS "Mot de passe" @@ -15,6 +18,10 @@ #define S_ROLLERSHUTTERS "Volet roulant" #define S_SAVE "Enregistrer" #define S_DEVICE_SETTINGS "Paramètres du module" +#define S_TOOLS "Outils" +#define S_SAVE_CONFIGURATION "Enregistrer la configuration" +#define S_LOAD_CONFIGURATION "Charger la configuration" +#define S_RESTORE_FACTORY_SETTING "Rétablir les paramètres d'usine" #define S_UPDATE "Mettre à jour" #define S_RESTART "Réinitialisation" #define S_RETURN "Retour" @@ -40,6 +47,7 @@ #define S_BUTTON_NR_SETTINGS "Bouton mettre à jour" #define S_NO_BUTTON_NR "Manquant numéro de bouton" #define S_REACTION_TO "Réaction à" +#define S_ACTION "Action" #define S_GPIO_SETTINGS_FOR_LIMIT_SWITCH "Paramètres GPIO pour capteur de limite" #define S_LIMIT_SWITCH "Capteur de limite" #define S_GPIO_SETTINGS_FOR "GPIO mettre à jour pour" @@ -82,6 +90,7 @@ // #### SuplaCommonPROGMEM.h #### #define S_OFF "ÉTEINDRE" #define S_ON "ALLUMER" +#define S_TOGGLE "TOGGLE" #define S_LOW "BASSE" #define S_HIGH "HAUT" #define S_POSITION_MEMORY "MEMOIRE DE POSITION" @@ -90,6 +99,16 @@ #define S_REACTION_ON_CHANGE "SUR LE CHANGEMENT" #define S_CFG_10_PRESSES "10 FOIS SUR PRESSE" #define S_5SEK_HOLD "5 SEC TENIR" +#define S_NORMAL "ORDINAIRE" +#define S_SLOW "LENT" +#define S_MANUALLY "MANUELLEMENT" +#define S_ON_CH_VAL_OFF_HEATING "ON < valeur de canal > OFF - chauffage" +#define S_ON_CH_VAL_OFF_COOLING "ON > valeur du canal < OFF - refroidissement" +#define S_ON_2CH_VAL_OFF_HUMIDIFICATION "ON < Valeur 2ème canal > OFF - humidification" +#define S_ON_2CH_VAL_OFF_DRYING "ON > Valeur 2ème canal < OFF - séchage" + +//#### SuplaWebServer.cpp #### +#define S_LIMIT_SWITCHES "INTERRUPTEURS DE FIN DE COURSE" // #### SuplaTemplateBoard.h #### #define S_ABSENT "ABSENT" @@ -101,6 +120,48 @@ #define S_IMPULSE_COUNTER_PULL_UP "Remonter" #define S_IMPULSE_COUNTER_CHANGE_VALUE "Modifier la valeur" #define S_IMPULSE_COUNTER_SETTINGS_NR "Paramètres Compteur d'impulsions No." -#define S_NO_IMPULSE_COUNTER_NR "Aucun Compteur d'impulsions No." +#define S_CONTROL "Contrôler" +#define S_OLED_BUTTON "Bouton OLED" +#define S_SCREEN "Écran" +#define S_BACKLIGHT_S "Rétroéclairage [s]" +#define S_ADDRESS_BMPE280 "Adresse BME280" + +//#### SuplaWebPageUpload.cpp #### +#define S_GENERATE_GUID_AND_KEY "Produire GUID & AUTHKEY" +#define S_UPLOAD "Télécharger" + +//#### SuplaWebPageControl.cpp #### +#define S_SETTINGS_FOR_BUTTONS "Paramètres des boutons" +#define S_REVERSE_LOGIC "Logique inversée" +#define S_INTERNAL_PULL_UP "Pull-up interne" + +//#### SuplaWebPageOther.cpp #### +#define S_CALIBRATION "Étalonnage" +#define S_CALIBRATION_SETTINGS "Paramètres d'étalonnage" +#define S_BULB_POWER_W "Puissance de l'ampoule [W]" +#define S_VOLTAGE_V "Tension [V]" +#define S_DEPTH_CM "Profondeur [cm]" +#define S_SENSOR_READING_DISTANCE "distance de lecture du capteur" + +//#### SuplaWebPageRelay.cpp #### +#define S_RELAY_ACTIVATION_STATUS "État d'activation du relais" +#define S_STATE "État" +#define S_MESSAGE "Un message" +#define S_DIRECT_LINKS "Liens directs" +#define S_CONDITIONING "Conditionnement" +#define S_SENSOR "Capteur" +#define S_CONDITION "Condition" +#define S_SWITCH_ON_VALUE "valeur d'enclenchement" +#define S_SWITCH_OFF_VALUE "valeur de coupure" +#define S_SETTINGS_FOR_RELAYS "Paramètres des relais" + +//#### SuplaHTTPUpdateServer.cpp #### +#define S_FLASH_MEMORY_SIZE "Taille de la mémoire flash" +#define S_SKETCH_LOADED_SIZE "Taille chargée de l'esquisse" +#define S_SKETCH_UPLOAD_MAX_SIZE "Taille max. du téléchargement de l'esquisse" +#define S_UPDATE_FIRMWARE "Mise à jour du firmware" +#define S_UPDATE_SUCCESS_REBOOTING "Mise à jour réussie! Redémarrage ..." +#define S_WARNING "ATTENTION" +#define S_ONLY_2_STEP_OTA "utilisez uniquement la mise à jour OTA en 2 étapes. Utilisation" -#endif // _LANGUAGE_FR_S_H_ \ No newline at end of file +#endif // _LANGUAGE_FR_S_H_ diff --git a/src/language/pl.h b/src/language/pl.h index 46ddc93a..d9afba14 100644 --- a/src/language/pl.h +++ b/src/language/pl.h @@ -2,6 +2,9 @@ #ifndef _LANGUAGE_PL_S_H_ #define _LANGUAGE_PL_S_H_ +#define S_LANG "pl" + +#define S_SETTING_FOR "Ustawienia dla" #define S_SETTING_WIFI_SSID "Ustawienia WIFI" #define S_WIFI_SSID "Nazwa sieci" #define S_WIFI_PASS "Hasło" @@ -15,6 +18,10 @@ #define S_ROLLERSHUTTERS "Rolety" #define S_SAVE "Zapisz" #define S_DEVICE_SETTINGS "Ustawienia urządzenia" +#define S_TOOLS "Narzędzia" +#define S_SAVE_CONFIGURATION "Zapisz konfigurację" +#define S_LOAD_CONFIGURATION "Wczytaj konfigurację" +#define S_RESTORE_FACTORY_SETTING "Przywróć ustawienia fabryczne" #define S_UPDATE "Aktualizacja" #define S_RESTART "Restart" #define S_RETURN "Powrót" @@ -22,24 +29,25 @@ #define S_TYPE "Rodzaj" #define S_RELAYS "PRZEKAŹNIKI" #define S_BUTTONS "PRZYCISKI" -#define S_SENSORS_1WIRE "SENSORY 1Wire" -#define S_SENSORS_I2C "SENSORY i2c" -#define S_SENSORS_SPI "SENSORY SPI" -#define S_SENSORS_OTHER "SENSORY INNE" -#define S_LED_BUTTON_CFG "LED, BUTTON CONFIG" +#define S_SENSORS_1WIRE "1WIRE" +#define S_SENSORS_I2C "I2C" +#define S_SENSORS_SPI "SPI" +#define S_SENSORS_OTHER "INNE" +#define S_LED_BUTTON_CFG "KONFIGURACJA" #define S_CFG_MODE "Tryb" #define S_QUANTITY "ILOŚĆ" #define S_GPIO_SETTINGS_FOR_RELAYS "Ustawienie GPIO dla przekaźników" #define S_RELAY "PRZEKAŹNIK" -#define S_RELAY_NR_SETTINGS "Ustawienia przekaźnika nr." -#define S_NO_RELAY_NR "Brak przekaźnika nr." -#define S_STATE_CONTROL "Sterowanie stanem" +#define S_RELAY_NR_SETTINGS "Ustawienia przekaźnika nr" +#define S_NO_RELAY_NR "Brak przekaźnika nr" +#define S_STATE_CONTROL "Załączany stanem" #define S_REACTION_AFTER_RESET "Reakcja po resecie" #define S_GPIO_SETTINGS_FOR_BUTTONS "Ustawienie GPIO dla przycisków" #define S_BUTTON "PRZYCISK" -#define S_BUTTON_NR_SETTINGS "Ustawienia przycisku nr." -#define S_NO_BUTTON_NR "Brak przycisku nr." +#define S_BUTTON_NR_SETTINGS "Ustawienia przycisku nr" +#define S_NO_BUTTON_NR "Brak przycisku nr" #define S_REACTION_TO "Reakcja na" +#define S_ACTION "Akcja" #define S_GPIO_SETTINGS_FOR_LIMIT_SWITCH "Ustawienie GPIO dla cz. otwarcia" #define S_LIMIT_SWITCH "KRAŃCÓWKA" #define S_GPIO_SETTINGS_FOR "Ustawienie GPIO dla" @@ -59,13 +67,13 @@ #define S_WRITE_ERROR_BAD_DATA "Błąd zapisu - złe dane." //#### SuplaConfigESP.cpp #### -#define S_ALREADY_INITIATED "Już zainicjalizowane" +#define S_ALREADY_INITIATED "Już zainicjalizowane" #define S_NOT_ASSIGNED_CB "Nie przypisane CB" #define S_INVALID_GUID_OR_DEVICE_REGISTRATION_INACTIVE "Nieprawidłowy identyfikator GUID lub rejestracja urządzeń NIEAKTYWNA" #define S_UNKNOWN_SEVER_ADDRESS "Nieznany adres serwera" #define S_UNKNOWN_ID "Nieznany identyfikator ID" #define S_INITIATED "Zainicjowany" -#define S_CHANNEL_LIMIT_EXCEEDED "Przekroczono limit kanału" +#define S_CHANNEL_LIMIT_EXCEEDED "Przekroczono limit kanałów" #define S_DISCONNECTED "Rozłączony" #define S_REGISTRATION_IS_PENDING "Rejestracja w toku" #define S_VARIABLE_ERROR "Błąd zmiennej" @@ -80,16 +88,27 @@ #define S_DEVICE_LIMIT_EXCEEDED "Przekroczono limit urządzeń" //#### SuplaCommonPROGMEM.h #### -#define S_OFF "WYŁĄCZONY" -#define S_ON "ZAŁĄCZONY" -#define S_LOW "ODWRÓCONE" -#define S_HIGH "NORMALNE" +#define S_OFF "WYŁĄCZ" +#define S_ON "ZAŁĄCZ" +#define S_TOGGLE "PRZEŁĄCZ" +#define S_LOW "LOW" +#define S_HIGH "HIGH" #define S_POSITION_MEMORY "PAMIĘTAJ STAN" #define S_REACTION_ON_PRESS "WCIŚNIĘCIE" #define S_REACTION_ON_RELEASE "ZWOLNIENIE" #define S_REACTION_ON_CHANGE "ZMIANA STANU" #define S_CFG_10_PRESSES "10 WCIŚNIĘĆ" -#define S_5SEK_HOLD "5 SEK" +#define S_5SEK_HOLD "WCIŚNIĘTY 5 SEKUND" +#define S_NORMAL "NORMALNE" +#define S_SLOW "WOLNE" +#define S_MANUALLY "RĘCZNE" +#define S_ON_CH_VAL_OFF_HEATING "ON < wartość kanału > OFF - ogrzewanie" +#define S_ON_CH_VAL_OFF_COOLING "ON > wartość kanału < OFF - chłodzenie" +#define S_ON_2CH_VAL_OFF_HUMIDIFICATION "ON < 2 wartość kanału > OFF - nawilżanie" +#define S_ON_2CH_VAL_OFF_DRYING "ON > 2 wartość kanału < OFF - osuszanie" + +//#### SuplaWebServer.cpp #### +#define S_LIMIT_SWITCHES "KRAŃCÓWKI" //#### SuplaTemplateBoard.h #### #define S_ABSENT "BRAK" @@ -100,7 +119,49 @@ #define S_IMPULSE_COUNTER_RAISING_EDGE "Zbocze rosnące" #define S_IMPULSE_COUNTER_PULL_UP "Podciąganie do VCC" #define S_IMPULSE_COUNTER_CHANGE_VALUE "Zmień wartość" -#define S_IMPULSE_COUNTER_SETTINGS_NR "Ustawienia IC nr." -#define S_NO_IMPULSE_COUNTER_NR "Brak IC nr." +#define S_IMPULSE_COUNTER_SETTINGS_NR "Ustawienia IC nr" +#define S_CONTROL "Sterowanie" +#define S_OLED_BUTTON "Przycisk OLED" +#define S_SCREEN "Ekran" +#define S_BACKLIGHT_S "Podświetlenie [s]" +#define S_ADDRESS_BMPE280 "Adres BME280" + +//#### SuplaWebPageUpload.cpp #### +#define S_GENERATE_GUID_AND_KEY "Generuj GUID & AUTHKEY" +#define S_UPLOAD "Prześlij" + +//#### SuplaWebPageControl.cpp #### +#define S_SETTINGS_FOR_BUTTONS "Ustawienia dla przycisków" +#define S_REVERSE_LOGIC "Odwrócona logika" +#define S_INTERNAL_PULL_UP "Wewnętrzny pull-up" + +//#### SuplaWebPageOther.cpp #### +#define S_CALIBRATION "Kalibracja" +#define S_CALIBRATION_SETTINGS "Ustawienia kalibracji" +#define S_BULB_POWER_W "Moc żarówki [W]" +#define S_VOLTAGE_V "Napięcie [V]" +#define S_DEPTH_CM "Głębokość [cm]" +#define S_SENSOR_READING_DISTANCE "maksymalna odległość odczytu czujnika" + +//#### SuplaWebPageRelay.cpp #### +#define S_RELAY_ACTIVATION_STATUS "Status załączenia przekaźnika" +#define S_STATE "Stan" +#define S_MESSAGE "Wiadomość" +#define S_DIRECT_LINKS "Linki bezpośrednie" +#define S_CONDITIONING "Warunkowanie" +#define S_SENSOR "Czujnik" +#define S_CONDITION "Warunek" +#define S_SWITCH_ON_VALUE "wartość włączenia" +#define S_SWITCH_OFF_VALUE "wartość wyłączenia" +#define S_SETTINGS_FOR_RELAYS "Ustawienia dla przekaźników" + +//#### SuplaHTTPUpdateServer.cpp #### +#define S_FLASH_MEMORY_SIZE "Rozmiar pamięci Flash" +#define S_SKETCH_LOADED_SIZE "Rozmiar wczytanego szkicu" +#define S_SKETCH_UPLOAD_MAX_SIZE "Maks. rozmiar przesyłanego szkicu" +#define S_UPDATE_FIRMWARE "Aktualizacja oprogramowania" +#define S_UPDATE_SUCCESS_REBOOTING "Aktualizacja udana! Ponowne uruchamianie ..." +#define S_WARNING "OSTRZEŻENIE" +#define S_ONLY_2_STEP_OTA "używaj tylko dwuetapowej aktualizacji OTA. Najpierw załaduj" #endif // _LANGUAGE_PL_S_H_