diff --git a/.github/workflows/build_firmware.yml b/.github/workflows/build_firmware.yml new file mode 100644 index 0000000..323f470 --- /dev/null +++ b/.github/workflows/build_firmware.yml @@ -0,0 +1,40 @@ +name: Build Firmware + +on: + release: + types: [published] + push: + branches: + - master + +# schedule: +# - cron: 0 8 * * 5 +# watch: +# types: [started] + +jobs: + build: + runs-on: ubuntu-18.04 + + steps: + - name: Checkout + uses: actions/checkout@master + + - name: Set up Python + uses: actions/setup-python@v1 + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install platformio + + - name: Run PlatformIO + run: | + cd /home/runner/work/esp_dc1/esp_dc1 + platformio run + + - name : Upload artifact + uses: actions/upload-artifact@master + with: + name: firmware + path: /home/runner/work/esp_dc1/esp_dc1/.pio/build/dc1/firmware.bin diff --git a/.gitignore b/.gitignore index b0d27ad..f3fa2d7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ -.pio -.vscode/.browse.c_cpp.db* -.vscode/c_cpp_properties.json -.vscode/launch.json -.vscode/ipch -/firmware.map -/mklink.bat -/output +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch +/firmware.map +/mklink.bat +/output diff --git a/.travis.yml b/.travis.yml index 7c486f1..a8bbc57 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,67 +1,67 @@ -# Continuous Integration (CI) is the practice, in software -# engineering, of merging all developer working copies with a shared mainline -# several times a day < https://docs.platformio.org/page/ci/index.html > -# -# Documentation: -# -# * Travis CI Embedded Builds with PlatformIO -# < https://docs.travis-ci.com/user/integration/platformio/ > -# -# * PlatformIO integration with Travis CI -# < https://docs.platformio.org/page/ci/travis.html > -# -# * User Guide for `platformio ci` command -# < https://docs.platformio.org/page/userguide/cmd_ci.html > -# -# -# Please choose one of the following templates (proposed below) and uncomment -# it (remove "# " before each line) or use own configuration according to the -# Travis CI documentation (see above). -# - - -# -# Template #1: General project. Test it using existing `platformio.ini`. -# - -# language: python -# python: -# - "2.7" -# -# sudo: false -# cache: -# directories: -# - "~/.platformio" -# -# install: -# - pip install -U platformio -# - platformio update -# -# script: -# - platformio run - - -# -# Template #2: The project is intended to be used as a library with examples. -# - -# language: python -# python: -# - "2.7" -# -# sudo: false -# cache: -# directories: -# - "~/.platformio" -# -# env: -# - PLATFORMIO_CI_SRC=path/to/test/file.c -# - PLATFORMIO_CI_SRC=examples/file.ino -# - PLATFORMIO_CI_SRC=path/to/test/directory -# -# install: -# - pip install -U platformio -# - platformio update -# -# script: -# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N +# Continuous Integration (CI) is the practice, in software +# engineering, of merging all developer working copies with a shared mainline +# several times a day < https://docs.platformio.org/page/ci/index.html > +# +# Documentation: +# +# * Travis CI Embedded Builds with PlatformIO +# < https://docs.travis-ci.com/user/integration/platformio/ > +# +# * PlatformIO integration with Travis CI +# < https://docs.platformio.org/page/ci/travis.html > +# +# * User Guide for `platformio ci` command +# < https://docs.platformio.org/page/userguide/cmd_ci.html > +# +# +# Please choose one of the following templates (proposed below) and uncomment +# it (remove "# " before each line) or use own configuration according to the +# Travis CI documentation (see above). +# + + +# +# Template #1: General project. Test it using existing `platformio.ini`. +# + +# language: python +# python: +# - "2.7" +# +# sudo: false +# cache: +# directories: +# - "~/.platformio" +# +# install: +# - pip install -U platformio +# - platformio update +# +# script: +# - platformio run + + +# +# Template #2: The project is intended to be used as a library with examples. +# + +# language: python +# python: +# - "2.7" +# +# sudo: false +# cache: +# directories: +# - "~/.platformio" +# +# env: +# - PLATFORMIO_CI_SRC=path/to/test/file.c +# - PLATFORMIO_CI_SRC=examples/file.ino +# - PLATFORMIO_CI_SRC=path/to/test/directory +# +# install: +# - pip install -U platformio +# - platformio update +# +# script: +# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N diff --git a/.vscode/extensions.json b/.vscode/extensions.json index e80666b..0f0d740 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,7 +1,7 @@ -{ - // See http://go.microsoft.com/fwlink/?LinkId=827846 - // for the documentation about the extensions.json format - "recommendations": [ - "platformio.platformio-ide" - ] -} +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 9f0d199..1c8616f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,9 +1,9 @@ -{ - "files.associations": { - "functional": "cpp", - "cmath": "cpp", - "string": "cpp", - "*.tcc": "cpp", - "sstream": "cpp" - } +{ + "files.associations": { + "functional": "cpp", + "cmath": "cpp", + "string": "cpp", + "*.tcc": "cpp", + "sstream": "cpp" + } } \ No newline at end of file diff --git a/LICENSE b/LICENSE index d159169..89e08fb 100644 --- a/LICENSE +++ b/LICENSE @@ -1,339 +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. - - - Copyright (C) - - 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. - - , 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. + 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. + + + Copyright (C) + + 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. + + , 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/README.md b/README.md index 0ea6abf..51d23dc 100644 --- a/README.md +++ b/README.md @@ -1,101 +1,101 @@ -# ESP DC1 -**斐讯DC1智能排插个人固件.** - -## WHY -众所周知的原因,斐讯服务器已经不能正常访问,插座的APP控制已经无法正常实现,需要有另外的方式实现插座的控制。 - -已有的方法为内网劫持实现,具体可参考[这里](https://bbs.hassbian.com/thread-5637-1-1.html)。 - -这次要实现的是通过一个自定义的固件,来完整实现DC1联网控制。 - -> ### 作者声明 -> -> 注意: 本项目主要目的为作者本人自己学习及使用DC1插线板而开发,本着开源精神及造福网友而开源,仅个人开发,可能无法做到完整的测试,所以不承担他人使用本项目照成的所有后果。 -> -> **严禁他人将本项目用户用于任何商业活动。个人在非盈利情况下可以自己使用,严禁收费代刷等任何盈利服务、** -> -> 有需要请联系作者:qlwz@qq.com - - -## 特性 - -本固件使用斐讯DC1插线板硬件为基础,实现以下功能: - -- [x] 4个USB充电 -- [x] 按键控制所有插口通断 -- [x] 控制每个接口独立开关 -- [x] OTA在线升级 -- [x] WEB配置页面 -- [x] MQTT服务器连接控制 -- [x] 通过MQTT连入Home Assistant -- [x] 电压/电流/功率/视在功率/无功功率/功率因数/用电量统计(不做任何精度保证) -- [x] 过载保护 - - -## 拆机接线及烧录固件相关 - -见[固件烧录](固件烧录.md) - -烧录固件完成后,即可开始使用 - -## 总开关控制USB - -1、抖音 搜索 DC1 按照李老师的硬件改造 - -2、WEB页面 分开关联动 设置为不联动 - -## 如何配网 - -1、第一次使用自动进入配网模式 - -2、以后通过长按总开发进入配网模式 - -## 如何编译 -Visual Studio Code + PlatformIO ID 开发 [安装](https://www.jianshu.com/p/c36f8be8c87f) - -## 已支持接入的开源智能家居平台 -以下排序随机,不分优劣。合适自己的就好。 - -### 1、Home Assistant -Home Assistant 是一款基于 Python 的智能家居开源系统,支持众多品牌的智能家居设备,可以轻松实现设备的语音控制、自动化等。 -- [官方网站](https://www.home-assistant.io/) -- [国内论坛](https://bbs.hassbian.com/) - -#### 接入方法 -WEB页面开启**MQTT自动发现** - -### 2、ioBroker -ioBroker是基于nodejs的物联网的集成平台,为物联网设备提供核心服务、系统管理和统一操作方式。 -- [官方网站](http://www.iobroker.net) -- [中文资料可以参考这里](https://doc.iobroker.cn/#/_zh-cn/) -- [国内论坛](https://bbs.iobroker.cn) -#### 接入方法 -ioBroker相关接入问题可以加QQ群776817275咨询 - -### 3、其他支持mqtt的平台 -理论上来说,只要是支持MQTT的平台都可以实现接入。 - -#### 接入方法 -添加对应的topic - -# 固件截图 - -![image](file/images/tab1.png) -![image](file/images/tab2.png) -![image](file/images/tab3.png) -![image](file/images/tab4.png) - -## 致谢 -以下排名不分先后,为随机。 -- killadm:导出原始固件,提供WiFi芯片对比图,主控制板WiFi模块、U7移除后的PCB照片,U7逻辑分析数据采集 -- 老妖:U7驱动编写,U7逻辑分析 -- 实验幼儿园小二班扛把子:测试引脚走向 -- Heller、巴山耗子:初期资料整理 -- 风中的summer:提供清晰的电路板照片、拆机过程照片 - -感谢各位使用本方法的玩家,欢迎加入QQ群776817275 - -## 免责申明 -以上纯属个人爱好,因为使用上述方法造成的任何问题,不承担任何责任。 - +# ESP DC1 +**斐讯DC1智能排插个人固件.** + +## WHY +众所周知的原因,斐讯服务器已经不能正常访问,插座的APP控制已经无法正常实现,需要有另外的方式实现插座的控制。 + +已有的方法为内网劫持实现,具体可参考[这里](https://bbs.hassbian.com/thread-5637-1-1.html)。 + +这次要实现的是通过一个自定义的固件,来完整实现DC1联网控制。 + +> ### 作者声明 +> +> 注意: 本项目主要目的为作者本人自己学习及使用DC1插线板而开发,本着开源精神及造福网友而开源,仅个人开发,可能无法做到完整的测试,所以不承担他人使用本项目照成的所有后果。 +> +> **严禁他人将本项目用户用于任何商业活动。个人在非盈利情况下可以自己使用,严禁收费代刷等任何盈利服务、** +> +> 有需要请联系作者:qlwz@qq.com + + +## 特性 + +本固件使用斐讯DC1插线板硬件为基础,实现以下功能: + +- [x] 4个USB充电 +- [x] 按键控制所有插口通断 +- [x] 控制每个接口独立开关 +- [x] OTA在线升级 +- [x] WEB配置页面 +- [x] MQTT服务器连接控制 +- [x] 通过MQTT连入Home Assistant +- [x] 电压/电流/功率/视在功率/无功功率/功率因数/用电量统计(不做任何精度保证) +- [x] 过载保护 + + +## 拆机接线及烧录固件相关 + +见[固件烧录](固件烧录.md) + +烧录固件完成后,即可开始使用 + +## 总开关控制USB + +1、抖音 搜索 DC1 按照李老师的硬件改造 + +2、WEB页面 分开关联动 设置为不联动 + +## 如何配网 + +1、第一次使用自动进入配网模式 + +2、以后通过长按总开发进入配网模式 + +## 如何编译 +Visual Studio Code + PlatformIO ID 开发 [安装](https://www.jianshu.com/p/c36f8be8c87f) + +## 已支持接入的开源智能家居平台 +以下排序随机,不分优劣。合适自己的就好。 + +### 1、Home Assistant +Home Assistant 是一款基于 Python 的智能家居开源系统,支持众多品牌的智能家居设备,可以轻松实现设备的语音控制、自动化等。 +- [官方网站](https://www.home-assistant.io/) +- [国内论坛](https://bbs.hassbian.com/) + +#### 接入方法 +WEB页面开启**MQTT自动发现** + +### 2、ioBroker +ioBroker是基于nodejs的物联网的集成平台,为物联网设备提供核心服务、系统管理和统一操作方式。 +- [官方网站](http://www.iobroker.net) +- [中文资料可以参考这里](https://doc.iobroker.cn/#/_zh-cn/) +- [国内论坛](https://bbs.iobroker.cn) +#### 接入方法 +ioBroker相关接入问题可以加QQ群776817275咨询 + +### 3、其他支持mqtt的平台 +理论上来说,只要是支持MQTT的平台都可以实现接入。 + +#### 接入方法 +添加对应的topic + +# 固件截图 + +![image](file/images/tab1.png) +![image](file/images/tab2.png) +![image](file/images/tab3.png) +![image](file/images/tab4.png) + +## 致谢 +以下排名不分先后,为随机。 +- killadm:导出原始固件,提供WiFi芯片对比图,主控制板WiFi模块、U7移除后的PCB照片,U7逻辑分析数据采集 +- 老妖:U7驱动编写,U7逻辑分析 +- 实验幼儿园小二班扛把子:测试引脚走向 +- Heller、巴山耗子:初期资料整理 +- 风中的summer:提供清晰的电路板照片、拆机过程照片 + +感谢各位使用本方法的玩家,欢迎加入QQ群776817275 + +## 免责申明 +以上纯属个人爱好,因为使用上述方法造成的任何问题,不承担任何责任。 + 部分图片来源于网络,如果涉及版权,请通知删除。 \ No newline at end of file diff --git a/include/CAT9554.h b/include/CAT9554.h index 29a8b46..3eec426 100644 --- a/include/CAT9554.h +++ b/include/CAT9554.h @@ -1,65 +1,65 @@ -#ifndef _CAT9554_h -#define _CAT9554_h - -#include "Arduino.h" - -/// Modes for CAT9554 pins -enum CAT9554GPIOMode : uint8_t -{ - CAT9554_INPUT = INPUT, - CAT9554_OUTPUT = OUTPUT, -}; - -enum CAT9554Commands -{ - INPUT_REG = 0x00, - OUTPUT_REG = 0x01, - CONFIG_REG = 0x03, -}; - -#define CAT9554_ADDRESS 0x20 -#define I2C_RETRY_COUNTER 5 - -class CAT9554 -{ -protected: - bool readByte(uint8_t reg, uint8_t *data); - bool writeByte(uint8_t reg, uint16_t value); - bool readGpio(); - bool writeGpio(); - bool configGpio(); - bool readConfig(); - - /// Mask for the pin mode - 1 means output, 0 means input - uint16_t configMask; - /// The mask to write as output state - 1 means HIGH, 0 means LOW - uint16_t outputMask; - /// The state read in read_gpio_ - 1 means HIGH, 0 means LOW - uint16_t inputMask; - /// IRQ is enabled. - bool enableIrq; - /// IRQ pin. - uint8_t irqPin; - static void gpioIntr(); - -public: - CAT9554(uint8_t sda, uint8_t scl, uint16_t frequency = 20000); - - /// Need update GPIO - static bool updateGpio; - /// Check i2c availability and setup masks - void setup(); - /// Helper function to read the value of a pin. - bool digitalRead(uint8_t pin); - /// Helper function to write the value of a pin. - bool digitalWrite(uint8_t pin, bool value); - /// Helper function to set the pin mode of a pin. - void pinMode(uint8_t pin, uint8_t mode); - /// Setup irq pin. - void setIrqPin(uint8_t irq_pin) - { - enableIrq = true; - irqPin = irq_pin; - }; -}; +#ifndef _CAT9554_h +#define _CAT9554_h + +#include "Arduino.h" + +/// Modes for CAT9554 pins +enum CAT9554GPIOMode : uint8_t +{ + CAT9554_INPUT = INPUT, + CAT9554_OUTPUT = OUTPUT, +}; + +enum CAT9554Commands +{ + INPUT_REG = 0x00, + OUTPUT_REG = 0x01, + CONFIG_REG = 0x03, +}; + +#define CAT9554_ADDRESS 0x20 +#define I2C_RETRY_COUNTER 5 + +class CAT9554 +{ +protected: + bool readByte(uint8_t reg, uint8_t *data); + bool writeByte(uint8_t reg, uint16_t value); + bool readGpio(); + bool writeGpio(); + bool configGpio(); + bool readConfig(); + + /// Mask for the pin mode - 1 means output, 0 means input + uint16_t configMask; + /// The mask to write as output state - 1 means HIGH, 0 means LOW + uint16_t outputMask; + /// The state read in read_gpio_ - 1 means HIGH, 0 means LOW + uint16_t inputMask; + /// IRQ is enabled. + bool enableIrq; + /// IRQ pin. + uint8_t irqPin; + static void gpioIntr(); + +public: + CAT9554(uint8_t sda, uint8_t scl, uint16_t frequency = 20000); + + /// Need update GPIO + static bool updateGpio; + /// Check i2c availability and setup masks + void setup(); + /// Helper function to read the value of a pin. + bool digitalRead(uint8_t pin); + /// Helper function to write the value of a pin. + bool digitalWrite(uint8_t pin, bool value); + /// Helper function to set the pin mode of a pin. + void pinMode(uint8_t pin, uint8_t mode); + /// Setup irq pin. + void setIrqPin(uint8_t irq_pin) + { + enableIrq = true; + irqPin = irq_pin; + }; +}; #endif \ No newline at end of file diff --git a/include/CSE7766.h b/include/CSE7766.h index 066e216..a273bb3 100644 --- a/include/CSE7766.h +++ b/include/CSE7766.h @@ -1,84 +1,84 @@ -#ifndef _CSE7766_h -#define _CSE7766_h - -#include "Arduino.h" -#include - -#define CSE_MAX_INVALID_POWER 128 // Number of invalid power receipts before deciding active power is zero -#define CSE_NOT_CALIBRATED 0xAA -#define CSE_PULSES_NOT_INITIALIZED -1 -#define CSE_PREF 1000 -#define CSE_UREF 100 - -#define ENERGY_WATCHDOG 4 // Allow up to 4 seconds before deciding no valid data present - -const uint16_t MAX_POWER_HOLD = 10; // Time in SECONDS to allow max agreed power -const uint16_t MAX_POWER_WINDOW = 30; // Time in SECONDS to disable allow max agreed power -const uint8_t MAX_POWER_RETRY = 5; // Retry count allowing agreed power limit overflow - -typedef struct _CSE -{ - long voltage_cycle = 0; - long current_cycle = 0; - long power_cycle = 0; - long power_cycle_first = 0; - long cf_pulses = 0; - long cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED; - - uint8_t power_invalid = 0; - bool received = false; -} CSE; - -typedef struct _ENERGY -{ - float voltage = 0.0f; // 123.1 V - float current = 0.0f; // 123.123 A - float active_power = 0.0f; // 123.1 W - float apparent_power = NAN; // 123.1 VA - float reactive_power = NAN; // 123.1 VAr - float power_factor = NAN; // 0.12 - - float daily = 0; // 123.123 kWh - float total = 0; // 12345.12345 kWh total energy - - unsigned long kWhtoday_delta = 0; // 1212312345 Wh 10^-5 (deca micro Watt hours) - Overflows to Energy.kWhtoday (HLW and CSE only) - unsigned long kWhtoday; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily - - uint8_t data_valid = 0; - - uint16_t power_history[3] = {0}; - uint8_t power_steady_counter = 8; // Allow for power on stabilization - bool power_delta = false; - - uint16_t mplh_counter = 0; - uint16_t mplw_counter = 0; - uint8_t mplr_counter = 0; - uint8_t mplv_counter = 0; -} ENERGY; - -const uint32_t HLW_PREF_PULSE = 12530; // was 4975us = 201Hz = 1000W -const uint32_t HLW_UREF_PULSE = 1950; // was 1666us = 600Hz = 220V -const uint32_t HLW_IREF_PULSE = 3500; // was 1666us = 600Hz = 4.545A - -class CSE7766 -{ -protected: - SoftwareSerial *serial; - - uint8_t rawData[24]; - uint8_t rawDataIndex = 0; - - void cseReceived(); - unsigned long powerCalibration = HLW_PREF_PULSE; // 364 - unsigned long voltageCalibration = HLW_UREF_PULSE; // 368 - unsigned long currentCalibration = HLW_IREF_PULSE; // 36C - -public: - CSE7766(uint8_t rxPin, uint16_t baudrate = 4800); - CSE Cse; - ENERGY Energy; - - void loop(); - bool everySecond(); -}; +#ifndef _CSE7766_h +#define _CSE7766_h + +#include "Arduino.h" +#include + +#define CSE_MAX_INVALID_POWER 128 // Number of invalid power receipts before deciding active power is zero +#define CSE_NOT_CALIBRATED 0xAA +#define CSE_PULSES_NOT_INITIALIZED -1 +#define CSE_PREF 1000 +#define CSE_UREF 100 + +#define ENERGY_WATCHDOG 4 // Allow up to 4 seconds before deciding no valid data present + +const uint16_t MAX_POWER_HOLD = 10; // Time in SECONDS to allow max agreed power +const uint16_t MAX_POWER_WINDOW = 30; // Time in SECONDS to disable allow max agreed power +const uint8_t MAX_POWER_RETRY = 5; // Retry count allowing agreed power limit overflow + +typedef struct _CSE +{ + long voltage_cycle = 0; + long current_cycle = 0; + long power_cycle = 0; + long power_cycle_first = 0; + long cf_pulses = 0; + long cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED; + + uint8_t power_invalid = 0; + bool received = false; +} CSE; + +typedef struct _ENERGY +{ + float voltage = 0.0f; // 123.1 V + float current = 0.0f; // 123.123 A + float active_power = 0.0f; // 123.1 W + float apparent_power = NAN; // 123.1 VA + float reactive_power = NAN; // 123.1 VAr + float power_factor = NAN; // 0.12 + + float daily = 0; // 123.123 kWh + float total = 0; // 12345.12345 kWh total energy + + unsigned long kWhtoday_delta = 0; // 1212312345 Wh 10^-5 (deca micro Watt hours) - Overflows to Energy.kWhtoday (HLW and CSE only) + unsigned long kWhtoday; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily + + uint8_t data_valid = 0; + + uint16_t power_history[3] = {0}; + uint8_t power_steady_counter = 8; // Allow for power on stabilization + bool power_delta = false; + + uint16_t mplh_counter = 0; + uint16_t mplw_counter = 0; + uint8_t mplr_counter = 0; + uint8_t mplv_counter = 0; +} ENERGY; + +const uint32_t HLW_PREF_PULSE = 12530; // was 4975us = 201Hz = 1000W +const uint32_t HLW_UREF_PULSE = 1950; // was 1666us = 600Hz = 220V +const uint32_t HLW_IREF_PULSE = 3500; // was 1666us = 600Hz = 4.545A + +class CSE7766 +{ +protected: + SoftwareSerial *serial; + + uint8_t rawData[24]; + uint8_t rawDataIndex = 0; + + void cseReceived(); + unsigned long powerCalibration = HLW_PREF_PULSE; // 364 + unsigned long voltageCalibration = HLW_UREF_PULSE; // 368 + unsigned long currentCalibration = HLW_IREF_PULSE; // 36C + +public: + CSE7766(uint8_t rxPin, uint16_t baudrate = 4800); + CSE Cse; + ENERGY Energy; + + void loop(); + bool everySecond(); +}; #endif \ No newline at end of file diff --git a/include/DC1.h b/include/DC1.h index 1c5ea66..2468836 100644 --- a/include/DC1.h +++ b/include/DC1.h @@ -1,123 +1,123 @@ -// DC1.h -#ifndef _DC1_h -#define _DC1_h - -#include "DC1Config.pb.h" -#include "Module.h" -#include "CAT9554.h" -#include "CSE7766.h" - -#define MODULE_CFG_VERSION 1002 //1001 - 1500 - -#define CAT9554_SDA_PIN 3 -#define CAT9554_SCL_PIN 12 -#define CAT9554_IRQ_PIN 4 - -#define CSE7766_RX_PIN 13 -#define CSE7766_BAUDRATE 4800 - -#define LED_PIN 0 // 指示灯 -#define LOGO_LED_PIN 14 // Logo指示灯 - -#define KEY_0_PIN 16 // 总开关 -#define KEY_1_PIN 0 // 开关1 -#define KEY_2_PIN 1 // 开关2 -#define KEY_3_PIN 2 // 开关3 - -#define REL_0_PIN 7 // 总继电器 -#define REL_1_PIN 6 // 继电器1 -#define REL_2_PIN 5 // 继电器2 -#define REL_3_PIN 4 // 继电器3 - -const char HASS_DISCOVER_DC1_SWICH[] PROGMEM = - "{\"name\":\"%s_%d\"," - "\"cmd_t\":\"%s\"," - "\"stat_t\":\"%s\"," - "\"pl_off\":\"OFF\"," - "\"pl_on\":\"ON\"," - "\"avty_t\":\"%s\"," - "\"pl_avail\":\"online\"," - "\"pl_not_avail\":\"offline\"}"; - -const char HASS_DISCOVER_DC1_SENSOR[] PROGMEM = - "{\"name\":\"%s_%s\"," - "\"stat_t\":\"%s\"," - "\"val_tpl\":\"{{value_json.%s}}\"," - "\"unit_of_meas\":\"%s\"}"; - -const char HASS_DISCOVER_DC1_SENSOR_WITHOUT_UNIT[] PROGMEM = - "{\"name\":\"%s_%s\"," - "\"stat_t\":\"%s\"," - "\"val_tpl\":\"{{value_json.%s}}\"}"; - -class DC1 : public Module -{ -private: - uint8_t operationFlag = 0; // 0每秒CSE7766, 1有保存操作 - - CAT9554 *cat9554; - CSE7766 *cse7766; - char kWhtotalTime[20]; - void energyInit(); - void energyClear(); - void energySync(); - void energyUpdate(); - void energyUpdateToday(); - void energyMarginCheck(); - void energyMaxPower(); - void energyShow(bool isMqtt); - - String powerTopic; - - uint8_t btnGPIO[4] = {KEY_0_PIN, KEY_1_PIN, KEY_2_PIN, KEY_3_PIN}; - uint8_t relGPIO[4] = {REL_0_PIN, REL_1_PIN, REL_2_PIN, REL_3_PIN}; - - // 按键 - uint8_t buttonDebounceTime = 50; - uint16_t buttonLongPressTime = 2000; // 2000 = 2s - uint8_t buttonTiming = 0; - unsigned long buttonTimingStart[4] = {0, 0, 0, 0}; - uint8_t buttonAction[4] = {0, 0, 0, 0}; // 0 = 没有要执行的动作, 1 = 执行短按动作, 2 = 执行长按动作 - void checkButton(uint8_t ch); - - void httpDo(ESP8266WebServer *server); - void httpSetting(ESP8266WebServer *server); - void httpHa(ESP8266WebServer *server); - - void logoLed(); - - void reportPower(); - void reportEnergy(); - -public: - DC1ConfigMessage config; - uint8_t lastState = 0; - uint8_t lastState2 = 0; - uint8_t channels = 0; - - void init(); - String getModuleName() { return F("dc1"); } - String getModuleCNName() { return F("DC1插线板"); } - String getModuleVersion() { return F("2020.02.19.1500"); } - String getModuleAuthor() { return F("情留メ蚊子"); } - bool moduleLed(); - - void loop(); - void perSecondDo(); - - void readConfig(); - void resetConfig(); - void saveConfig(bool isEverySecond); - - void mqttCallback(String topicStr, String str); - void mqttConnected(); - void mqttDiscovery(bool isEnable = true); - - void httpAdd(ESP8266WebServer *server); - void httpHtml(ESP8266WebServer *server); - String httpGetStatus(ESP8266WebServer *server); - - void switchRelay(uint8_t ch, bool isOn, bool isSave = true); -}; - +// DC1.h +#ifndef _DC1_h +#define _DC1_h + +#include "DC1Config.pb.h" +#include "Module.h" +#include "CAT9554.h" +#include "CSE7766.h" + +#define MODULE_CFG_VERSION 1002 //1001 - 1500 + +#define CAT9554_SDA_PIN 3 +#define CAT9554_SCL_PIN 12 +#define CAT9554_IRQ_PIN 4 + +#define CSE7766_RX_PIN 13 +#define CSE7766_BAUDRATE 4800 + +#define LED_PIN 0 // 指示灯 +#define LOGO_LED_PIN 14 // Logo指示灯 + +#define KEY_0_PIN 16 // 总开关 +#define KEY_1_PIN 0 // 开关1 +#define KEY_2_PIN 1 // 开关2 +#define KEY_3_PIN 2 // 开关3 + +#define REL_0_PIN 7 // 总继电器 +#define REL_1_PIN 6 // 继电器1 +#define REL_2_PIN 5 // 继电器2 +#define REL_3_PIN 4 // 继电器3 + +const char HASS_DISCOVER_DC1_SWICH[] PROGMEM = + "{\"name\":\"%s_%d\"," + "\"cmd_t\":\"%s\"," + "\"stat_t\":\"%s\"," + "\"pl_off\":\"OFF\"," + "\"pl_on\":\"ON\"," + "\"avty_t\":\"%s\"," + "\"pl_avail\":\"online\"," + "\"pl_not_avail\":\"offline\"}"; + +const char HASS_DISCOVER_DC1_SENSOR[] PROGMEM = + "{\"name\":\"%s_%s\"," + "\"stat_t\":\"%s\"," + "\"val_tpl\":\"{{value_json.%s}}\"," + "\"unit_of_meas\":\"%s\"}"; + +const char HASS_DISCOVER_DC1_SENSOR_WITHOUT_UNIT[] PROGMEM = + "{\"name\":\"%s_%s\"," + "\"stat_t\":\"%s\"," + "\"val_tpl\":\"{{value_json.%s}}\"}"; + +class DC1 : public Module +{ +private: + uint8_t operationFlag = 0; // 0每秒CSE7766, 1有保存操作 + + CAT9554 *cat9554; + CSE7766 *cse7766; + char kWhtotalTime[20]; + void energyInit(); + void energyClear(); + void energySync(); + void energyUpdate(); + void energyUpdateToday(); + void energyMarginCheck(); + void energyMaxPower(); + void energyShow(bool isMqtt); + + String powerTopic; + + uint8_t btnGPIO[4] = {KEY_0_PIN, KEY_1_PIN, KEY_2_PIN, KEY_3_PIN}; + uint8_t relGPIO[4] = {REL_0_PIN, REL_1_PIN, REL_2_PIN, REL_3_PIN}; + + // 按键 + uint8_t buttonDebounceTime = 50; + uint16_t buttonLongPressTime = 2000; // 2000 = 2s + uint8_t buttonTiming = 0; + unsigned long buttonTimingStart[4] = {0, 0, 0, 0}; + uint8_t buttonAction[4] = {0, 0, 0, 0}; // 0 = 没有要执行的动作, 1 = 执行短按动作, 2 = 执行长按动作 + void checkButton(uint8_t ch); + + void httpDo(ESP8266WebServer *server); + void httpSetting(ESP8266WebServer *server); + void httpHa(ESP8266WebServer *server); + + void logoLed(); + + void reportPower(); + void reportEnergy(); + +public: + DC1ConfigMessage config; + uint8_t lastState = 0; + uint8_t lastState2 = 0; + uint8_t channels = 0; + + void init(); + String getModuleName() { return F("dc1"); } + String getModuleCNName() { return F("DC1插线板"); } + String getModuleVersion() { return F("2020.02.19.1500"); } + String getModuleAuthor() { return F("情留メ蚊子"); } + bool moduleLed(); + + void loop(); + void perSecondDo(); + + void readConfig(); + void resetConfig(); + void saveConfig(bool isEverySecond); + + void mqttCallback(String topicStr, String str); + void mqttConnected(); + void mqttDiscovery(bool isEnable = true); + + void httpAdd(ESP8266WebServer *server); + void httpHtml(ESP8266WebServer *server); + String httpGetStatus(ESP8266WebServer *server); + + void switchRelay(uint8_t ch, bool isOn, bool isSave = true); +}; + #endif \ No newline at end of file diff --git a/include/DC1Config.pb.h b/include/DC1Config.pb.h index 5bbc1dd..14e51c3 100644 --- a/include/DC1Config.pb.h +++ b/include/DC1Config.pb.h @@ -1,58 +1,58 @@ -/* Automatically generated nanopb header */ -/* Generated by nanopb-0.3.9.4 at Wed Feb 19 10:49:10 2020. */ - -#ifndef PB_DC1CONFIG_PB_H_INCLUDED -#define PB_DC1CONFIG_PB_H_INCLUDED -#include - -/* @@protoc_insertion_point(includes) */ -#if PB_PROTO_HEADER_VERSION != 30 -#error Regenerate this file with the current version of nanopb generator. -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* Struct definitions */ -typedef struct _DC1ConfigMessage { - uint8_t last_state; - uint8_t power_on_state; - uint8_t power_mode; - uint8_t logo_led; - uint8_t wifi_led; - uint8_t sub_kinkage; - uint16_t report_interval; - uint16_t energy_power_delta; - uint64_t energy_power_calibration; - uint64_t energy_voltage_calibration; - uint64_t energy_current_calibration; - uint64_t energy_kWhtoday; - uint64_t energy_kWhyesterday; - uint64_t energy_kWhtotal; - uint16_t energy_kWhdoy; - uint32_t energy_kWhtotal_time; - uint16_t energy_max_power; -/* @@protoc_insertion_point(struct:DC1ConfigMessage) */ -} DC1ConfigMessage; - -/* Struct field encoding specification for nanopb */ -extern const pb_field_t DC1ConfigMessage_fields[18]; - -/* Maximum encoded size of messages (where known) */ -#define DC1ConfigMessage_size 113 - -/* Message IDs (where set with "msgid" option) */ -#ifdef PB_MSGID - -#define DC1CONFIG_MESSAGES \ - - -#endif - -#ifdef __cplusplus -} /* extern "C" */ -#endif -/* @@protoc_insertion_point(eof) */ - -#endif +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.9.4 at Wed Feb 19 10:49:10 2020. */ + +#ifndef PB_DC1CONFIG_PB_H_INCLUDED +#define PB_DC1CONFIG_PB_H_INCLUDED +#include + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct definitions */ +typedef struct _DC1ConfigMessage { + uint8_t last_state; + uint8_t power_on_state; + uint8_t power_mode; + uint8_t logo_led; + uint8_t wifi_led; + uint8_t sub_kinkage; + uint16_t report_interval; + uint16_t energy_power_delta; + uint64_t energy_power_calibration; + uint64_t energy_voltage_calibration; + uint64_t energy_current_calibration; + uint64_t energy_kWhtoday; + uint64_t energy_kWhyesterday; + uint64_t energy_kWhtotal; + uint16_t energy_kWhdoy; + uint32_t energy_kWhtotal_time; + uint16_t energy_max_power; +/* @@protoc_insertion_point(struct:DC1ConfigMessage) */ +} DC1ConfigMessage; + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t DC1ConfigMessage_fields[18]; + +/* Maximum encoded size of messages (where known) */ +#define DC1ConfigMessage_size 113 + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define DC1CONFIG_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/include/README b/include/README index 194dcd4..45496b1 100644 --- a/include/README +++ b/include/README @@ -1,39 +1,39 @@ - -This directory is intended for project header files. - -A header file is a file containing C declarations and macro definitions -to be shared between several project source files. You request the use of a -header file in your project source file (C, C++, etc) located in `src` folder -by including it, with the C preprocessing directive `#include'. - -```src/main.c - -#include "header.h" - -int main (void) -{ - ... -} -``` - -Including a header file produces the same results as copying the header file -into each source file that needs it. Such copying would be time-consuming -and error-prone. With a header file, the related declarations appear -in only one place. If they need to be changed, they can be changed in one -place, and programs that include the header file will automatically use the -new version when next recompiled. The header file eliminates the labor of -finding and changing all the copies as well as the risk that a failure to -find one copy will result in inconsistencies within a program. - -In C, the usual convention is to give header files names that end with `.h'. -It is most portable to use only letters, digits, dashes, and underscores in -header file names, and at most one dot. - -Read more about using header files in official GCC documentation: - -* Include Syntax -* Include Operation -* Once-Only Headers -* Computed Includes - -https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/lib/README b/lib/README index 6debab1..8c9c29c 100644 --- a/lib/README +++ b/lib/README @@ -1,46 +1,46 @@ - -This directory is intended for project specific (private) libraries. -PlatformIO will compile them to static libraries and link into executable file. - -The source code of each library should be placed in a an own separate directory -("lib/your_library_name/[here are source files]"). - -For example, see a structure of the following two libraries `Foo` and `Bar`: - -|--lib -| | -| |--Bar -| | |--docs -| | |--examples -| | |--src -| | |- Bar.c -| | |- Bar.h -| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html -| | -| |--Foo -| | |- Foo.c -| | |- Foo.h -| | -| |- README --> THIS FILE -| -|- platformio.ini -|--src - |- main.c - -and a contents of `src/main.c`: -``` -#include -#include - -int main (void) -{ - ... -} - -``` - -PlatformIO Library Dependency Finder will find automatically dependent -libraries scanning project source files. - -More information about PlatformIO Library Dependency Finder -- https://docs.platformio.org/page/librarymanager/ldf.html + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/lib/esp_framework/include/Config.h b/lib/esp_framework/include/Config.h index d7492d6..ee8496a 100644 --- a/lib/esp_framework/include/Config.h +++ b/lib/esp_framework/include/Config.h @@ -1,60 +1,60 @@ -// Config.h - -#ifndef _CONFIG_h -#define _CONFIG_h - -#include -#include -#include -#include -#include "Arduino.h" -#include "GlobalConfig.pb.h" - -#if PB_PROTO_HEADER_VERSION != 30 -#error Regenerate this file with the current version of nanopb generator. -#endif - -#define GLOBAL_CFG_VERSION 1 // 1 - 999 - -//#define WIFI_SSID "qlwz" // WiFi ssid -//#define WIFI_PASS "" // WiFi 密码 - -//#define MQTT_SERVER "10.0.0.25" // MQTTַ 地址 -//#define MQTT_PORT 1883 // MQTT 端口 -//#define MQTT_USER "mqtt" // MQTT 用户名 -//#define MQTT_PASS "" // MQTT 密码 - -#define MQTT_FULLTOPIC "%module%/%hostname%/%prefix%/" // MQTT 主题格式 - -#define OTA_URL "http://10.0.0.50/esp/%module%.bin" - -#define WEB_LOG_SIZE 4000 // Max number of characters in weblog -#define BOOT_LOOP_OFFSET 5 // 开始恢复默认值之前的引导循环数 (0 = disable, 1..200 = 循环次数) - -extern char UID[16]; -extern char tmpData[512]; -extern GlobalConfigMessage globalConfig; -extern uint32_t perSecond; -extern Ticker *tickerPerSecond; - -class Config -{ -private: - static uint16_t nowCrc; - static bool isDelay; - static uint8_t countdown; - -public: - static uint16_t crc16(uint8_t *ptr, uint16_t len); - - static void readConfig(); - static void resetConfig(); - static bool saveConfig(bool isEverySecond = false); - static void delaySaveConfig(uint8_t second); - - static void moduleReadConfig(uint16_t version, uint16_t size, const pb_field_t fields[], void *dest_struct); - static bool moduleSaveConfig(uint16_t version, uint16_t size, const pb_field_t fields[], const void *src_struct); - - static void perSecondDo(); -}; -#endif +// Config.h + +#ifndef _CONFIG_h +#define _CONFIG_h + +#include +#include +#include +#include +#include "Arduino.h" +#include "GlobalConfig.pb.h" + +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#define GLOBAL_CFG_VERSION 1 // 1 - 999 + +//#define WIFI_SSID "qlwz" // WiFi ssid +//#define WIFI_PASS "" // WiFi 密码 + +//#define MQTT_SERVER "10.0.0.25" // MQTTַ 地址 +//#define MQTT_PORT 1883 // MQTT 端口 +//#define MQTT_USER "mqtt" // MQTT 用户名 +//#define MQTT_PASS "" // MQTT 密码 + +#define MQTT_FULLTOPIC "%module%/%hostname%/%prefix%/" // MQTT 主题格式 + +#define OTA_URL "http://10.0.0.50/esp/%module%.bin" + +#define WEB_LOG_SIZE 4000 // Max number of characters in weblog +#define BOOT_LOOP_OFFSET 5 // 开始恢复默认值之前的引导循环数 (0 = disable, 1..200 = 循环次数) + +extern char UID[16]; +extern char tmpData[512]; +extern GlobalConfigMessage globalConfig; +extern uint32_t perSecond; +extern Ticker *tickerPerSecond; + +class Config +{ +private: + static uint16_t nowCrc; + static bool isDelay; + static uint8_t countdown; + +public: + static uint16_t crc16(uint8_t *ptr, uint16_t len); + + static void readConfig(); + static void resetConfig(); + static bool saveConfig(bool isEverySecond = false); + static void delaySaveConfig(uint8_t second); + + static void moduleReadConfig(uint16_t version, uint16_t size, const pb_field_t fields[], void *dest_struct); + static bool moduleSaveConfig(uint16_t version, uint16_t size, const pb_field_t fields[], const void *src_struct); + + static void perSecondDo(); +}; +#endif diff --git a/lib/esp_framework/include/Debug.h b/lib/esp_framework/include/Debug.h index 6a54187..e1e9f84 100644 --- a/lib/esp_framework/include/Debug.h +++ b/lib/esp_framework/include/Debug.h @@ -1,39 +1,39 @@ -// Debug.h - -#ifndef _DEBUG_h -#define _DEBUG_h - -#include -#include "Config.h" - -enum LoggingLevels -{ - LOG_LEVEL_NONE, - LOG_LEVEL_ERROR, - LOG_LEVEL_INFO, - LOG_LEVEL_DEBUG, - LOG_LEVEL_DEBUG_MORE, - LOG_LEVEL_ALL -}; - -class Debug -{ -protected: - static size_t strchrspn(const char *str1, int character); - -public: - static uint8_t webLogIndex; - static char webLog[WEB_LOG_SIZE]; - static void GetLog(uint8_t idx, char **entry_pp, uint16_t *len_p); - - static IPAddress ip; - static void Syslog(); - static void AddLog(uint8_t loglevel); - static void AddLog(uint8_t loglevel, PGM_P formatP, ...); - - static void AddInfo(PGM_P formatP, ...); - static void AddDebug(PGM_P formatP, ...); - static void AddError(PGM_P formatP, ...); -}; - -#endif +// Debug.h + +#ifndef _DEBUG_h +#define _DEBUG_h + +#include +#include "Config.h" + +enum LoggingLevels +{ + LOG_LEVEL_NONE, + LOG_LEVEL_ERROR, + LOG_LEVEL_INFO, + LOG_LEVEL_DEBUG, + LOG_LEVEL_DEBUG_MORE, + LOG_LEVEL_ALL +}; + +class Debug +{ +protected: + static size_t strchrspn(const char *str1, int character); + +public: + static uint8_t webLogIndex; + static char webLog[WEB_LOG_SIZE]; + static void GetLog(uint8_t idx, char **entry_pp, uint16_t *len_p); + + static IPAddress ip; + static void Syslog(); + static void AddLog(uint8_t loglevel); + static void AddLog(uint8_t loglevel, PGM_P formatP, ...); + + static void AddInfo(PGM_P formatP, ...); + static void AddDebug(PGM_P formatP, ...); + static void AddError(PGM_P formatP, ...); +}; + +#endif diff --git a/lib/esp_framework/include/Framework.h b/lib/esp_framework/include/Framework.h index 1b72967..2b2c8f2 100644 --- a/lib/esp_framework/include/Framework.h +++ b/lib/esp_framework/include/Framework.h @@ -1,22 +1,22 @@ - -// Framework.h - -#ifndef _FRAMEWORK_h -#define _FRAMEWORK_h - -#include "Arduino.h" - -class Framework -{ - static uint16_t rebootCount; - static void callback(char *topic, byte *payload, unsigned int length); - static void connectedCallback(); - static void tickerPerSecondDo(); - -public: - static void one(unsigned long baud); - static void setup(); - static void loop(); -}; - + +// Framework.h + +#ifndef _FRAMEWORK_h +#define _FRAMEWORK_h + +#include "Arduino.h" + +class Framework +{ + static uint16_t rebootCount; + static void callback(char *topic, byte *payload, unsigned int length); + static void connectedCallback(); + static void tickerPerSecondDo(); + +public: + static void one(unsigned long baud); + static void setup(); + static void loop(); +}; + #endif \ No newline at end of file diff --git a/lib/esp_framework/include/GlobalConfig.pb.h b/lib/esp_framework/include/GlobalConfig.pb.h index 70a2524..fb7ef4e 100644 --- a/lib/esp_framework/include/GlobalConfig.pb.h +++ b/lib/esp_framework/include/GlobalConfig.pb.h @@ -1,94 +1,94 @@ -/* Automatically generated nanopb header */ -/* Generated by nanopb-0.3.9.4 at Sat Feb 15 11:23:30 2020. */ - -#ifndef PB_GLOBALCONFIG_PB_H_INCLUDED -#define PB_GLOBALCONFIG_PB_H_INCLUDED -#include - -/* @@protoc_insertion_point(includes) */ -#if PB_PROTO_HEADER_VERSION != 30 -#error Regenerate this file with the current version of nanopb generator. -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* Struct definitions */ -typedef struct _DebugConfigMessage { - uint8_t type; - char server[40]; - uint16_t port; -/* @@protoc_insertion_point(struct:DebugConfigMessage) */ -} DebugConfigMessage; - -typedef struct _HttpConfigMessage { - uint16_t port; - char user[15]; - char pass[15]; - char ota_url[150]; -/* @@protoc_insertion_point(struct:HttpConfigMessage) */ -} HttpConfigMessage; - -typedef struct _MqttConfigMessage { - char server[30]; - uint16_t port; - char user[20]; - char pass[30]; - bool retain; - char topic[50]; - bool discovery; - char discovery_prefix[30]; - uint16_t interval; -/* @@protoc_insertion_point(struct:MqttConfigMessage) */ -} MqttConfigMessage; - -typedef struct _WifiConfigMessage { - char ssid[20]; - char pass[30]; - bool is_static; - char ip[15]; - char sn[15]; - char gw[15]; - char ntp[40]; -/* @@protoc_insertion_point(struct:WifiConfigMessage) */ -} WifiConfigMessage; - -typedef PB_BYTES_ARRAY_T(500) GlobalConfigMessage_module_cfg_t; -typedef struct _GlobalConfigMessage { - WifiConfigMessage wifi; - HttpConfigMessage http; - MqttConfigMessage mqtt; - DebugConfigMessage debug; - uint16_t cfg_version; - uint16_t module_crc; - GlobalConfigMessage_module_cfg_t module_cfg; - char uid[20]; -/* @@protoc_insertion_point(struct:GlobalConfigMessage) */ -} GlobalConfigMessage; - - -/* Struct field encoding specification for nanopb */ -extern const pb_field_t GlobalConfigMessage_fields[9]; -extern const pb_field_t WifiConfigMessage_fields[8]; -extern const pb_field_t HttpConfigMessage_fields[5]; -extern const pb_field_t MqttConfigMessage_fields[10]; -extern const pb_field_t DebugConfigMessage_fields[4]; - -/* Maximum encoded size of messages (where known) */ -#define GlobalConfigMessage_size 1130 - -/* Message IDs (where set with "msgid" option) */ -#ifdef PB_MSGID - -#define GLOBALCONFIG_MESSAGES \ - - -#endif - -#ifdef __cplusplus -} /* extern "C" */ -#endif -/* @@protoc_insertion_point(eof) */ - -#endif +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.9.4 at Sat Feb 15 11:23:30 2020. */ + +#ifndef PB_GLOBALCONFIG_PB_H_INCLUDED +#define PB_GLOBALCONFIG_PB_H_INCLUDED +#include + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct definitions */ +typedef struct _DebugConfigMessage { + uint8_t type; + char server[40]; + uint16_t port; +/* @@protoc_insertion_point(struct:DebugConfigMessage) */ +} DebugConfigMessage; + +typedef struct _HttpConfigMessage { + uint16_t port; + char user[15]; + char pass[15]; + char ota_url[150]; +/* @@protoc_insertion_point(struct:HttpConfigMessage) */ +} HttpConfigMessage; + +typedef struct _MqttConfigMessage { + char server[30]; + uint16_t port; + char user[20]; + char pass[30]; + bool retain; + char topic[50]; + bool discovery; + char discovery_prefix[30]; + uint16_t interval; +/* @@protoc_insertion_point(struct:MqttConfigMessage) */ +} MqttConfigMessage; + +typedef struct _WifiConfigMessage { + char ssid[20]; + char pass[30]; + bool is_static; + char ip[15]; + char sn[15]; + char gw[15]; + char ntp[40]; +/* @@protoc_insertion_point(struct:WifiConfigMessage) */ +} WifiConfigMessage; + +typedef PB_BYTES_ARRAY_T(500) GlobalConfigMessage_module_cfg_t; +typedef struct _GlobalConfigMessage { + WifiConfigMessage wifi; + HttpConfigMessage http; + MqttConfigMessage mqtt; + DebugConfigMessage debug; + uint16_t cfg_version; + uint16_t module_crc; + GlobalConfigMessage_module_cfg_t module_cfg; + char uid[20]; +/* @@protoc_insertion_point(struct:GlobalConfigMessage) */ +} GlobalConfigMessage; + + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t GlobalConfigMessage_fields[9]; +extern const pb_field_t WifiConfigMessage_fields[8]; +extern const pb_field_t HttpConfigMessage_fields[5]; +extern const pb_field_t MqttConfigMessage_fields[10]; +extern const pb_field_t DebugConfigMessage_fields[4]; + +/* Maximum encoded size of messages (where known) */ +#define GlobalConfigMessage_size 1130 + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define GLOBALCONFIG_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/lib/esp_framework/include/Http.h b/lib/esp_framework/include/Http.h index c5a54b7..f8f654a 100644 --- a/lib/esp_framework/include/Http.h +++ b/lib/esp_framework/include/Http.h @@ -1,40 +1,40 @@ -// Http.h - -#ifndef _HTTP_h -#define _HTTP_h - -#include "Arduino.h" -#include -#include - -class Http -{ -private: - static bool isBegin; - static void handleRoot(); - static void handleMqtt(); - static void handledhcp(); - static void handleScanWifi(); - static void handleWifi(); - static void handleDiscovery(); - static void handleRestart(); - static void handleReset(); - static void handleNotFound(); - static void handleModuleSetting(); - static void handleOTA(); - static void handleGetStatus(); - static void handleUpdate(); - static bool checkAuth(); - static String updaterError; - -public: - static ESP8266WebServer *server; - static void begin(); - static void stop(); - static void loop(); - static bool captivePortal(); - - static void OTA(String url); -}; - -#endif +// Http.h + +#ifndef _HTTP_h +#define _HTTP_h + +#include "Arduino.h" +#include +#include + +class Http +{ +private: + static bool isBegin; + static void handleRoot(); + static void handleMqtt(); + static void handledhcp(); + static void handleScanWifi(); + static void handleWifi(); + static void handleDiscovery(); + static void handleRestart(); + static void handleReset(); + static void handleNotFound(); + static void handleModuleSetting(); + static void handleOTA(); + static void handleGetStatus(); + static void handleUpdate(); + static bool checkAuth(); + static String updaterError; + +public: + static ESP8266WebServer *server; + static void begin(); + static void stop(); + static void loop(); + static bool captivePortal(); + + static void OTA(String url); +}; + +#endif diff --git a/lib/esp_framework/include/Led.h b/lib/esp_framework/include/Led.h index 1e19158..fc2e49a 100644 --- a/lib/esp_framework/include/Led.h +++ b/lib/esp_framework/include/Led.h @@ -1,30 +1,30 @@ -// Led.h - -#ifndef _LED_h -#define _LED_h - -#include "Arduino.h" -#include - -class Led -{ -protected: - static uint8_t io; - static uint8_t light; - static Ticker *ledTicker; - static Ticker *ledTicker2; - static uint8_t ledType; - static bool isOn; - -public: - static void init(uint8_t _io, uint8_t _light); - static void loop(); - static void led(int ms = 200); - static void blinkLED(int duration, int n); - - static void on(); - static void off(); - static void toggle(); -}; - -#endif +// Led.h + +#ifndef _LED_h +#define _LED_h + +#include "Arduino.h" +#include + +class Led +{ +protected: + static uint8_t io; + static uint8_t light; + static Ticker *ledTicker; + static Ticker *ledTicker2; + static uint8_t ledType; + static bool isOn; + +public: + static void init(uint8_t _io, uint8_t _light); + static void loop(); + static void led(int ms = 200); + static void blinkLED(int duration, int n); + + static void on(); + static void off(); + static void toggle(); +}; + +#endif diff --git a/lib/esp_framework/include/Module.h b/lib/esp_framework/include/Module.h index 9a7492d..a05d9e7 100644 --- a/lib/esp_framework/include/Module.h +++ b/lib/esp_framework/include/Module.h @@ -1,42 +1,42 @@ -// Module.h - -#ifndef _MODULE_h -#define _MODULE_h - -#include -#include "Config.h" -#include "Led.h" -#include "Wifi.h" -#include "Mqtt.h" -#include "Debug.h" -#include "Util.h" - -class Module -{ -public: - virtual void init(); - virtual String getModuleName(); - virtual String getModuleCNName(); - virtual String getModuleVersion(); - virtual String getModuleAuthor(); - - virtual bool moduleLed(); - - virtual void loop(); - virtual void perSecondDo(); - - virtual void readConfig(); - virtual void resetConfig(); - virtual void saveConfig(bool isEverySecond); - - virtual void httpAdd(ESP8266WebServer *server); - virtual void httpHtml(ESP8266WebServer *server); - virtual String httpGetStatus(ESP8266WebServer *server); - - virtual void mqttCallback(String topicStr, String str); - virtual void mqttConnected(); - virtual void mqttDiscovery(bool isEnable = true); -}; - -extern Module *module; -#endif +// Module.h + +#ifndef _MODULE_h +#define _MODULE_h + +#include +#include "Config.h" +#include "Led.h" +#include "Wifi.h" +#include "Mqtt.h" +#include "Debug.h" +#include "Util.h" + +class Module +{ +public: + virtual void init(); + virtual String getModuleName(); + virtual String getModuleCNName(); + virtual String getModuleVersion(); + virtual String getModuleAuthor(); + + virtual bool moduleLed(); + + virtual void loop(); + virtual void perSecondDo(); + + virtual void readConfig(); + virtual void resetConfig(); + virtual void saveConfig(bool isEverySecond); + + virtual void httpAdd(ESP8266WebServer *server); + virtual void httpHtml(ESP8266WebServer *server); + virtual String httpGetStatus(ESP8266WebServer *server); + + virtual void mqttCallback(String topicStr, String str); + virtual void mqttConnected(); + virtual void mqttDiscovery(bool isEnable = true); +}; + +extern Module *module; +#endif diff --git a/lib/esp_framework/include/Mqtt.h b/lib/esp_framework/include/Mqtt.h index be0c606..891dc9d 100644 --- a/lib/esp_framework/include/Mqtt.h +++ b/lib/esp_framework/include/Mqtt.h @@ -1,59 +1,59 @@ -// Mqtt.h - -#ifndef _MQTT_h -#define _MQTT_h - -#define MQTT_SOCKET_TIMEOUT 5 -#define MQTT_MAX_PACKET_SIZE 768 - -#include -#include "Arduino.h" - -#define MQTT_CONNECTED_CALLBACK_SIGNATURE std::function connectedcallback - -class Mqtt -{ -protected: - static String getTopic(uint8_t prefix, String subtopic); - static String topicCmnd; - static String topicStat; - static String topicTele; - static uint8_t operationFlag; - static void doReportHeartbeat(); - -public: - static PubSubClient mqttClient; - static MQTT_CONNECTED_CALLBACK_SIGNATURE; - - static uint32_t lastReconnectAttempt; // 最后尝试重连时间 - static uint32_t kMqttReconnectTime; // 重新连接尝试之间的延迟(ms) - - static bool mqttConnect(); - static void availability(); - static void loop(); - static void mqttSetLoopCallback(MQTT_CALLBACK_SIGNATURE); - static void mqttSetConnectedCallback(MQTT_CONNECTED_CALLBACK_SIGNATURE); - - static void setTopic(); - static String getCmndTopic(String topic); - static String getStatTopic(String topic); - static String getTeleTopic(String topic); - - static PubSubClient &setClient(Client &client); - static bool publish(String topic, const char *payload); - static bool publish(String topic, const char *payload, bool retained); - - static bool publish(const char *topic, const char *payload); - static bool publish(const char *topic, const char *payload, bool retained); - static bool publish(const char *topic, const uint8_t *payload, unsigned int plength); - static bool publish(const char *topic, const uint8_t *payload, unsigned int plength, bool retained); - static bool publish_P(const char *topic, const char *payload, bool retained); - static bool publish_P(const char *topic, const uint8_t *payload, unsigned int plength, bool retained); - - static bool subscribe(String topic); - static bool subscribe(String topic, uint8_t qos); - static bool unsubscribe(String topic); - static void perSecondDo(); -}; - -#endif +// Mqtt.h + +#ifndef _MQTT_h +#define _MQTT_h + +#define MQTT_SOCKET_TIMEOUT 5 +#define MQTT_MAX_PACKET_SIZE 768 + +#include +#include "Arduino.h" + +#define MQTT_CONNECTED_CALLBACK_SIGNATURE std::function connectedcallback + +class Mqtt +{ +protected: + static String getTopic(uint8_t prefix, String subtopic); + static String topicCmnd; + static String topicStat; + static String topicTele; + static uint8_t operationFlag; + static void doReportHeartbeat(); + +public: + static PubSubClient mqttClient; + static MQTT_CONNECTED_CALLBACK_SIGNATURE; + + static uint32_t lastReconnectAttempt; // 最后尝试重连时间 + static uint32_t kMqttReconnectTime; // 重新连接尝试之间的延迟(ms) + + static bool mqttConnect(); + static void availability(); + static void loop(); + static void mqttSetLoopCallback(MQTT_CALLBACK_SIGNATURE); + static void mqttSetConnectedCallback(MQTT_CONNECTED_CALLBACK_SIGNATURE); + + static void setTopic(); + static String getCmndTopic(String topic); + static String getStatTopic(String topic); + static String getTeleTopic(String topic); + + static PubSubClient &setClient(Client &client); + static bool publish(String topic, const char *payload); + static bool publish(String topic, const char *payload, bool retained); + + static bool publish(const char *topic, const char *payload); + static bool publish(const char *topic, const char *payload, bool retained); + static bool publish(const char *topic, const uint8_t *payload, unsigned int plength); + static bool publish(const char *topic, const uint8_t *payload, unsigned int plength, bool retained); + static bool publish_P(const char *topic, const char *payload, bool retained); + static bool publish_P(const char *topic, const uint8_t *payload, unsigned int plength, bool retained); + + static bool subscribe(String topic); + static bool subscribe(String topic, uint8_t qos); + static bool unsubscribe(String topic); + static void perSecondDo(); +}; + +#endif diff --git a/lib/esp_framework/include/Rtc.h b/lib/esp_framework/include/Rtc.h index 965e42a..04e2148 100644 --- a/lib/esp_framework/include/Rtc.h +++ b/lib/esp_framework/include/Rtc.h @@ -1,59 +1,59 @@ -// Ntp.h - -#ifndef _NTP_h -#define _NTP_h - -#include "Arduino.h" - -#define LEAP_YEAR(Y) (((1970 + Y) > 0) && !((1970 + Y) % 4) && (((1970 + Y) % 100) || !((1970 + Y) % 400))) - -typedef struct -{ - uint8_t second; - uint8_t minute; - uint8_t hour; - uint8_t day_of_week; // sunday is day 1 - uint8_t day_of_month; - uint8_t month; - uint16_t day_of_year; - uint16_t year; - unsigned long days; - bool valid; -} TIME_T; - -typedef struct _RtcReboot -{ - uint16_t valid; // 280 (RTC memory offset 100 - sizeof(RTCRBT)) - uint8_t fast_reboot_count; // 282 - uint8_t free_003[1]; // 283 -} RtcReboot; - -const uint16_t RTC_MEM_VALID = 0xA55A; - -class Rtc -{ -protected: - static uint32_t rtcRebootCrc; - static uint8_t operationFlag; - static void getNtp(); - -public: - static void breakTime(uint32_t time_input, TIME_T &tm); - static uint32_t utcTime; - static RtcReboot rtcReboot; - static TIME_T rtcTime; - - static String msToHumanString(uint32_t const msecs); - static String timeSince(uint32_t const start); - - static String GetBuildDateAndTime(); - static void perSecondDo(); - static void init(); - static void loop(); - - static uint32_t getRtcRebootCrc(); - static void rtcRebootLoad(); - static void rtcRebootSave(); -}; - -#endif +// Ntp.h + +#ifndef _NTP_h +#define _NTP_h + +#include "Arduino.h" + +#define LEAP_YEAR(Y) (((1970 + Y) > 0) && !((1970 + Y) % 4) && (((1970 + Y) % 100) || !((1970 + Y) % 400))) + +typedef struct +{ + uint8_t second; + uint8_t minute; + uint8_t hour; + uint8_t day_of_week; // sunday is day 1 + uint8_t day_of_month; + uint8_t month; + uint16_t day_of_year; + uint16_t year; + unsigned long days; + bool valid; +} TIME_T; + +typedef struct _RtcReboot +{ + uint16_t valid; // 280 (RTC memory offset 100 - sizeof(RTCRBT)) + uint8_t fast_reboot_count; // 282 + uint8_t free_003[1]; // 283 +} RtcReboot; + +const uint16_t RTC_MEM_VALID = 0xA55A; + +class Rtc +{ +protected: + static uint32_t rtcRebootCrc; + static uint8_t operationFlag; + static void getNtp(); + +public: + static void breakTime(uint32_t time_input, TIME_T &tm); + static uint32_t utcTime; + static RtcReboot rtcReboot; + static TIME_T rtcTime; + + static String msToHumanString(uint32_t const msecs); + static String timeSince(uint32_t const start); + + static String GetBuildDateAndTime(); + static void perSecondDo(); + static void init(); + static void loop(); + + static uint32_t getRtcRebootCrc(); + static void rtcRebootLoad(); + static void rtcRebootSave(); +}; + +#endif diff --git a/lib/esp_framework/include/Util.h b/lib/esp_framework/include/Util.h index 0f7197e..27109a9 100644 --- a/lib/esp_framework/include/Util.h +++ b/lib/esp_framework/include/Util.h @@ -1,16 +1,16 @@ -#ifndef _Util_h -#define _Util_h - -#include "Arduino.h" - -class Util -{ -public: - static char *strlowr(char *str); - static char *strupr(char *str); - static uint16_t hex2Str(uint8_t *bin, uint16_t bin_size, char *buff, bool needBlank = true); - static char *dtostrfd(double number, unsigned char prec, char *s); - static uint32_t SqrtInt(uint32_t num); - static uint32_t RoundSqrtInt(uint32_t num); -}; +#ifndef _Util_h +#define _Util_h + +#include "Arduino.h" + +class Util +{ +public: + static char *strlowr(char *str); + static char *strupr(char *str); + static uint16_t hex2Str(uint8_t *bin, uint16_t bin_size, char *buff, bool needBlank = true); + static char *dtostrfd(double number, unsigned char prec, char *s); + static uint32_t SqrtInt(uint32_t num); + static uint32_t RoundSqrtInt(uint32_t num); +}; #endif \ No newline at end of file diff --git a/lib/esp_framework/include/Wifi.h b/lib/esp_framework/include/Wifi.h index 16776f4..7c785c0 100644 --- a/lib/esp_framework/include/Wifi.h +++ b/lib/esp_framework/include/Wifi.h @@ -1,42 +1,42 @@ -// Wifi.h - -#ifndef _WIFI_h -#define _WIFI_h - -#include -#include -#include "Arduino.h" - -//#define ConnectTimeOut 300 -#define ConfigPortalTimeOut 120 -#define MinimumWifiSignalQuality 8 - -class Wifi -{ -private: - static bool connect; - - static String _ssid; - static String _pass; - - static DNSServer *dnsServer; - //static unsigned long connectStart; - -public: - static unsigned long configPortalStart; - static bool isDHCP; - static WiFiEventHandler STAGotIP; - //static WiFiEventHandler STADisconnected; - static WiFiClient wifiClient; - static void connectWifi(); - static void setupWifi(); - static void setupWifiManager(bool resetSettings); - static bool isIp(String str); - - static uint8_t waitForConnectResult(); - static void tryConnect(String ssid, String pass); - - static void loop(); -}; - -#endif +// Wifi.h + +#ifndef _WIFI_h +#define _WIFI_h + +#include +#include +#include "Arduino.h" + +//#define ConnectTimeOut 300 +#define ConfigPortalTimeOut 120 +#define MinimumWifiSignalQuality 8 + +class Wifi +{ +private: + static bool connect; + + static String _ssid; + static String _pass; + + static DNSServer *dnsServer; + //static unsigned long connectStart; + +public: + static unsigned long configPortalStart; + static bool isDHCP; + static WiFiEventHandler STAGotIP; + //static WiFiEventHandler STADisconnected; + static WiFiClient wifiClient; + static void connectWifi(); + static void setupWifi(); + static void setupWifiManager(bool resetSettings); + static bool isIp(String str); + + static uint8_t waitForConnectResult(); + static void tryConnect(String ssid, String pass); + + static void loop(); +}; + +#endif diff --git a/lib/esp_framework/library.json b/lib/esp_framework/library.json index 25f56df..f7ff0d7 100644 --- a/lib/esp_framework/library.json +++ b/lib/esp_framework/library.json @@ -1,8 +1,8 @@ -{ - "name": "esp_framework", - "keywords": "esp framework", - "description": "esp framework", - "version": "1.0", - "frameworks": "arduino", - "platforms": "espressif8266" +{ + "name": "esp_framework", + "keywords": "esp framework", + "description": "esp framework", + "version": "1.0", + "frameworks": "arduino", + "platforms": "espressif8266" } \ No newline at end of file diff --git a/lib/esp_framework/nanopb/GlobalConfig.proto b/lib/esp_framework/nanopb/GlobalConfig.proto index c8563de..870b1be 100644 --- a/lib/esp_framework/nanopb/GlobalConfig.proto +++ b/lib/esp_framework/nanopb/GlobalConfig.proto @@ -1,52 +1,52 @@ - -syntax = "proto3"; - -import "nanopb.proto"; - -message GlobalConfigMessage { - WifiConfigMessage wifi = 1; - HttpConfigMessage http = 2; - MqttConfigMessage mqtt = 3; - DebugConfigMessage debug = 4; - uint32 cfg_version = 5 [(nanopb).int_size = IS_16]; - uint32 module_crc = 6 [(nanopb).int_size = IS_16]; - bytes module_cfg = 7 [(nanopb).max_size = 500]; - string uid = 8 [(nanopb).max_size = 20]; -} - -message WifiConfigMessage { - string ssid = 1 [(nanopb).max_size = 20]; - string pass = 2 [(nanopb).max_size = 30]; - - bool is_static = 3; - string ip = 4 [(nanopb).max_size = 15]; - string sn = 5 [(nanopb).max_size = 15]; - string gw = 6 [(nanopb).max_size = 15]; - - string ntp = 7 [(nanopb).max_size = 40]; -} - -message HttpConfigMessage { - uint32 port = 1 [(nanopb).int_size = IS_16]; - string user = 2 [(nanopb).max_size = 15]; - string pass = 3 [(nanopb).max_size = 15]; - string ota_url = 4 [(nanopb).max_size = 150]; -} - -message MqttConfigMessage { - string server = 1 [(nanopb).max_size = 30]; - uint32 port = 2 [(nanopb).int_size = IS_16]; - string user = 3 [(nanopb).max_size = 20]; - string pass = 4 [(nanopb).max_size = 30]; - bool retain = 5; - string topic = 6 [(nanopb).max_size = 50]; - bool discovery = 7; - string discovery_prefix = 8 [(nanopb).max_size = 30]; - uint32 interval = 9 [(nanopb).int_size = IS_16]; -} - -message DebugConfigMessage { - uint32 type = 1 [(nanopb).int_size = IS_8]; - string server = 2 [(nanopb).max_size = 40]; - uint32 port = 3 [(nanopb).int_size = IS_16]; -} + +syntax = "proto3"; + +import "nanopb.proto"; + +message GlobalConfigMessage { + WifiConfigMessage wifi = 1; + HttpConfigMessage http = 2; + MqttConfigMessage mqtt = 3; + DebugConfigMessage debug = 4; + uint32 cfg_version = 5 [(nanopb).int_size = IS_16]; + uint32 module_crc = 6 [(nanopb).int_size = IS_16]; + bytes module_cfg = 7 [(nanopb).max_size = 500]; + string uid = 8 [(nanopb).max_size = 20]; +} + +message WifiConfigMessage { + string ssid = 1 [(nanopb).max_size = 20]; + string pass = 2 [(nanopb).max_size = 30]; + + bool is_static = 3; + string ip = 4 [(nanopb).max_size = 15]; + string sn = 5 [(nanopb).max_size = 15]; + string gw = 6 [(nanopb).max_size = 15]; + + string ntp = 7 [(nanopb).max_size = 40]; +} + +message HttpConfigMessage { + uint32 port = 1 [(nanopb).int_size = IS_16]; + string user = 2 [(nanopb).max_size = 15]; + string pass = 3 [(nanopb).max_size = 15]; + string ota_url = 4 [(nanopb).max_size = 150]; +} + +message MqttConfigMessage { + string server = 1 [(nanopb).max_size = 30]; + uint32 port = 2 [(nanopb).int_size = IS_16]; + string user = 3 [(nanopb).max_size = 20]; + string pass = 4 [(nanopb).max_size = 30]; + bool retain = 5; + string topic = 6 [(nanopb).max_size = 50]; + bool discovery = 7; + string discovery_prefix = 8 [(nanopb).max_size = 30]; + uint32 interval = 9 [(nanopb).int_size = IS_16]; +} + +message DebugConfigMessage { + uint32 type = 1 [(nanopb).int_size = IS_8]; + string server = 2 [(nanopb).max_size = 40]; + uint32 port = 3 [(nanopb).int_size = IS_16]; +} diff --git a/lib/esp_framework/nanopb/descriptor.proto b/lib/esp_framework/nanopb/descriptor.proto index 8697a50..a29d266 100644 --- a/lib/esp_framework/nanopb/descriptor.proto +++ b/lib/esp_framework/nanopb/descriptor.proto @@ -1,872 +1,872 @@ -// Protocol Buffers - Google's data interchange format -// Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Author: kenton@google.com (Kenton Varda) -// Based on original Protocol Buffers design by -// Sanjay Ghemawat, Jeff Dean, and others. -// -// The messages in this file describe the definitions found in .proto files. -// A valid .proto file can be translated directly to a FileDescriptorProto -// without any other information (e.g. without reading its imports). - - -syntax = "proto2"; - -package google.protobuf; -option go_package = "github.com/golang/protobuf/protoc-gen-go/descriptor;descriptor"; -option java_package = "com.google.protobuf"; -option java_outer_classname = "DescriptorProtos"; -option csharp_namespace = "Google.Protobuf.Reflection"; -option objc_class_prefix = "GPB"; -option cc_enable_arenas = true; - -// descriptor.proto must be optimized for speed because reflection-based -// algorithms don't work during bootstrapping. -option optimize_for = SPEED; - -// The protocol compiler can output a FileDescriptorSet containing the .proto -// files it parses. -message FileDescriptorSet { - repeated FileDescriptorProto file = 1; -} - -// Describes a complete .proto file. -message FileDescriptorProto { - optional string name = 1; // file name, relative to root of source tree - optional string package = 2; // e.g. "foo", "foo.bar", etc. - - // Names of files imported by this file. - repeated string dependency = 3; - // Indexes of the public imported files in the dependency list above. - repeated int32 public_dependency = 10; - // Indexes of the weak imported files in the dependency list. - // For Google-internal migration only. Do not use. - repeated int32 weak_dependency = 11; - - // All top-level definitions in this file. - repeated DescriptorProto message_type = 4; - repeated EnumDescriptorProto enum_type = 5; - repeated ServiceDescriptorProto service = 6; - repeated FieldDescriptorProto extension = 7; - - optional FileOptions options = 8; - - // This field contains optional information about the original source code. - // You may safely remove this entire field without harming runtime - // functionality of the descriptors -- the information is needed only by - // development tools. - optional SourceCodeInfo source_code_info = 9; - - // The syntax of the proto file. - // The supported values are "proto2" and "proto3". - optional string syntax = 12; -} - -// Describes a message type. -message DescriptorProto { - optional string name = 1; - - repeated FieldDescriptorProto field = 2; - repeated FieldDescriptorProto extension = 6; - - repeated DescriptorProto nested_type = 3; - repeated EnumDescriptorProto enum_type = 4; - - message ExtensionRange { - optional int32 start = 1; - optional int32 end = 2; - - optional ExtensionRangeOptions options = 3; - } - repeated ExtensionRange extension_range = 5; - - repeated OneofDescriptorProto oneof_decl = 8; - - optional MessageOptions options = 7; - - // Range of reserved tag numbers. Reserved tag numbers may not be used by - // fields or extension ranges in the same message. Reserved ranges may - // not overlap. - message ReservedRange { - optional int32 start = 1; // Inclusive. - optional int32 end = 2; // Exclusive. - } - repeated ReservedRange reserved_range = 9; - // Reserved field names, which may not be used by fields in the same message. - // A given name may only be reserved once. - repeated string reserved_name = 10; -} - -message ExtensionRangeOptions { - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - -// Describes a field within a message. -message FieldDescriptorProto { - enum Type { - // 0 is reserved for errors. - // Order is weird for historical reasons. - TYPE_DOUBLE = 1; - TYPE_FLOAT = 2; - // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if - // negative values are likely. - TYPE_INT64 = 3; - TYPE_UINT64 = 4; - // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if - // negative values are likely. - TYPE_INT32 = 5; - TYPE_FIXED64 = 6; - TYPE_FIXED32 = 7; - TYPE_BOOL = 8; - TYPE_STRING = 9; - // Tag-delimited aggregate. - // Group type is deprecated and not supported in proto3. However, Proto3 - // implementations should still be able to parse the group wire format and - // treat group fields as unknown fields. - TYPE_GROUP = 10; - TYPE_MESSAGE = 11; // Length-delimited aggregate. - - // New in version 2. - TYPE_BYTES = 12; - TYPE_UINT32 = 13; - TYPE_ENUM = 14; - TYPE_SFIXED32 = 15; - TYPE_SFIXED64 = 16; - TYPE_SINT32 = 17; // Uses ZigZag encoding. - TYPE_SINT64 = 18; // Uses ZigZag encoding. - }; - - enum Label { - // 0 is reserved for errors - LABEL_OPTIONAL = 1; - LABEL_REQUIRED = 2; - LABEL_REPEATED = 3; - }; - - optional string name = 1; - optional int32 number = 3; - optional Label label = 4; - - // If type_name is set, this need not be set. If both this and type_name - // are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP. - optional Type type = 5; - - // For message and enum types, this is the name of the type. If the name - // starts with a '.', it is fully-qualified. Otherwise, C++-like scoping - // rules are used to find the type (i.e. first the nested types within this - // message are searched, then within the parent, on up to the root - // namespace). - optional string type_name = 6; - - // For extensions, this is the name of the type being extended. It is - // resolved in the same manner as type_name. - optional string extendee = 2; - - // For numeric types, contains the original text representation of the value. - // For booleans, "true" or "false". - // For strings, contains the default text contents (not escaped in any way). - // For bytes, contains the C escaped value. All bytes >= 128 are escaped. - // TODO(kenton): Base-64 encode? - optional string default_value = 7; - - // If set, gives the index of a oneof in the containing type's oneof_decl - // list. This field is a member of that oneof. - optional int32 oneof_index = 9; - - // JSON name of this field. The value is set by protocol compiler. If the - // user has set a "json_name" option on this field, that option's value - // will be used. Otherwise, it's deduced from the field's name by converting - // it to camelCase. - optional string json_name = 10; - - optional FieldOptions options = 8; -} - -// Describes a oneof. -message OneofDescriptorProto { - optional string name = 1; - optional OneofOptions options = 2; -} - -// Describes an enum type. -message EnumDescriptorProto { - optional string name = 1; - - repeated EnumValueDescriptorProto value = 2; - - optional EnumOptions options = 3; - - // Range of reserved numeric values. Reserved values may not be used by - // entries in the same enum. Reserved ranges may not overlap. - // - // Note that this is distinct from DescriptorProto.ReservedRange in that it - // is inclusive such that it can appropriately represent the entire int32 - // domain. - message EnumReservedRange { - optional int32 start = 1; // Inclusive. - optional int32 end = 2; // Inclusive. - } - - // Range of reserved numeric values. Reserved numeric values may not be used - // by enum values in the same enum declaration. Reserved ranges may not - // overlap. - repeated EnumReservedRange reserved_range = 4; - - // Reserved enum value names, which may not be reused. A given name may only - // be reserved once. - repeated string reserved_name = 5; -} - -// Describes a value within an enum. -message EnumValueDescriptorProto { - optional string name = 1; - optional int32 number = 2; - - optional EnumValueOptions options = 3; -} - -// Describes a service. -message ServiceDescriptorProto { - optional string name = 1; - repeated MethodDescriptorProto method = 2; - - optional ServiceOptions options = 3; -} - -// Describes a method of a service. -message MethodDescriptorProto { - optional string name = 1; - - // Input and output type names. These are resolved in the same way as - // FieldDescriptorProto.type_name, but must refer to a message type. - optional string input_type = 2; - optional string output_type = 3; - - optional MethodOptions options = 4; - - // Identifies if client streams multiple client messages - optional bool client_streaming = 5 [default=false]; - // Identifies if server streams multiple server messages - optional bool server_streaming = 6 [default=false]; -} - - -// =================================================================== -// Options - -// Each of the definitions above may have "options" attached. These are -// just annotations which may cause code to be generated slightly differently -// or may contain hints for code that manipulates protocol messages. -// -// Clients may define custom options as extensions of the *Options messages. -// These extensions may not yet be known at parsing time, so the parser cannot -// store the values in them. Instead it stores them in a field in the *Options -// message called uninterpreted_option. This field must have the same name -// across all *Options messages. We then use this field to populate the -// extensions when we build a descriptor, at which point all protos have been -// parsed and so all extensions are known. -// -// Extension numbers for custom options may be chosen as follows: -// * For options which will only be used within a single application or -// organization, or for experimental options, use field numbers 50000 -// through 99999. It is up to you to ensure that you do not use the -// same number for multiple options. -// * For options which will be published and used publicly by multiple -// independent entities, e-mail protobuf-global-extension-registry@google.com -// to reserve extension numbers. Simply provide your project name (e.g. -// Objective-C plugin) and your project website (if available) -- there's no -// need to explain how you intend to use them. Usually you only need one -// extension number. You can declare multiple options with only one extension -// number by putting them in a sub-message. See the Custom Options section of -// the docs for examples: -// https://developers.google.com/protocol-buffers/docs/proto#options -// If this turns out to be popular, a web service will be set up -// to automatically assign option numbers. - - -message FileOptions { - - // Sets the Java package where classes generated from this .proto will be - // placed. By default, the proto package is used, but this is often - // inappropriate because proto packages do not normally start with backwards - // domain names. - optional string java_package = 1; - - - // If set, all the classes from the .proto file are wrapped in a single - // outer class with the given name. This applies to both Proto1 - // (equivalent to the old "--one_java_file" option) and Proto2 (where - // a .proto always translates to a single class, but you may want to - // explicitly choose the class name). - optional string java_outer_classname = 8; - - // If set true, then the Java code generator will generate a separate .java - // file for each top-level message, enum, and service defined in the .proto - // file. Thus, these types will *not* be nested inside the outer class - // named by java_outer_classname. However, the outer class will still be - // generated to contain the file's getDescriptor() method as well as any - // top-level extensions defined in the file. - optional bool java_multiple_files = 10 [default=false]; - - // This option does nothing. - optional bool java_generate_equals_and_hash = 20 [deprecated=true]; - - // If set true, then the Java2 code generator will generate code that - // throws an exception whenever an attempt is made to assign a non-UTF-8 - // byte sequence to a string field. - // Message reflection will do the same. - // However, an extension field still accepts non-UTF-8 byte sequences. - // This option has no effect on when used with the lite runtime. - optional bool java_string_check_utf8 = 27 [default=false]; - - - // Generated classes can be optimized for speed or code size. - enum OptimizeMode { - SPEED = 1; // Generate complete code for parsing, serialization, - // etc. - CODE_SIZE = 2; // Use ReflectionOps to implement these methods. - LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime. - } - optional OptimizeMode optimize_for = 9 [default=SPEED]; - - // Sets the Go package where structs generated from this .proto will be - // placed. If omitted, the Go package will be derived from the following: - // - The basename of the package import path, if provided. - // - Otherwise, the package statement in the .proto file, if present. - // - Otherwise, the basename of the .proto file, without extension. - optional string go_package = 11; - - - - // Should generic services be generated in each language? "Generic" services - // are not specific to any particular RPC system. They are generated by the - // main code generators in each language (without additional plugins). - // Generic services were the only kind of service generation supported by - // early versions of google.protobuf. - // - // Generic services are now considered deprecated in favor of using plugins - // that generate code specific to your particular RPC system. Therefore, - // these default to false. Old code which depends on generic services should - // explicitly set them to true. - optional bool cc_generic_services = 16 [default=false]; - optional bool java_generic_services = 17 [default=false]; - optional bool py_generic_services = 18 [default=false]; - optional bool php_generic_services = 42 [default=false]; - - // Is this file deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for everything in the file, or it will be completely ignored; in the very - // least, this is a formalization for deprecating files. - optional bool deprecated = 23 [default=false]; - - // Enables the use of arenas for the proto messages in this file. This applies - // only to generated classes for C++. - optional bool cc_enable_arenas = 31 [default=false]; - - - // Sets the objective c class prefix which is prepended to all objective c - // generated classes from this .proto. There is no default. - optional string objc_class_prefix = 36; - - // Namespace for generated classes; defaults to the package. - optional string csharp_namespace = 37; - - // By default Swift generators will take the proto package and CamelCase it - // replacing '.' with underscore and use that to prefix the types/symbols - // defined. When this options is provided, they will use this value instead - // to prefix the types/symbols defined. - optional string swift_prefix = 39; - - // Sets the php class prefix which is prepended to all php generated classes - // from this .proto. Default is empty. - optional string php_class_prefix = 40; - - // Use this option to change the namespace of php generated classes. Default - // is empty. When this option is empty, the package name will be used for - // determining the namespace. - optional string php_namespace = 41; - - // The parser stores options it doesn't recognize here. - // See the documentation for the "Options" section above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. - // See the documentation for the "Options" section above. - extensions 1000 to max; - - reserved 38; -} - -message MessageOptions { - // Set true to use the old proto1 MessageSet wire format for extensions. - // This is provided for backwards-compatibility with the MessageSet wire - // format. You should not use this for any other reason: It's less - // efficient, has fewer features, and is more complicated. - // - // The message must be defined exactly as follows: - // message Foo { - // option message_set_wire_format = true; - // extensions 4 to max; - // } - // Note that the message cannot have any defined fields; MessageSets only - // have extensions. - // - // All extensions of your type must be singular messages; e.g. they cannot - // be int32s, enums, or repeated messages. - // - // Because this is an option, the above two restrictions are not enforced by - // the protocol compiler. - optional bool message_set_wire_format = 1 [default=false]; - - // Disables the generation of the standard "descriptor()" accessor, which can - // conflict with a field of the same name. This is meant to make migration - // from proto1 easier; new code should avoid fields named "descriptor". - optional bool no_standard_descriptor_accessor = 2 [default=false]; - - // Is this message deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the message, or it will be completely ignored; in the very least, - // this is a formalization for deprecating messages. - optional bool deprecated = 3 [default=false]; - - // Whether the message is an automatically generated map entry type for the - // maps field. - // - // For maps fields: - // map map_field = 1; - // The parsed descriptor looks like: - // message MapFieldEntry { - // option map_entry = true; - // optional KeyType key = 1; - // optional ValueType value = 2; - // } - // repeated MapFieldEntry map_field = 1; - // - // Implementations may choose not to generate the map_entry=true message, but - // use a native map in the target language to hold the keys and values. - // The reflection APIs in such implementions still need to work as - // if the field is a repeated message field. - // - // NOTE: Do not set the option in .proto files. Always use the maps syntax - // instead. The option should only be implicitly set by the proto compiler - // parser. - optional bool map_entry = 7; - - reserved 8; // javalite_serializable - reserved 9; // javanano_as_lite - - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - -message FieldOptions { - // The ctype option instructs the C++ code generator to use a different - // representation of the field than it normally would. See the specific - // options below. This option is not yet implemented in the open source - // release -- sorry, we'll try to include it in a future version! - optional CType ctype = 1 [default = STRING]; - enum CType { - // Default mode. - STRING = 0; - - CORD = 1; - - STRING_PIECE = 2; - } - // The packed option can be enabled for repeated primitive fields to enable - // a more efficient representation on the wire. Rather than repeatedly - // writing the tag and type for each element, the entire array is encoded as - // a single length-delimited blob. In proto3, only explicit setting it to - // false will avoid using packed encoding. - optional bool packed = 2; - - // The jstype option determines the JavaScript type used for values of the - // field. The option is permitted only for 64 bit integral and fixed types - // (int64, uint64, sint64, fixed64, sfixed64). A field with jstype JS_STRING - // is represented as JavaScript string, which avoids loss of precision that - // can happen when a large value is converted to a floating point JavaScript. - // Specifying JS_NUMBER for the jstype causes the generated JavaScript code to - // use the JavaScript "number" type. The behavior of the default option - // JS_NORMAL is implementation dependent. - // - // This option is an enum to permit additional types to be added, e.g. - // goog.math.Integer. - optional JSType jstype = 6 [default = JS_NORMAL]; - enum JSType { - // Use the default type. - JS_NORMAL = 0; - - // Use JavaScript strings. - JS_STRING = 1; - - // Use JavaScript numbers. - JS_NUMBER = 2; - } - - // Should this field be parsed lazily? Lazy applies only to message-type - // fields. It means that when the outer message is initially parsed, the - // inner message's contents will not be parsed but instead stored in encoded - // form. The inner message will actually be parsed when it is first accessed. - // - // This is only a hint. Implementations are free to choose whether to use - // eager or lazy parsing regardless of the value of this option. However, - // setting this option true suggests that the protocol author believes that - // using lazy parsing on this field is worth the additional bookkeeping - // overhead typically needed to implement it. - // - // This option does not affect the public interface of any generated code; - // all method signatures remain the same. Furthermore, thread-safety of the - // interface is not affected by this option; const methods remain safe to - // call from multiple threads concurrently, while non-const methods continue - // to require exclusive access. - // - // - // Note that implementations may choose not to check required fields within - // a lazy sub-message. That is, calling IsInitialized() on the outer message - // may return true even if the inner message has missing required fields. - // This is necessary because otherwise the inner message would have to be - // parsed in order to perform the check, defeating the purpose of lazy - // parsing. An implementation which chooses not to check required fields - // must be consistent about it. That is, for any particular sub-message, the - // implementation must either *always* check its required fields, or *never* - // check its required fields, regardless of whether or not the message has - // been parsed. - optional bool lazy = 5 [default=false]; - - // Is this field deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for accessors, or it will be completely ignored; in the very least, this - // is a formalization for deprecating fields. - optional bool deprecated = 3 [default=false]; - - // For Google-internal migration only. Do not use. - optional bool weak = 10 [default=false]; - - - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; - - reserved 4; // removed jtype -} - -message OneofOptions { - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - -message EnumOptions { - - // Set this option to true to allow mapping different tag names to the same - // value. - optional bool allow_alias = 2; - - // Is this enum deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the enum, or it will be completely ignored; in the very least, this - // is a formalization for deprecating enums. - optional bool deprecated = 3 [default=false]; - - reserved 5; // javanano_as_lite - - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - -message EnumValueOptions { - // Is this enum value deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the enum value, or it will be completely ignored; in the very least, - // this is a formalization for deprecating enum values. - optional bool deprecated = 1 [default=false]; - - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - -message ServiceOptions { - - // Note: Field numbers 1 through 32 are reserved for Google's internal RPC - // framework. We apologize for hoarding these numbers to ourselves, but - // we were already using them long before we decided to release Protocol - // Buffers. - - // Is this service deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the service, or it will be completely ignored; in the very least, - // this is a formalization for deprecating services. - optional bool deprecated = 33 [default=false]; - - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - -message MethodOptions { - - // Note: Field numbers 1 through 32 are reserved for Google's internal RPC - // framework. We apologize for hoarding these numbers to ourselves, but - // we were already using them long before we decided to release Protocol - // Buffers. - - // Is this method deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the method, or it will be completely ignored; in the very least, - // this is a formalization for deprecating methods. - optional bool deprecated = 33 [default=false]; - - // Is this method side-effect-free (or safe in HTTP parlance), or idempotent, - // or neither? HTTP based RPC implementation may choose GET verb for safe - // methods, and PUT verb for idempotent methods instead of the default POST. - enum IdempotencyLevel { - IDEMPOTENCY_UNKNOWN = 0; - NO_SIDE_EFFECTS = 1; // implies idempotent - IDEMPOTENT = 2; // idempotent, but may have side effects - } - optional IdempotencyLevel idempotency_level = - 34 [default=IDEMPOTENCY_UNKNOWN]; - - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - - -// A message representing a option the parser does not recognize. This only -// appears in options protos created by the compiler::Parser class. -// DescriptorPool resolves these when building Descriptor objects. Therefore, -// options protos in descriptor objects (e.g. returned by Descriptor::options(), -// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions -// in them. -message UninterpretedOption { - // The name of the uninterpreted option. Each string represents a segment in - // a dot-separated name. is_extension is true iff a segment represents an - // extension (denoted with parentheses in options specs in .proto files). - // E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents - // "foo.(bar.baz).qux". - message NamePart { - required string name_part = 1; - required bool is_extension = 2; - } - repeated NamePart name = 2; - - // The value of the uninterpreted option, in whatever type the tokenizer - // identified it as during parsing. Exactly one of these should be set. - optional string identifier_value = 3; - optional uint64 positive_int_value = 4; - optional int64 negative_int_value = 5; - optional double double_value = 6; - optional bytes string_value = 7; - optional string aggregate_value = 8; -} - -// =================================================================== -// Optional source code info - -// Encapsulates information about the original source file from which a -// FileDescriptorProto was generated. -message SourceCodeInfo { - // A Location identifies a piece of source code in a .proto file which - // corresponds to a particular definition. This information is intended - // to be useful to IDEs, code indexers, documentation generators, and similar - // tools. - // - // For example, say we have a file like: - // message Foo { - // optional string foo = 1; - // } - // Let's look at just the field definition: - // optional string foo = 1; - // ^ ^^ ^^ ^ ^^^ - // a bc de f ghi - // We have the following locations: - // span path represents - // [a,i) [ 4, 0, 2, 0 ] The whole field definition. - // [a,b) [ 4, 0, 2, 0, 4 ] The label (optional). - // [c,d) [ 4, 0, 2, 0, 5 ] The type (string). - // [e,f) [ 4, 0, 2, 0, 1 ] The name (foo). - // [g,h) [ 4, 0, 2, 0, 3 ] The number (1). - // - // Notes: - // - A location may refer to a repeated field itself (i.e. not to any - // particular index within it). This is used whenever a set of elements are - // logically enclosed in a single code segment. For example, an entire - // extend block (possibly containing multiple extension definitions) will - // have an outer location whose path refers to the "extensions" repeated - // field without an index. - // - Multiple locations may have the same path. This happens when a single - // logical declaration is spread out across multiple places. The most - // obvious example is the "extend" block again -- there may be multiple - // extend blocks in the same scope, each of which will have the same path. - // - A location's span is not always a subset of its parent's span. For - // example, the "extendee" of an extension declaration appears at the - // beginning of the "extend" block and is shared by all extensions within - // the block. - // - Just because a location's span is a subset of some other location's span - // does not mean that it is a descendent. For example, a "group" defines - // both a type and a field in a single declaration. Thus, the locations - // corresponding to the type and field and their components will overlap. - // - Code which tries to interpret locations should probably be designed to - // ignore those that it doesn't understand, as more types of locations could - // be recorded in the future. - repeated Location location = 1; - message Location { - // Identifies which part of the FileDescriptorProto was defined at this - // location. - // - // Each element is a field number or an index. They form a path from - // the root FileDescriptorProto to the place where the definition. For - // example, this path: - // [ 4, 3, 2, 7, 1 ] - // refers to: - // file.message_type(3) // 4, 3 - // .field(7) // 2, 7 - // .name() // 1 - // This is because FileDescriptorProto.message_type has field number 4: - // repeated DescriptorProto message_type = 4; - // and DescriptorProto.field has field number 2: - // repeated FieldDescriptorProto field = 2; - // and FieldDescriptorProto.name has field number 1: - // optional string name = 1; - // - // Thus, the above path gives the location of a field name. If we removed - // the last element: - // [ 4, 3, 2, 7 ] - // this path refers to the whole field declaration (from the beginning - // of the label to the terminating semicolon). - repeated int32 path = 1 [packed=true]; - - // Always has exactly three or four elements: start line, start column, - // end line (optional, otherwise assumed same as start line), end column. - // These are packed into a single field for efficiency. Note that line - // and column numbers are zero-based -- typically you will want to add - // 1 to each before displaying to a user. - repeated int32 span = 2 [packed=true]; - - // If this SourceCodeInfo represents a complete declaration, these are any - // comments appearing before and after the declaration which appear to be - // attached to the declaration. - // - // A series of line comments appearing on consecutive lines, with no other - // tokens appearing on those lines, will be treated as a single comment. - // - // leading_detached_comments will keep paragraphs of comments that appear - // before (but not connected to) the current element. Each paragraph, - // separated by empty lines, will be one comment element in the repeated - // field. - // - // Only the comment content is provided; comment markers (e.g. //) are - // stripped out. For block comments, leading whitespace and an asterisk - // will be stripped from the beginning of each line other than the first. - // Newlines are included in the output. - // - // Examples: - // - // optional int32 foo = 1; // Comment attached to foo. - // // Comment attached to bar. - // optional int32 bar = 2; - // - // optional string baz = 3; - // // Comment attached to baz. - // // Another line attached to baz. - // - // // Comment attached to qux. - // // - // // Another line attached to qux. - // optional double qux = 4; - // - // // Detached comment for corge. This is not leading or trailing comments - // // to qux or corge because there are blank lines separating it from - // // both. - // - // // Detached comment for corge paragraph 2. - // - // optional string corge = 5; - // /* Block comment attached - // * to corge. Leading asterisks - // * will be removed. */ - // /* Block comment attached to - // * grault. */ - // optional int32 grault = 6; - // - // // ignored detached comments. - optional string leading_comments = 3; - optional string trailing_comments = 4; - repeated string leading_detached_comments = 6; - } -} - -// Describes the relationship between generated code and its original source -// file. A GeneratedCodeInfo message is associated with only one generated -// source file, but may contain references to different source .proto files. -message GeneratedCodeInfo { - // An Annotation connects some span of text in generated code to an element - // of its generating .proto file. - repeated Annotation annotation = 1; - message Annotation { - // Identifies the element in the original source .proto file. This field - // is formatted the same as SourceCodeInfo.Location.path. - repeated int32 path = 1 [packed=true]; - - // Identifies the filesystem path to the original source .proto. - optional string source_file = 2; - - // Identifies the starting offset in bytes in the generated code - // that relates to the identified object. - optional int32 begin = 3; - - // Identifies the ending offset in bytes in the generated code that - // relates to the identified offset. The end offset should be one past - // the last relevant byte (so the length of the text = end - begin). - optional int32 end = 4; - } -} +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// The messages in this file describe the definitions found in .proto files. +// A valid .proto file can be translated directly to a FileDescriptorProto +// without any other information (e.g. without reading its imports). + + +syntax = "proto2"; + +package google.protobuf; +option go_package = "github.com/golang/protobuf/protoc-gen-go/descriptor;descriptor"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "DescriptorProtos"; +option csharp_namespace = "Google.Protobuf.Reflection"; +option objc_class_prefix = "GPB"; +option cc_enable_arenas = true; + +// descriptor.proto must be optimized for speed because reflection-based +// algorithms don't work during bootstrapping. +option optimize_for = SPEED; + +// The protocol compiler can output a FileDescriptorSet containing the .proto +// files it parses. +message FileDescriptorSet { + repeated FileDescriptorProto file = 1; +} + +// Describes a complete .proto file. +message FileDescriptorProto { + optional string name = 1; // file name, relative to root of source tree + optional string package = 2; // e.g. "foo", "foo.bar", etc. + + // Names of files imported by this file. + repeated string dependency = 3; + // Indexes of the public imported files in the dependency list above. + repeated int32 public_dependency = 10; + // Indexes of the weak imported files in the dependency list. + // For Google-internal migration only. Do not use. + repeated int32 weak_dependency = 11; + + // All top-level definitions in this file. + repeated DescriptorProto message_type = 4; + repeated EnumDescriptorProto enum_type = 5; + repeated ServiceDescriptorProto service = 6; + repeated FieldDescriptorProto extension = 7; + + optional FileOptions options = 8; + + // This field contains optional information about the original source code. + // You may safely remove this entire field without harming runtime + // functionality of the descriptors -- the information is needed only by + // development tools. + optional SourceCodeInfo source_code_info = 9; + + // The syntax of the proto file. + // The supported values are "proto2" and "proto3". + optional string syntax = 12; +} + +// Describes a message type. +message DescriptorProto { + optional string name = 1; + + repeated FieldDescriptorProto field = 2; + repeated FieldDescriptorProto extension = 6; + + repeated DescriptorProto nested_type = 3; + repeated EnumDescriptorProto enum_type = 4; + + message ExtensionRange { + optional int32 start = 1; + optional int32 end = 2; + + optional ExtensionRangeOptions options = 3; + } + repeated ExtensionRange extension_range = 5; + + repeated OneofDescriptorProto oneof_decl = 8; + + optional MessageOptions options = 7; + + // Range of reserved tag numbers. Reserved tag numbers may not be used by + // fields or extension ranges in the same message. Reserved ranges may + // not overlap. + message ReservedRange { + optional int32 start = 1; // Inclusive. + optional int32 end = 2; // Exclusive. + } + repeated ReservedRange reserved_range = 9; + // Reserved field names, which may not be used by fields in the same message. + // A given name may only be reserved once. + repeated string reserved_name = 10; +} + +message ExtensionRangeOptions { + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +// Describes a field within a message. +message FieldDescriptorProto { + enum Type { + // 0 is reserved for errors. + // Order is weird for historical reasons. + TYPE_DOUBLE = 1; + TYPE_FLOAT = 2; + // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if + // negative values are likely. + TYPE_INT64 = 3; + TYPE_UINT64 = 4; + // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if + // negative values are likely. + TYPE_INT32 = 5; + TYPE_FIXED64 = 6; + TYPE_FIXED32 = 7; + TYPE_BOOL = 8; + TYPE_STRING = 9; + // Tag-delimited aggregate. + // Group type is deprecated and not supported in proto3. However, Proto3 + // implementations should still be able to parse the group wire format and + // treat group fields as unknown fields. + TYPE_GROUP = 10; + TYPE_MESSAGE = 11; // Length-delimited aggregate. + + // New in version 2. + TYPE_BYTES = 12; + TYPE_UINT32 = 13; + TYPE_ENUM = 14; + TYPE_SFIXED32 = 15; + TYPE_SFIXED64 = 16; + TYPE_SINT32 = 17; // Uses ZigZag encoding. + TYPE_SINT64 = 18; // Uses ZigZag encoding. + }; + + enum Label { + // 0 is reserved for errors + LABEL_OPTIONAL = 1; + LABEL_REQUIRED = 2; + LABEL_REPEATED = 3; + }; + + optional string name = 1; + optional int32 number = 3; + optional Label label = 4; + + // If type_name is set, this need not be set. If both this and type_name + // are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP. + optional Type type = 5; + + // For message and enum types, this is the name of the type. If the name + // starts with a '.', it is fully-qualified. Otherwise, C++-like scoping + // rules are used to find the type (i.e. first the nested types within this + // message are searched, then within the parent, on up to the root + // namespace). + optional string type_name = 6; + + // For extensions, this is the name of the type being extended. It is + // resolved in the same manner as type_name. + optional string extendee = 2; + + // For numeric types, contains the original text representation of the value. + // For booleans, "true" or "false". + // For strings, contains the default text contents (not escaped in any way). + // For bytes, contains the C escaped value. All bytes >= 128 are escaped. + // TODO(kenton): Base-64 encode? + optional string default_value = 7; + + // If set, gives the index of a oneof in the containing type's oneof_decl + // list. This field is a member of that oneof. + optional int32 oneof_index = 9; + + // JSON name of this field. The value is set by protocol compiler. If the + // user has set a "json_name" option on this field, that option's value + // will be used. Otherwise, it's deduced from the field's name by converting + // it to camelCase. + optional string json_name = 10; + + optional FieldOptions options = 8; +} + +// Describes a oneof. +message OneofDescriptorProto { + optional string name = 1; + optional OneofOptions options = 2; +} + +// Describes an enum type. +message EnumDescriptorProto { + optional string name = 1; + + repeated EnumValueDescriptorProto value = 2; + + optional EnumOptions options = 3; + + // Range of reserved numeric values. Reserved values may not be used by + // entries in the same enum. Reserved ranges may not overlap. + // + // Note that this is distinct from DescriptorProto.ReservedRange in that it + // is inclusive such that it can appropriately represent the entire int32 + // domain. + message EnumReservedRange { + optional int32 start = 1; // Inclusive. + optional int32 end = 2; // Inclusive. + } + + // Range of reserved numeric values. Reserved numeric values may not be used + // by enum values in the same enum declaration. Reserved ranges may not + // overlap. + repeated EnumReservedRange reserved_range = 4; + + // Reserved enum value names, which may not be reused. A given name may only + // be reserved once. + repeated string reserved_name = 5; +} + +// Describes a value within an enum. +message EnumValueDescriptorProto { + optional string name = 1; + optional int32 number = 2; + + optional EnumValueOptions options = 3; +} + +// Describes a service. +message ServiceDescriptorProto { + optional string name = 1; + repeated MethodDescriptorProto method = 2; + + optional ServiceOptions options = 3; +} + +// Describes a method of a service. +message MethodDescriptorProto { + optional string name = 1; + + // Input and output type names. These are resolved in the same way as + // FieldDescriptorProto.type_name, but must refer to a message type. + optional string input_type = 2; + optional string output_type = 3; + + optional MethodOptions options = 4; + + // Identifies if client streams multiple client messages + optional bool client_streaming = 5 [default=false]; + // Identifies if server streams multiple server messages + optional bool server_streaming = 6 [default=false]; +} + + +// =================================================================== +// Options + +// Each of the definitions above may have "options" attached. These are +// just annotations which may cause code to be generated slightly differently +// or may contain hints for code that manipulates protocol messages. +// +// Clients may define custom options as extensions of the *Options messages. +// These extensions may not yet be known at parsing time, so the parser cannot +// store the values in them. Instead it stores them in a field in the *Options +// message called uninterpreted_option. This field must have the same name +// across all *Options messages. We then use this field to populate the +// extensions when we build a descriptor, at which point all protos have been +// parsed and so all extensions are known. +// +// Extension numbers for custom options may be chosen as follows: +// * For options which will only be used within a single application or +// organization, or for experimental options, use field numbers 50000 +// through 99999. It is up to you to ensure that you do not use the +// same number for multiple options. +// * For options which will be published and used publicly by multiple +// independent entities, e-mail protobuf-global-extension-registry@google.com +// to reserve extension numbers. Simply provide your project name (e.g. +// Objective-C plugin) and your project website (if available) -- there's no +// need to explain how you intend to use them. Usually you only need one +// extension number. You can declare multiple options with only one extension +// number by putting them in a sub-message. See the Custom Options section of +// the docs for examples: +// https://developers.google.com/protocol-buffers/docs/proto#options +// If this turns out to be popular, a web service will be set up +// to automatically assign option numbers. + + +message FileOptions { + + // Sets the Java package where classes generated from this .proto will be + // placed. By default, the proto package is used, but this is often + // inappropriate because proto packages do not normally start with backwards + // domain names. + optional string java_package = 1; + + + // If set, all the classes from the .proto file are wrapped in a single + // outer class with the given name. This applies to both Proto1 + // (equivalent to the old "--one_java_file" option) and Proto2 (where + // a .proto always translates to a single class, but you may want to + // explicitly choose the class name). + optional string java_outer_classname = 8; + + // If set true, then the Java code generator will generate a separate .java + // file for each top-level message, enum, and service defined in the .proto + // file. Thus, these types will *not* be nested inside the outer class + // named by java_outer_classname. However, the outer class will still be + // generated to contain the file's getDescriptor() method as well as any + // top-level extensions defined in the file. + optional bool java_multiple_files = 10 [default=false]; + + // This option does nothing. + optional bool java_generate_equals_and_hash = 20 [deprecated=true]; + + // If set true, then the Java2 code generator will generate code that + // throws an exception whenever an attempt is made to assign a non-UTF-8 + // byte sequence to a string field. + // Message reflection will do the same. + // However, an extension field still accepts non-UTF-8 byte sequences. + // This option has no effect on when used with the lite runtime. + optional bool java_string_check_utf8 = 27 [default=false]; + + + // Generated classes can be optimized for speed or code size. + enum OptimizeMode { + SPEED = 1; // Generate complete code for parsing, serialization, + // etc. + CODE_SIZE = 2; // Use ReflectionOps to implement these methods. + LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime. + } + optional OptimizeMode optimize_for = 9 [default=SPEED]; + + // Sets the Go package where structs generated from this .proto will be + // placed. If omitted, the Go package will be derived from the following: + // - The basename of the package import path, if provided. + // - Otherwise, the package statement in the .proto file, if present. + // - Otherwise, the basename of the .proto file, without extension. + optional string go_package = 11; + + + + // Should generic services be generated in each language? "Generic" services + // are not specific to any particular RPC system. They are generated by the + // main code generators in each language (without additional plugins). + // Generic services were the only kind of service generation supported by + // early versions of google.protobuf. + // + // Generic services are now considered deprecated in favor of using plugins + // that generate code specific to your particular RPC system. Therefore, + // these default to false. Old code which depends on generic services should + // explicitly set them to true. + optional bool cc_generic_services = 16 [default=false]; + optional bool java_generic_services = 17 [default=false]; + optional bool py_generic_services = 18 [default=false]; + optional bool php_generic_services = 42 [default=false]; + + // Is this file deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for everything in the file, or it will be completely ignored; in the very + // least, this is a formalization for deprecating files. + optional bool deprecated = 23 [default=false]; + + // Enables the use of arenas for the proto messages in this file. This applies + // only to generated classes for C++. + optional bool cc_enable_arenas = 31 [default=false]; + + + // Sets the objective c class prefix which is prepended to all objective c + // generated classes from this .proto. There is no default. + optional string objc_class_prefix = 36; + + // Namespace for generated classes; defaults to the package. + optional string csharp_namespace = 37; + + // By default Swift generators will take the proto package and CamelCase it + // replacing '.' with underscore and use that to prefix the types/symbols + // defined. When this options is provided, they will use this value instead + // to prefix the types/symbols defined. + optional string swift_prefix = 39; + + // Sets the php class prefix which is prepended to all php generated classes + // from this .proto. Default is empty. + optional string php_class_prefix = 40; + + // Use this option to change the namespace of php generated classes. Default + // is empty. When this option is empty, the package name will be used for + // determining the namespace. + optional string php_namespace = 41; + + // The parser stores options it doesn't recognize here. + // See the documentation for the "Options" section above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. + // See the documentation for the "Options" section above. + extensions 1000 to max; + + reserved 38; +} + +message MessageOptions { + // Set true to use the old proto1 MessageSet wire format for extensions. + // This is provided for backwards-compatibility with the MessageSet wire + // format. You should not use this for any other reason: It's less + // efficient, has fewer features, and is more complicated. + // + // The message must be defined exactly as follows: + // message Foo { + // option message_set_wire_format = true; + // extensions 4 to max; + // } + // Note that the message cannot have any defined fields; MessageSets only + // have extensions. + // + // All extensions of your type must be singular messages; e.g. they cannot + // be int32s, enums, or repeated messages. + // + // Because this is an option, the above two restrictions are not enforced by + // the protocol compiler. + optional bool message_set_wire_format = 1 [default=false]; + + // Disables the generation of the standard "descriptor()" accessor, which can + // conflict with a field of the same name. This is meant to make migration + // from proto1 easier; new code should avoid fields named "descriptor". + optional bool no_standard_descriptor_accessor = 2 [default=false]; + + // Is this message deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the message, or it will be completely ignored; in the very least, + // this is a formalization for deprecating messages. + optional bool deprecated = 3 [default=false]; + + // Whether the message is an automatically generated map entry type for the + // maps field. + // + // For maps fields: + // map map_field = 1; + // The parsed descriptor looks like: + // message MapFieldEntry { + // option map_entry = true; + // optional KeyType key = 1; + // optional ValueType value = 2; + // } + // repeated MapFieldEntry map_field = 1; + // + // Implementations may choose not to generate the map_entry=true message, but + // use a native map in the target language to hold the keys and values. + // The reflection APIs in such implementions still need to work as + // if the field is a repeated message field. + // + // NOTE: Do not set the option in .proto files. Always use the maps syntax + // instead. The option should only be implicitly set by the proto compiler + // parser. + optional bool map_entry = 7; + + reserved 8; // javalite_serializable + reserved 9; // javanano_as_lite + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message FieldOptions { + // The ctype option instructs the C++ code generator to use a different + // representation of the field than it normally would. See the specific + // options below. This option is not yet implemented in the open source + // release -- sorry, we'll try to include it in a future version! + optional CType ctype = 1 [default = STRING]; + enum CType { + // Default mode. + STRING = 0; + + CORD = 1; + + STRING_PIECE = 2; + } + // The packed option can be enabled for repeated primitive fields to enable + // a more efficient representation on the wire. Rather than repeatedly + // writing the tag and type for each element, the entire array is encoded as + // a single length-delimited blob. In proto3, only explicit setting it to + // false will avoid using packed encoding. + optional bool packed = 2; + + // The jstype option determines the JavaScript type used for values of the + // field. The option is permitted only for 64 bit integral and fixed types + // (int64, uint64, sint64, fixed64, sfixed64). A field with jstype JS_STRING + // is represented as JavaScript string, which avoids loss of precision that + // can happen when a large value is converted to a floating point JavaScript. + // Specifying JS_NUMBER for the jstype causes the generated JavaScript code to + // use the JavaScript "number" type. The behavior of the default option + // JS_NORMAL is implementation dependent. + // + // This option is an enum to permit additional types to be added, e.g. + // goog.math.Integer. + optional JSType jstype = 6 [default = JS_NORMAL]; + enum JSType { + // Use the default type. + JS_NORMAL = 0; + + // Use JavaScript strings. + JS_STRING = 1; + + // Use JavaScript numbers. + JS_NUMBER = 2; + } + + // Should this field be parsed lazily? Lazy applies only to message-type + // fields. It means that when the outer message is initially parsed, the + // inner message's contents will not be parsed but instead stored in encoded + // form. The inner message will actually be parsed when it is first accessed. + // + // This is only a hint. Implementations are free to choose whether to use + // eager or lazy parsing regardless of the value of this option. However, + // setting this option true suggests that the protocol author believes that + // using lazy parsing on this field is worth the additional bookkeeping + // overhead typically needed to implement it. + // + // This option does not affect the public interface of any generated code; + // all method signatures remain the same. Furthermore, thread-safety of the + // interface is not affected by this option; const methods remain safe to + // call from multiple threads concurrently, while non-const methods continue + // to require exclusive access. + // + // + // Note that implementations may choose not to check required fields within + // a lazy sub-message. That is, calling IsInitialized() on the outer message + // may return true even if the inner message has missing required fields. + // This is necessary because otherwise the inner message would have to be + // parsed in order to perform the check, defeating the purpose of lazy + // parsing. An implementation which chooses not to check required fields + // must be consistent about it. That is, for any particular sub-message, the + // implementation must either *always* check its required fields, or *never* + // check its required fields, regardless of whether or not the message has + // been parsed. + optional bool lazy = 5 [default=false]; + + // Is this field deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for accessors, or it will be completely ignored; in the very least, this + // is a formalization for deprecating fields. + optional bool deprecated = 3 [default=false]; + + // For Google-internal migration only. Do not use. + optional bool weak = 10 [default=false]; + + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; + + reserved 4; // removed jtype +} + +message OneofOptions { + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message EnumOptions { + + // Set this option to true to allow mapping different tag names to the same + // value. + optional bool allow_alias = 2; + + // Is this enum deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the enum, or it will be completely ignored; in the very least, this + // is a formalization for deprecating enums. + optional bool deprecated = 3 [default=false]; + + reserved 5; // javanano_as_lite + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message EnumValueOptions { + // Is this enum value deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the enum value, or it will be completely ignored; in the very least, + // this is a formalization for deprecating enum values. + optional bool deprecated = 1 [default=false]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message ServiceOptions { + + // Note: Field numbers 1 through 32 are reserved for Google's internal RPC + // framework. We apologize for hoarding these numbers to ourselves, but + // we were already using them long before we decided to release Protocol + // Buffers. + + // Is this service deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the service, or it will be completely ignored; in the very least, + // this is a formalization for deprecating services. + optional bool deprecated = 33 [default=false]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message MethodOptions { + + // Note: Field numbers 1 through 32 are reserved for Google's internal RPC + // framework. We apologize for hoarding these numbers to ourselves, but + // we were already using them long before we decided to release Protocol + // Buffers. + + // Is this method deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the method, or it will be completely ignored; in the very least, + // this is a formalization for deprecating methods. + optional bool deprecated = 33 [default=false]; + + // Is this method side-effect-free (or safe in HTTP parlance), or idempotent, + // or neither? HTTP based RPC implementation may choose GET verb for safe + // methods, and PUT verb for idempotent methods instead of the default POST. + enum IdempotencyLevel { + IDEMPOTENCY_UNKNOWN = 0; + NO_SIDE_EFFECTS = 1; // implies idempotent + IDEMPOTENT = 2; // idempotent, but may have side effects + } + optional IdempotencyLevel idempotency_level = + 34 [default=IDEMPOTENCY_UNKNOWN]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + + +// A message representing a option the parser does not recognize. This only +// appears in options protos created by the compiler::Parser class. +// DescriptorPool resolves these when building Descriptor objects. Therefore, +// options protos in descriptor objects (e.g. returned by Descriptor::options(), +// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions +// in them. +message UninterpretedOption { + // The name of the uninterpreted option. Each string represents a segment in + // a dot-separated name. is_extension is true iff a segment represents an + // extension (denoted with parentheses in options specs in .proto files). + // E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents + // "foo.(bar.baz).qux". + message NamePart { + required string name_part = 1; + required bool is_extension = 2; + } + repeated NamePart name = 2; + + // The value of the uninterpreted option, in whatever type the tokenizer + // identified it as during parsing. Exactly one of these should be set. + optional string identifier_value = 3; + optional uint64 positive_int_value = 4; + optional int64 negative_int_value = 5; + optional double double_value = 6; + optional bytes string_value = 7; + optional string aggregate_value = 8; +} + +// =================================================================== +// Optional source code info + +// Encapsulates information about the original source file from which a +// FileDescriptorProto was generated. +message SourceCodeInfo { + // A Location identifies a piece of source code in a .proto file which + // corresponds to a particular definition. This information is intended + // to be useful to IDEs, code indexers, documentation generators, and similar + // tools. + // + // For example, say we have a file like: + // message Foo { + // optional string foo = 1; + // } + // Let's look at just the field definition: + // optional string foo = 1; + // ^ ^^ ^^ ^ ^^^ + // a bc de f ghi + // We have the following locations: + // span path represents + // [a,i) [ 4, 0, 2, 0 ] The whole field definition. + // [a,b) [ 4, 0, 2, 0, 4 ] The label (optional). + // [c,d) [ 4, 0, 2, 0, 5 ] The type (string). + // [e,f) [ 4, 0, 2, 0, 1 ] The name (foo). + // [g,h) [ 4, 0, 2, 0, 3 ] The number (1). + // + // Notes: + // - A location may refer to a repeated field itself (i.e. not to any + // particular index within it). This is used whenever a set of elements are + // logically enclosed in a single code segment. For example, an entire + // extend block (possibly containing multiple extension definitions) will + // have an outer location whose path refers to the "extensions" repeated + // field without an index. + // - Multiple locations may have the same path. This happens when a single + // logical declaration is spread out across multiple places. The most + // obvious example is the "extend" block again -- there may be multiple + // extend blocks in the same scope, each of which will have the same path. + // - A location's span is not always a subset of its parent's span. For + // example, the "extendee" of an extension declaration appears at the + // beginning of the "extend" block and is shared by all extensions within + // the block. + // - Just because a location's span is a subset of some other location's span + // does not mean that it is a descendent. For example, a "group" defines + // both a type and a field in a single declaration. Thus, the locations + // corresponding to the type and field and their components will overlap. + // - Code which tries to interpret locations should probably be designed to + // ignore those that it doesn't understand, as more types of locations could + // be recorded in the future. + repeated Location location = 1; + message Location { + // Identifies which part of the FileDescriptorProto was defined at this + // location. + // + // Each element is a field number or an index. They form a path from + // the root FileDescriptorProto to the place where the definition. For + // example, this path: + // [ 4, 3, 2, 7, 1 ] + // refers to: + // file.message_type(3) // 4, 3 + // .field(7) // 2, 7 + // .name() // 1 + // This is because FileDescriptorProto.message_type has field number 4: + // repeated DescriptorProto message_type = 4; + // and DescriptorProto.field has field number 2: + // repeated FieldDescriptorProto field = 2; + // and FieldDescriptorProto.name has field number 1: + // optional string name = 1; + // + // Thus, the above path gives the location of a field name. If we removed + // the last element: + // [ 4, 3, 2, 7 ] + // this path refers to the whole field declaration (from the beginning + // of the label to the terminating semicolon). + repeated int32 path = 1 [packed=true]; + + // Always has exactly three or four elements: start line, start column, + // end line (optional, otherwise assumed same as start line), end column. + // These are packed into a single field for efficiency. Note that line + // and column numbers are zero-based -- typically you will want to add + // 1 to each before displaying to a user. + repeated int32 span = 2 [packed=true]; + + // If this SourceCodeInfo represents a complete declaration, these are any + // comments appearing before and after the declaration which appear to be + // attached to the declaration. + // + // A series of line comments appearing on consecutive lines, with no other + // tokens appearing on those lines, will be treated as a single comment. + // + // leading_detached_comments will keep paragraphs of comments that appear + // before (but not connected to) the current element. Each paragraph, + // separated by empty lines, will be one comment element in the repeated + // field. + // + // Only the comment content is provided; comment markers (e.g. //) are + // stripped out. For block comments, leading whitespace and an asterisk + // will be stripped from the beginning of each line other than the first. + // Newlines are included in the output. + // + // Examples: + // + // optional int32 foo = 1; // Comment attached to foo. + // // Comment attached to bar. + // optional int32 bar = 2; + // + // optional string baz = 3; + // // Comment attached to baz. + // // Another line attached to baz. + // + // // Comment attached to qux. + // // + // // Another line attached to qux. + // optional double qux = 4; + // + // // Detached comment for corge. This is not leading or trailing comments + // // to qux or corge because there are blank lines separating it from + // // both. + // + // // Detached comment for corge paragraph 2. + // + // optional string corge = 5; + // /* Block comment attached + // * to corge. Leading asterisks + // * will be removed. */ + // /* Block comment attached to + // * grault. */ + // optional int32 grault = 6; + // + // // ignored detached comments. + optional string leading_comments = 3; + optional string trailing_comments = 4; + repeated string leading_detached_comments = 6; + } +} + +// Describes the relationship between generated code and its original source +// file. A GeneratedCodeInfo message is associated with only one generated +// source file, but may contain references to different source .proto files. +message GeneratedCodeInfo { + // An Annotation connects some span of text in generated code to an element + // of its generating .proto file. + repeated Annotation annotation = 1; + message Annotation { + // Identifies the element in the original source .proto file. This field + // is formatted the same as SourceCodeInfo.Location.path. + repeated int32 path = 1 [packed=true]; + + // Identifies the filesystem path to the original source .proto. + optional string source_file = 2; + + // Identifies the starting offset in bytes in the generated code + // that relates to the identified object. + optional int32 begin = 3; + + // Identifies the ending offset in bytes in the generated code that + // relates to the identified offset. The end offset should be one past + // the last relevant byte (so the length of the text = end - begin). + optional int32 end = 4; + } +} diff --git a/lib/esp_framework/nanopb/generator.bat b/lib/esp_framework/nanopb/generator.bat index b768dd1..52b2982 100644 --- a/lib/esp_framework/nanopb/generator.bat +++ b/lib/esp_framework/nanopb/generator.bat @@ -1,7 +1,7 @@ -cd /d %~dp0 - -D:\Workspaces_Smarthome\esp\nanopb\generator-bin\protoc.exe --nanopb_out=. GlobalConfig.proto -copy GlobalConfig.pb.h ..\include /y -copy GlobalConfig.pb.c ..\src /y -del GlobalConfig.pb.h -del GlobalConfig.pb.c +cd /d %~dp0 + +D:\Workspaces_Smarthome\esp\nanopb\generator-bin\protoc.exe --nanopb_out=. GlobalConfig.proto +copy GlobalConfig.pb.h ..\include /y +copy GlobalConfig.pb.c ..\src /y +del GlobalConfig.pb.h +del GlobalConfig.pb.c diff --git a/lib/esp_framework/nanopb/nanopb.proto b/lib/esp_framework/nanopb/nanopb.proto index e89eea1..978da53 100644 --- a/lib/esp_framework/nanopb/nanopb.proto +++ b/lib/esp_framework/nanopb/nanopb.proto @@ -1,124 +1,124 @@ -// Custom options for defining: -// - Maximum size of string/bytes -// - Maximum number of elements in array -// -// These are used by nanopb to generate statically allocable structures -// for memory-limited environments. - -syntax = "proto2"; -import "descriptor.proto"; - -option java_package = "fi.kapsi.koti.jpa.nanopb"; - -enum FieldType { - FT_DEFAULT = 0; // Automatically decide field type, generate static field if possible. - FT_CALLBACK = 1; // Always generate a callback field. - FT_POINTER = 4; // Always generate a dynamically allocated field. - FT_STATIC = 2; // Generate a static field or raise an exception if not possible. - FT_IGNORE = 3; // Ignore the field completely. - FT_INLINE = 5; // Legacy option, use the separate 'fixed_length' option instead -} - -enum IntSize { - IS_DEFAULT = 0; // Default, 32/64bit based on type in .proto - IS_8 = 8; - IS_16 = 16; - IS_32 = 32; - IS_64 = 64; -} - -enum TypenameMangling { - M_NONE = 0; // Default, no typename mangling - M_STRIP_PACKAGE = 1; // Strip current package name - M_FLATTEN = 2; // Only use last path component -} - -// This is the inner options message, which basically defines options for -// a field. When it is used in message or file scope, it applies to all -// fields. -message NanoPBOptions { - // Allocated size for 'bytes' and 'string' fields. - // For string fields, this should include the space for null terminator. - optional int32 max_size = 1; - - // Maximum length for 'string' fields. Setting this is equivalent - // to setting max_size to a value of length+1. - optional int32 max_length = 14; - - // Allocated number of entries in arrays ('repeated' fields) - optional int32 max_count = 2; - - // Size of integer fields. Can save some memory if you don't need - // full 32 bits for the value. - optional IntSize int_size = 7 [default = IS_DEFAULT]; - - // Force type of field (callback or static allocation) - optional FieldType type = 3 [default = FT_DEFAULT]; - - // Use long names for enums, i.e. EnumName_EnumValue. - optional bool long_names = 4 [default = true]; - - // Add 'packed' attribute to generated structs. - // Note: this cannot be used on CPUs that break on unaligned - // accesses to variables. - optional bool packed_struct = 5 [default = false]; - - // Add 'packed' attribute to generated enums. - optional bool packed_enum = 10 [default = false]; - - // Skip this message - optional bool skip_message = 6 [default = false]; - - // Generate oneof fields as normal optional fields instead of union. - optional bool no_unions = 8 [default = false]; - - // integer type tag for a message - optional uint32 msgid = 9; - - // decode oneof as anonymous union - optional bool anonymous_oneof = 11 [default = false]; - - // Proto3 singular field does not generate a "has_" flag - optional bool proto3 = 12 [default = false]; - - // Generate an enum->string mapping function (can take up lots of space). - optional bool enum_to_string = 13 [default = false]; - - // Generate bytes arrays with fixed length - optional bool fixed_length = 15 [default = false]; - - // Generate repeated field with fixed count - optional bool fixed_count = 16 [default = false]; - - // Mangle long names - optional TypenameMangling mangle_names = 17 [default = M_NONE]; -} - -// Extensions to protoc 'Descriptor' type in order to define options -// inside a .proto file. -// -// Protocol Buffers extension number registry -// -------------------------------- -// Project: Nanopb -// Contact: Petteri Aimonen -// Web site: http://kapsi.fi/~jpa/nanopb -// Extensions: 1010 (all types) -// -------------------------------- - -extend google.protobuf.FileOptions { - optional NanoPBOptions nanopb_fileopt = 1010; -} - -extend google.protobuf.MessageOptions { - optional NanoPBOptions nanopb_msgopt = 1010; -} - -extend google.protobuf.EnumOptions { - optional NanoPBOptions nanopb_enumopt = 1010; -} - -extend google.protobuf.FieldOptions { - optional NanoPBOptions nanopb = 1010; -} - - +// Custom options for defining: +// - Maximum size of string/bytes +// - Maximum number of elements in array +// +// These are used by nanopb to generate statically allocable structures +// for memory-limited environments. + +syntax = "proto2"; +import "descriptor.proto"; + +option java_package = "fi.kapsi.koti.jpa.nanopb"; + +enum FieldType { + FT_DEFAULT = 0; // Automatically decide field type, generate static field if possible. + FT_CALLBACK = 1; // Always generate a callback field. + FT_POINTER = 4; // Always generate a dynamically allocated field. + FT_STATIC = 2; // Generate a static field or raise an exception if not possible. + FT_IGNORE = 3; // Ignore the field completely. + FT_INLINE = 5; // Legacy option, use the separate 'fixed_length' option instead +} + +enum IntSize { + IS_DEFAULT = 0; // Default, 32/64bit based on type in .proto + IS_8 = 8; + IS_16 = 16; + IS_32 = 32; + IS_64 = 64; +} + +enum TypenameMangling { + M_NONE = 0; // Default, no typename mangling + M_STRIP_PACKAGE = 1; // Strip current package name + M_FLATTEN = 2; // Only use last path component +} + +// This is the inner options message, which basically defines options for +// a field. When it is used in message or file scope, it applies to all +// fields. +message NanoPBOptions { + // Allocated size for 'bytes' and 'string' fields. + // For string fields, this should include the space for null terminator. + optional int32 max_size = 1; + + // Maximum length for 'string' fields. Setting this is equivalent + // to setting max_size to a value of length+1. + optional int32 max_length = 14; + + // Allocated number of entries in arrays ('repeated' fields) + optional int32 max_count = 2; + + // Size of integer fields. Can save some memory if you don't need + // full 32 bits for the value. + optional IntSize int_size = 7 [default = IS_DEFAULT]; + + // Force type of field (callback or static allocation) + optional FieldType type = 3 [default = FT_DEFAULT]; + + // Use long names for enums, i.e. EnumName_EnumValue. + optional bool long_names = 4 [default = true]; + + // Add 'packed' attribute to generated structs. + // Note: this cannot be used on CPUs that break on unaligned + // accesses to variables. + optional bool packed_struct = 5 [default = false]; + + // Add 'packed' attribute to generated enums. + optional bool packed_enum = 10 [default = false]; + + // Skip this message + optional bool skip_message = 6 [default = false]; + + // Generate oneof fields as normal optional fields instead of union. + optional bool no_unions = 8 [default = false]; + + // integer type tag for a message + optional uint32 msgid = 9; + + // decode oneof as anonymous union + optional bool anonymous_oneof = 11 [default = false]; + + // Proto3 singular field does not generate a "has_" flag + optional bool proto3 = 12 [default = false]; + + // Generate an enum->string mapping function (can take up lots of space). + optional bool enum_to_string = 13 [default = false]; + + // Generate bytes arrays with fixed length + optional bool fixed_length = 15 [default = false]; + + // Generate repeated field with fixed count + optional bool fixed_count = 16 [default = false]; + + // Mangle long names + optional TypenameMangling mangle_names = 17 [default = M_NONE]; +} + +// Extensions to protoc 'Descriptor' type in order to define options +// inside a .proto file. +// +// Protocol Buffers extension number registry +// -------------------------------- +// Project: Nanopb +// Contact: Petteri Aimonen +// Web site: http://kapsi.fi/~jpa/nanopb +// Extensions: 1010 (all types) +// -------------------------------- + +extend google.protobuf.FileOptions { + optional NanoPBOptions nanopb_fileopt = 1010; +} + +extend google.protobuf.MessageOptions { + optional NanoPBOptions nanopb_msgopt = 1010; +} + +extend google.protobuf.EnumOptions { + optional NanoPBOptions nanopb_enumopt = 1010; +} + +extend google.protobuf.FieldOptions { + optional NanoPBOptions nanopb = 1010; +} + + diff --git a/lib/esp_framework/src/Config.cpp b/lib/esp_framework/src/Config.cpp index 74ac220..3030b9c 100644 --- a/lib/esp_framework/src/Config.cpp +++ b/lib/esp_framework/src/Config.cpp @@ -1,243 +1,243 @@ -#include -#include "Module.h" - -Module *module; -char UID[16]; -char tmpData[512] = {0}; -uint32_t perSecond; -Ticker *tickerPerSecond; -GlobalConfigMessage globalConfig; - -uint16_t Config::nowCrc; -uint8_t Config::countdown = 60; -bool Config::isDelay = false; - -const uint16_t crcTalbe[] = { - 0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401, - 0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400}; - -/** - * 计算Crc16 - */ -uint16_t Config::crc16(uint8_t *ptr, uint16_t len) -{ - uint16_t crc = 0xffff; - for (uint16_t i = 0; i < len; i++) - { - const uint8_t ch = *ptr++; - crc = crcTalbe[(ch ^ crc) & 15] ^ (crc >> 4); - crc = crcTalbe[((ch >> 4) ^ crc) & 15] ^ (crc >> 4); - } - return crc; -} - -void Config::resetConfig() -{ - Debug::AddInfo(PSTR("resetConfig . . . OK")); - memset(&globalConfig, 0, sizeof(GlobalConfigMessage)); - -#ifdef WIFI_SSID - strcpy(globalConfig.wifi.ssid, WIFI_SSID); -#endif -#ifdef WIFI_PASS - strcpy(globalConfig.wifi.pass, WIFI_PASS); - -#endif -#ifdef MQTT_SERVER - strcpy(globalConfig.mqtt.server, MQTT_SERVER); -#endif -#ifdef MQTT_PORT - globalConfig.mqtt.port = MQTT_PORT; -#endif -#ifdef MQTT_USER - strcpy(globalConfig.mqtt.user, MQTT_USER); -#endif -#ifdef MQTT_PASS - strcpy(globalConfig.mqtt.pass, MQTT_PASS); -#endif - globalConfig.mqtt.discovery = false; - strcpy(globalConfig.mqtt.discovery_prefix, "homeassistant"); - -#ifdef MQTT_FULLTOPIC - strcpy(globalConfig.mqtt.topic, MQTT_FULLTOPIC); -#endif -#ifdef OTA_URL - strcpy(globalConfig.http.ota_url, OTA_URL); -#endif - globalConfig.http.port = 80; - globalConfig.debug.type = 1; - - if (module) - { - module->resetConfig(); - } -} - -void Config::readConfig() -{ - uint16 len; - bool status = false; - uint16 cfg = (EEPROM.read(0) << 8 | EEPROM.read(1)); - if (cfg == GLOBAL_CFG_VERSION) - { - len = (EEPROM.read(2) << 8 | EEPROM.read(3)); - nowCrc = (EEPROM.read(4) << 8 | EEPROM.read(5)); - - if (len > GlobalConfigMessage_size) - { - len = GlobalConfigMessage_size; - } - - uint16_t crc = 0xffff; - uint8_t buffer[GlobalConfigMessage_size]; - for (uint16_t i = 0; i < len; ++i) - { - buffer[i] = EEPROM.read(i + 6); - crc = crcTalbe[(buffer[i] ^ crc) & 15] ^ (crc >> 4); - crc = crcTalbe[((buffer[i] >> 4) ^ crc) & 15] ^ (crc >> 4); - } - if (crc == nowCrc) - { - memset(&globalConfig, 0, sizeof(GlobalConfigMessage)); - pb_istream_t stream = pb_istream_from_buffer(buffer, len); - status = pb_decode(&stream, GlobalConfigMessage_fields, &globalConfig); - if (globalConfig.http.port == 0) - { - globalConfig.http.port = 80; - } - } - } - - if (!status) - { - globalConfig.debug.type = 1; - Debug::AddError(PSTR("readConfig . . . Error")); - resetConfig(); - } - else - { - if (module) - { - module->readConfig(); - } - Debug::AddInfo(PSTR("readConfig . . . OK Len: %d"), len); - } -} - -bool Config::saveConfig(bool isEverySecond) -{ - countdown = 60; - if (module) - { - module->saveConfig(isEverySecond); - } - uint8_t buffer[GlobalConfigMessage_size]; - pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); - bool status = pb_encode(&stream, GlobalConfigMessage_fields, &globalConfig); - size_t len = stream.bytes_written; - if (!status) - { - Debug::AddError(PSTR("saveConfig . . . Error")); - return false; - } - else - { - uint16_t crc = crc16(buffer, len); - if (crc == nowCrc) - { - // Debug::AddInfo(PSTR("Check Config CRC . . . Same")); - return true; - } - else - { - nowCrc = crc; - } - } - - EEPROM.write(0, GLOBAL_CFG_VERSION >> 8); - EEPROM.write(1, GLOBAL_CFG_VERSION); - - EEPROM.write(2, len >> 8); - EEPROM.write(3, len); - - EEPROM.write(4, nowCrc >> 8); - EEPROM.write(5, nowCrc); - - for (uint16_t i = 0; i < len; i++) - { - EEPROM.write(i + 6, buffer[i]); - } - EEPROM.commit(); - - Debug::AddInfo(PSTR("saveConfig . . . OK Len: %d"), len); - return true; -} - -void Config::perSecondDo() -{ - countdown--; - if (countdown == 0) - { - saveConfig(Config::isDelay ? false : true); - Config::isDelay = false; - } -} - -void Config::delaySaveConfig(uint8_t second) -{ - Config::isDelay = true; - if (countdown > second) - { - countdown = second; - } -} - -void Config::moduleReadConfig(uint16_t version, uint16_t size, const pb_field_t fields[], void *dest_struct) -{ - if (globalConfig.module_cfg.size == 0 // 没有数据 - || globalConfig.cfg_version != version // 版本不一致 - || globalConfig.module_crc != Config::crc16(globalConfig.module_cfg.bytes, globalConfig.module_cfg.size)) // crc错误 - { - Debug::AddError(PSTR("moduleReadConfig . . . Error %d %d %d"), globalConfig.cfg_version, version, globalConfig.module_cfg.size); - if (module) - { - module->resetConfig(); - } - return; - } - memset(dest_struct, 0, size); - pb_istream_t stream = pb_istream_from_buffer(globalConfig.module_cfg.bytes, globalConfig.module_cfg.size); - bool status = pb_decode(&stream, fields, dest_struct); - if (!status) // 解密失败 - { - if (module) - { - module->resetConfig(); - } - } - else - { - Debug::AddInfo(PSTR("moduleReadConfig . . . OK Len: %d"), globalConfig.module_cfg.size); - } -} - -bool Config::moduleSaveConfig(uint16_t version, uint16_t size, const pb_field_t fields[], const void *src_struct) -{ - uint8_t buffer[size]; - pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); - bool status = pb_encode(&stream, fields, src_struct); - if (status) - { - size_t len = stream.bytes_written; - uint16_t crc = Config::crc16(buffer, len); - if (crc != globalConfig.module_crc) - { - globalConfig.cfg_version = version; - globalConfig.module_crc = crc; - globalConfig.module_cfg.size = len; - memcpy(globalConfig.module_cfg.bytes, buffer, len); - //Debug::AddInfo(PSTR("moduleSaveConfig . . . OK Len: %d"), len); - } - } - return status; +#include +#include "Module.h" + +Module *module; +char UID[16]; +char tmpData[512] = {0}; +uint32_t perSecond; +Ticker *tickerPerSecond; +GlobalConfigMessage globalConfig; + +uint16_t Config::nowCrc; +uint8_t Config::countdown = 60; +bool Config::isDelay = false; + +const uint16_t crcTalbe[] = { + 0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401, + 0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400}; + +/** + * 计算Crc16 + */ +uint16_t Config::crc16(uint8_t *ptr, uint16_t len) +{ + uint16_t crc = 0xffff; + for (uint16_t i = 0; i < len; i++) + { + const uint8_t ch = *ptr++; + crc = crcTalbe[(ch ^ crc) & 15] ^ (crc >> 4); + crc = crcTalbe[((ch >> 4) ^ crc) & 15] ^ (crc >> 4); + } + return crc; +} + +void Config::resetConfig() +{ + Debug::AddInfo(PSTR("resetConfig . . . OK")); + memset(&globalConfig, 0, sizeof(GlobalConfigMessage)); + +#ifdef WIFI_SSID + strcpy(globalConfig.wifi.ssid, WIFI_SSID); +#endif +#ifdef WIFI_PASS + strcpy(globalConfig.wifi.pass, WIFI_PASS); + +#endif +#ifdef MQTT_SERVER + strcpy(globalConfig.mqtt.server, MQTT_SERVER); +#endif +#ifdef MQTT_PORT + globalConfig.mqtt.port = MQTT_PORT; +#endif +#ifdef MQTT_USER + strcpy(globalConfig.mqtt.user, MQTT_USER); +#endif +#ifdef MQTT_PASS + strcpy(globalConfig.mqtt.pass, MQTT_PASS); +#endif + globalConfig.mqtt.discovery = false; + strcpy(globalConfig.mqtt.discovery_prefix, "homeassistant"); + +#ifdef MQTT_FULLTOPIC + strcpy(globalConfig.mqtt.topic, MQTT_FULLTOPIC); +#endif +#ifdef OTA_URL + strcpy(globalConfig.http.ota_url, OTA_URL); +#endif + globalConfig.http.port = 80; + globalConfig.debug.type = 1; + + if (module) + { + module->resetConfig(); + } +} + +void Config::readConfig() +{ + uint16 len; + bool status = false; + uint16 cfg = (EEPROM.read(0) << 8 | EEPROM.read(1)); + if (cfg == GLOBAL_CFG_VERSION) + { + len = (EEPROM.read(2) << 8 | EEPROM.read(3)); + nowCrc = (EEPROM.read(4) << 8 | EEPROM.read(5)); + + if (len > GlobalConfigMessage_size) + { + len = GlobalConfigMessage_size; + } + + uint16_t crc = 0xffff; + uint8_t buffer[GlobalConfigMessage_size]; + for (uint16_t i = 0; i < len; ++i) + { + buffer[i] = EEPROM.read(i + 6); + crc = crcTalbe[(buffer[i] ^ crc) & 15] ^ (crc >> 4); + crc = crcTalbe[((buffer[i] >> 4) ^ crc) & 15] ^ (crc >> 4); + } + if (crc == nowCrc) + { + memset(&globalConfig, 0, sizeof(GlobalConfigMessage)); + pb_istream_t stream = pb_istream_from_buffer(buffer, len); + status = pb_decode(&stream, GlobalConfigMessage_fields, &globalConfig); + if (globalConfig.http.port == 0) + { + globalConfig.http.port = 80; + } + } + } + + if (!status) + { + globalConfig.debug.type = 1; + Debug::AddError(PSTR("readConfig . . . Error")); + resetConfig(); + } + else + { + if (module) + { + module->readConfig(); + } + Debug::AddInfo(PSTR("readConfig . . . OK Len: %d"), len); + } +} + +bool Config::saveConfig(bool isEverySecond) +{ + countdown = 60; + if (module) + { + module->saveConfig(isEverySecond); + } + uint8_t buffer[GlobalConfigMessage_size]; + pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + bool status = pb_encode(&stream, GlobalConfigMessage_fields, &globalConfig); + size_t len = stream.bytes_written; + if (!status) + { + Debug::AddError(PSTR("saveConfig . . . Error")); + return false; + } + else + { + uint16_t crc = crc16(buffer, len); + if (crc == nowCrc) + { + // Debug::AddInfo(PSTR("Check Config CRC . . . Same")); + return true; + } + else + { + nowCrc = crc; + } + } + + EEPROM.write(0, GLOBAL_CFG_VERSION >> 8); + EEPROM.write(1, GLOBAL_CFG_VERSION); + + EEPROM.write(2, len >> 8); + EEPROM.write(3, len); + + EEPROM.write(4, nowCrc >> 8); + EEPROM.write(5, nowCrc); + + for (uint16_t i = 0; i < len; i++) + { + EEPROM.write(i + 6, buffer[i]); + } + EEPROM.commit(); + + Debug::AddInfo(PSTR("saveConfig . . . OK Len: %d"), len); + return true; +} + +void Config::perSecondDo() +{ + countdown--; + if (countdown == 0) + { + saveConfig(Config::isDelay ? false : true); + Config::isDelay = false; + } +} + +void Config::delaySaveConfig(uint8_t second) +{ + Config::isDelay = true; + if (countdown > second) + { + countdown = second; + } +} + +void Config::moduleReadConfig(uint16_t version, uint16_t size, const pb_field_t fields[], void *dest_struct) +{ + if (globalConfig.module_cfg.size == 0 // 没有数据 + || globalConfig.cfg_version != version // 版本不一致 + || globalConfig.module_crc != Config::crc16(globalConfig.module_cfg.bytes, globalConfig.module_cfg.size)) // crc错误 + { + Debug::AddError(PSTR("moduleReadConfig . . . Error %d %d %d"), globalConfig.cfg_version, version, globalConfig.module_cfg.size); + if (module) + { + module->resetConfig(); + } + return; + } + memset(dest_struct, 0, size); + pb_istream_t stream = pb_istream_from_buffer(globalConfig.module_cfg.bytes, globalConfig.module_cfg.size); + bool status = pb_decode(&stream, fields, dest_struct); + if (!status) // 解密失败 + { + if (module) + { + module->resetConfig(); + } + } + else + { + Debug::AddInfo(PSTR("moduleReadConfig . . . OK Len: %d"), globalConfig.module_cfg.size); + } +} + +bool Config::moduleSaveConfig(uint16_t version, uint16_t size, const pb_field_t fields[], const void *src_struct) +{ + uint8_t buffer[size]; + pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + bool status = pb_encode(&stream, fields, src_struct); + if (status) + { + size_t len = stream.bytes_written; + uint16_t crc = Config::crc16(buffer, len); + if (crc != globalConfig.module_crc) + { + globalConfig.cfg_version = version; + globalConfig.module_crc = crc; + globalConfig.module_cfg.size = len; + memcpy(globalConfig.module_cfg.bytes, buffer, len); + //Debug::AddInfo(PSTR("moduleSaveConfig . . . OK Len: %d"), len); + } + } + return status; } \ No newline at end of file diff --git a/lib/esp_framework/src/Debug.cpp b/lib/esp_framework/src/Debug.cpp index b775543..9c1b0a1 100644 --- a/lib/esp_framework/src/Debug.cpp +++ b/lib/esp_framework/src/Debug.cpp @@ -1,151 +1,151 @@ -#include -#include -#include "Config.h" -#include "Debug.h" -#include "Rtc.h" - -uint8_t Debug::webLogIndex = 1; -char Debug::webLog[WEB_LOG_SIZE] = {'\0'}; -IPAddress Debug::ip; - -WiFiUDP Udp; -size_t Debug::strchrspn(const char *str1, int character) -{ - size_t ret = 0; - char *start = (char *)str1; - char *end = strchr(str1, character); - if (end) - ret = end - start; - return ret; -} - -void Debug::GetLog(uint8_t idx, char **entry_pp, uint16_t *len_p) -{ - char *entry_p = NULL; - size_t len = 0; - - if (idx) - { - char *it = webLog; - do - { - uint8_t cur_idx = *it; - it++; - size_t tmp = strchrspn(it, '\1'); - tmp++; // Skip terminating '\1' - if (cur_idx == idx) - { // Found the requested entry - len = tmp; - entry_p = it; - break; - } - it += tmp; - } while (it < webLog + WEB_LOG_SIZE && *it != '\0'); - } - *entry_pp = entry_p; - *len_p = len; -} - -void Debug::Syslog() -{ - if ((2 & globalConfig.debug.type) != 2 || WiFi.status() != WL_CONNECTED || globalConfig.debug.server[0] == '\0' || globalConfig.debug.port == 0) - { - return; - } - - if (!ip) - { - WiFi.hostByName(globalConfig.debug.server, ip); - } - if (Udp.beginPacket(ip, globalConfig.debug.port)) - { - char syslog_preamble[64]; // Hostname + Id - - snprintf_P(syslog_preamble, sizeof(syslog_preamble), PSTR("%s "), UID); - memmove(tmpData + strlen(syslog_preamble), tmpData, sizeof(tmpData) - strlen(syslog_preamble)); - tmpData[sizeof(tmpData) - 1] = '\0'; - memcpy(tmpData, syslog_preamble, strlen(syslog_preamble)); - Udp.write(tmpData, strlen(tmpData)); - Udp.endPacket(); - delay(1); // Add time for UDP handling (#5512) - } -} - -void Debug::AddLog(uint8_t loglevel) -{ - char mxtime[10]; // "13:45:21 " - snprintf_P(mxtime, sizeof(mxtime), PSTR("%02d:%02d:%02d "), Rtc::rtcTime.hour, Rtc::rtcTime.minute, Rtc::rtcTime.second); - - if ((1 & globalConfig.debug.type) == 1) - { - Serial.printf("%s%s\r\n", mxtime, tmpData); - } - if ((8 & globalConfig.debug.type) == 8) - { - Serial1.printf("%s%s\r\n", mxtime, tmpData); - } - - //if (Settings.webserver && (loglevel <= Settings.weblog_level)) - //{ - // Delimited, zero-terminated buffer of log lines. - // Each entry has this format: [index][log data]['\1'] - if ((4 & globalConfig.debug.type) == 4 || loglevel == LOG_LEVEL_ERROR) - { - if (!webLogIndex) - webLogIndex++; // Index 0 is not allowed as it is the end of char string - while (webLogIndex == webLog[0] || // If log already holds the next index, remove it - strlen(webLog) + strlen(tmpData) + 13 > WEB_LOG_SIZE) // 13 = web_log_index + mxtime + '\1' + '\0' - { - char *it = webLog; - it++; // Skip web_log_index - it += strchrspn(it, '\1'); // Skip log line - it++; // Skip delimiting "\1" - memmove(webLog, it, WEB_LOG_SIZE - (it - webLog)); // Move buffer forward to remove oldest log line - } - snprintf_P(webLog, sizeof(webLog), PSTR("%s%c%s%s\1"), webLog, webLogIndex++, mxtime, tmpData); - if (!webLogIndex) - webLogIndex++; // Index 0 is not allowed as it is the end of char string - } - - Syslog(); -} - -void Debug::AddLog(uint8_t loglevel, PGM_P formatP, ...) -{ - va_list arg; - va_start(arg, formatP); - vsnprintf_P(tmpData, sizeof(tmpData), formatP, arg); - va_end(arg); - - AddLog(loglevel); -} - -void Debug::AddInfo(PGM_P formatP, ...) -{ - va_list arg; - va_start(arg, formatP); - vsnprintf_P(tmpData, sizeof(tmpData), formatP, arg); - va_end(arg); - - AddLog(LOG_LEVEL_INFO); -} - -void Debug::AddDebug(PGM_P formatP, ...) -{ - va_list arg; - va_start(arg, formatP); - vsnprintf_P(tmpData, sizeof(tmpData), formatP, arg); - va_end(arg); - - AddLog(LOG_LEVEL_DEBUG); -} - -void Debug::AddError(PGM_P formatP, ...) -{ - va_list arg; - va_start(arg, formatP); - vsnprintf_P(tmpData, sizeof(tmpData), formatP, arg); - va_end(arg); - - AddLog(LOG_LEVEL_ERROR); -} +#include +#include +#include "Config.h" +#include "Debug.h" +#include "Rtc.h" + +uint8_t Debug::webLogIndex = 1; +char Debug::webLog[WEB_LOG_SIZE] = {'\0'}; +IPAddress Debug::ip; + +WiFiUDP Udp; +size_t Debug::strchrspn(const char *str1, int character) +{ + size_t ret = 0; + char *start = (char *)str1; + char *end = strchr(str1, character); + if (end) + ret = end - start; + return ret; +} + +void Debug::GetLog(uint8_t idx, char **entry_pp, uint16_t *len_p) +{ + char *entry_p = NULL; + size_t len = 0; + + if (idx) + { + char *it = webLog; + do + { + uint8_t cur_idx = *it; + it++; + size_t tmp = strchrspn(it, '\1'); + tmp++; // Skip terminating '\1' + if (cur_idx == idx) + { // Found the requested entry + len = tmp; + entry_p = it; + break; + } + it += tmp; + } while (it < webLog + WEB_LOG_SIZE && *it != '\0'); + } + *entry_pp = entry_p; + *len_p = len; +} + +void Debug::Syslog() +{ + if ((2 & globalConfig.debug.type) != 2 || WiFi.status() != WL_CONNECTED || globalConfig.debug.server[0] == '\0' || globalConfig.debug.port == 0) + { + return; + } + + if (!ip) + { + WiFi.hostByName(globalConfig.debug.server, ip); + } + if (Udp.beginPacket(ip, globalConfig.debug.port)) + { + char syslog_preamble[64]; // Hostname + Id + + snprintf_P(syslog_preamble, sizeof(syslog_preamble), PSTR("%s "), UID); + memmove(tmpData + strlen(syslog_preamble), tmpData, sizeof(tmpData) - strlen(syslog_preamble)); + tmpData[sizeof(tmpData) - 1] = '\0'; + memcpy(tmpData, syslog_preamble, strlen(syslog_preamble)); + Udp.write(tmpData, strlen(tmpData)); + Udp.endPacket(); + delay(1); // Add time for UDP handling (#5512) + } +} + +void Debug::AddLog(uint8_t loglevel) +{ + char mxtime[10]; // "13:45:21 " + snprintf_P(mxtime, sizeof(mxtime), PSTR("%02d:%02d:%02d "), Rtc::rtcTime.hour, Rtc::rtcTime.minute, Rtc::rtcTime.second); + + if ((1 & globalConfig.debug.type) == 1) + { + Serial.printf("%s%s\r\n", mxtime, tmpData); + } + if ((8 & globalConfig.debug.type) == 8) + { + Serial1.printf("%s%s\r\n", mxtime, tmpData); + } + + //if (Settings.webserver && (loglevel <= Settings.weblog_level)) + //{ + // Delimited, zero-terminated buffer of log lines. + // Each entry has this format: [index][log data]['\1'] + if ((4 & globalConfig.debug.type) == 4 || loglevel == LOG_LEVEL_ERROR) + { + if (!webLogIndex) + webLogIndex++; // Index 0 is not allowed as it is the end of char string + while (webLogIndex == webLog[0] || // If log already holds the next index, remove it + strlen(webLog) + strlen(tmpData) + 13 > WEB_LOG_SIZE) // 13 = web_log_index + mxtime + '\1' + '\0' + { + char *it = webLog; + it++; // Skip web_log_index + it += strchrspn(it, '\1'); // Skip log line + it++; // Skip delimiting "\1" + memmove(webLog, it, WEB_LOG_SIZE - (it - webLog)); // Move buffer forward to remove oldest log line + } + snprintf_P(webLog, sizeof(webLog), PSTR("%s%c%s%s\1"), webLog, webLogIndex++, mxtime, tmpData); + if (!webLogIndex) + webLogIndex++; // Index 0 is not allowed as it is the end of char string + } + + Syslog(); +} + +void Debug::AddLog(uint8_t loglevel, PGM_P formatP, ...) +{ + va_list arg; + va_start(arg, formatP); + vsnprintf_P(tmpData, sizeof(tmpData), formatP, arg); + va_end(arg); + + AddLog(loglevel); +} + +void Debug::AddInfo(PGM_P formatP, ...) +{ + va_list arg; + va_start(arg, formatP); + vsnprintf_P(tmpData, sizeof(tmpData), formatP, arg); + va_end(arg); + + AddLog(LOG_LEVEL_INFO); +} + +void Debug::AddDebug(PGM_P formatP, ...) +{ + va_list arg; + va_start(arg, formatP); + vsnprintf_P(tmpData, sizeof(tmpData), formatP, arg); + va_end(arg); + + AddLog(LOG_LEVEL_DEBUG); +} + +void Debug::AddError(PGM_P formatP, ...) +{ + va_list arg; + va_start(arg, formatP); + vsnprintf_P(tmpData, sizeof(tmpData), formatP, arg); + va_end(arg); + + AddLog(LOG_LEVEL_ERROR); +} diff --git a/lib/esp_framework/src/Framework.cpp b/lib/esp_framework/src/Framework.cpp index 60964c7..ffd4122 100644 --- a/lib/esp_framework/src/Framework.cpp +++ b/lib/esp_framework/src/Framework.cpp @@ -1,161 +1,161 @@ -#include -#include "Framework.h" -#include "Module.h" -#include "Rtc.h" -#include "Http.h" -#include "Util.h" - -uint16_t Framework::rebootCount = 0; -void Framework::callback(char *topic, byte *payload, unsigned int length) -{ - String str; - for (int i = 0; i < length; i++) - { - str += (char)payload[i]; - } - - Debug::AddInfo(PSTR("Subscribe: %s payload: %s"), topic, str.c_str()); - - String topicStr = String(topic); - if (topicStr.endsWith(F("/OTA"))) - { - Http::OTA(str.endsWith(F(".bin")) ? str : OTA_URL); - } - else if (topicStr.endsWith(F("/restart"))) - { - ESP.reset(); - } - else if (module) - { - module->mqttCallback(topicStr, str); - } - - Led::led(200); -} - -void Framework::connectedCallback() -{ - Mqtt::subscribe(Mqtt::getCmndTopic(F("#"))); - Led::blinkLED(40, 8); - if (module) - { - module->mqttConnected(); - } -} - -void Framework::tickerPerSecondDo() -{ - perSecond++; - if (perSecond == 30) - { - Rtc::rtcReboot.fast_reboot_count = 0; - Rtc::rtcRebootSave(); - } - if (rebootCount == 3) - { - return; - } - Rtc::perSecondDo(); - - Config::perSecondDo(); - Mqtt::perSecondDo(); - module->perSecondDo(); -} - -void Framework::one(unsigned long baud) -{ - Rtc::rtcRebootLoad(); - Rtc::rtcReboot.fast_reboot_count++; - Rtc::rtcRebootSave(); - rebootCount = Rtc::rtcReboot.fast_reboot_count > BOOT_LOOP_OFFSET ? Rtc::rtcReboot.fast_reboot_count - BOOT_LOOP_OFFSET : 0; - - Serial.begin(baud); - EEPROM.begin(GlobalConfigMessage_size + 6); - globalConfig.debug.type = 1; -} - -void Framework::setup() -{ - Debug::AddError(PSTR("--------------------- v%s %s -------------------"), module->getModuleVersion().c_str(), Rtc::GetBuildDateAndTime().c_str()); - if (rebootCount == 1) - { - Config::readConfig(); - module->resetConfig(); - } - else if (rebootCount == 2) - { - Config::resetConfig(); - } - else - { - Config::readConfig(); - } - if (globalConfig.uid[0] != '\0') - { - strcpy(UID, globalConfig.uid); - } - else - { - String mac = WiFi.macAddress(); - mac.replace(":", ""); - mac = mac.substring(6, 12); - sprintf(UID, "%s_%s", module->getModuleName().c_str(), mac.c_str()); - } - Util::strlowr(UID); - - Debug::AddInfo(PSTR("UID: %s"), UID); - // Debug::AddInfo(PSTR("Config Len: %d"), GlobalConfigMessage_size + 6); - - //Config::resetConfig(); - if (MQTT_MAX_PACKET_SIZE == 128) - { - Debug::AddError(PSTR("WRONG PUBSUBCLIENT LIBRARY USED PLEASE INSTALL THE ONE FROM OMG LIB FOLDER")); - } - - if (rebootCount == 3) - { - module = NULL; - - tickerPerSecond = new Ticker(); - tickerPerSecond->attach(1, tickerPerSecondDo); - - Http::begin(); - Wifi::connectWifi(); - } - else - { - Mqtt::setClient(Wifi::wifiClient); - Mqtt::mqttSetConnectedCallback(connectedCallback); - Mqtt::mqttSetLoopCallback(callback); - module->init(); - tickerPerSecond = new Ticker(); - tickerPerSecond->attach(1, tickerPerSecondDo); - Http::begin(); - Wifi::connectWifi(); - Rtc::init(); - } -} - -void Framework::loop() -{ - if (rebootCount == 3) - { - Wifi::loop(); - Http::loop(); - } - else - { - yield(); - Led::loop(); - yield(); - Mqtt::loop(); - yield(); - module->loop(); - yield(); - Wifi::loop(); - yield(); - Http::loop(); - yield(); - Rtc::loop(); - } -} +#include +#include "Framework.h" +#include "Module.h" +#include "Rtc.h" +#include "Http.h" +#include "Util.h" + +uint16_t Framework::rebootCount = 0; +void Framework::callback(char *topic, byte *payload, unsigned int length) +{ + String str; + for (int i = 0; i < length; i++) + { + str += (char)payload[i]; + } + + Debug::AddInfo(PSTR("Subscribe: %s payload: %s"), topic, str.c_str()); + + String topicStr = String(topic); + if (topicStr.endsWith(F("/OTA"))) + { + Http::OTA(str.endsWith(F(".bin")) ? str : OTA_URL); + } + else if (topicStr.endsWith(F("/restart"))) + { + ESP.reset(); + } + else if (module) + { + module->mqttCallback(topicStr, str); + } + + Led::led(200); +} + +void Framework::connectedCallback() +{ + Mqtt::subscribe(Mqtt::getCmndTopic(F("#"))); + Led::blinkLED(40, 8); + if (module) + { + module->mqttConnected(); + } +} + +void Framework::tickerPerSecondDo() +{ + perSecond++; + if (perSecond == 30) + { + Rtc::rtcReboot.fast_reboot_count = 0; + Rtc::rtcRebootSave(); + } + if (rebootCount == 3) + { + return; + } + Rtc::perSecondDo(); + + Config::perSecondDo(); + Mqtt::perSecondDo(); + module->perSecondDo(); +} + +void Framework::one(unsigned long baud) +{ + Rtc::rtcRebootLoad(); + Rtc::rtcReboot.fast_reboot_count++; + Rtc::rtcRebootSave(); + rebootCount = Rtc::rtcReboot.fast_reboot_count > BOOT_LOOP_OFFSET ? Rtc::rtcReboot.fast_reboot_count - BOOT_LOOP_OFFSET : 0; + + Serial.begin(baud); + EEPROM.begin(GlobalConfigMessage_size + 6); + globalConfig.debug.type = 1; +} + +void Framework::setup() +{ + Debug::AddError(PSTR("--------------------- v%s %s -------------------"), module->getModuleVersion().c_str(), Rtc::GetBuildDateAndTime().c_str()); + if (rebootCount == 1) + { + Config::readConfig(); + module->resetConfig(); + } + else if (rebootCount == 2) + { + Config::resetConfig(); + } + else + { + Config::readConfig(); + } + if (globalConfig.uid[0] != '\0') + { + strcpy(UID, globalConfig.uid); + } + else + { + String mac = WiFi.macAddress(); + mac.replace(":", ""); + mac = mac.substring(6, 12); + sprintf(UID, "%s_%s", module->getModuleName().c_str(), mac.c_str()); + } + Util::strlowr(UID); + + Debug::AddInfo(PSTR("UID: %s"), UID); + // Debug::AddInfo(PSTR("Config Len: %d"), GlobalConfigMessage_size + 6); + + //Config::resetConfig(); + if (MQTT_MAX_PACKET_SIZE == 128) + { + Debug::AddError(PSTR("WRONG PUBSUBCLIENT LIBRARY USED PLEASE INSTALL THE ONE FROM OMG LIB FOLDER")); + } + + if (rebootCount == 3) + { + module = NULL; + + tickerPerSecond = new Ticker(); + tickerPerSecond->attach(1, tickerPerSecondDo); + + Http::begin(); + Wifi::connectWifi(); + } + else + { + Mqtt::setClient(Wifi::wifiClient); + Mqtt::mqttSetConnectedCallback(connectedCallback); + Mqtt::mqttSetLoopCallback(callback); + module->init(); + tickerPerSecond = new Ticker(); + tickerPerSecond->attach(1, tickerPerSecondDo); + Http::begin(); + Wifi::connectWifi(); + Rtc::init(); + } +} + +void Framework::loop() +{ + if (rebootCount == 3) + { + Wifi::loop(); + Http::loop(); + } + else + { + yield(); + Led::loop(); + yield(); + Mqtt::loop(); + yield(); + module->loop(); + yield(); + Wifi::loop(); + yield(); + Http::loop(); + yield(); + Rtc::loop(); + } +} diff --git a/lib/esp_framework/src/GlobalConfig.pb.c b/lib/esp_framework/src/GlobalConfig.pb.c index 25eeed2..7c8380f 100644 --- a/lib/esp_framework/src/GlobalConfig.pb.c +++ b/lib/esp_framework/src/GlobalConfig.pb.c @@ -1,82 +1,82 @@ -/* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.3.9.4 at Sat Feb 15 11:23:30 2020. */ - -#include "GlobalConfig.pb.h" - -/* @@protoc_insertion_point(includes) */ -#if PB_PROTO_HEADER_VERSION != 30 -#error Regenerate this file with the current version of nanopb generator. -#endif - - - -const pb_field_t GlobalConfigMessage_fields[9] = { - PB_FIELD( 1, MESSAGE , SINGULAR, STATIC , FIRST, GlobalConfigMessage, wifi, wifi, &WifiConfigMessage_fields), - PB_FIELD( 2, MESSAGE , SINGULAR, STATIC , OTHER, GlobalConfigMessage, http, wifi, &HttpConfigMessage_fields), - PB_FIELD( 3, MESSAGE , SINGULAR, STATIC , OTHER, GlobalConfigMessage, mqtt, http, &MqttConfigMessage_fields), - PB_FIELD( 4, MESSAGE , SINGULAR, STATIC , OTHER, GlobalConfigMessage, debug, mqtt, &DebugConfigMessage_fields), - PB_FIELD( 5, UINT32 , SINGULAR, STATIC , OTHER, GlobalConfigMessage, cfg_version, debug, 0), - PB_FIELD( 6, UINT32 , SINGULAR, STATIC , OTHER, GlobalConfigMessage, module_crc, cfg_version, 0), - PB_FIELD( 7, BYTES , SINGULAR, STATIC , OTHER, GlobalConfigMessage, module_cfg, module_crc, 0), - PB_FIELD( 8, STRING , SINGULAR, STATIC , OTHER, GlobalConfigMessage, uid, module_cfg, 0), - PB_LAST_FIELD -}; - -const pb_field_t WifiConfigMessage_fields[8] = { - PB_FIELD( 1, STRING , SINGULAR, STATIC , FIRST, WifiConfigMessage, ssid, ssid, 0), - PB_FIELD( 2, STRING , SINGULAR, STATIC , OTHER, WifiConfigMessage, pass, ssid, 0), - PB_FIELD( 3, BOOL , SINGULAR, STATIC , OTHER, WifiConfigMessage, is_static, pass, 0), - PB_FIELD( 4, STRING , SINGULAR, STATIC , OTHER, WifiConfigMessage, ip, is_static, 0), - PB_FIELD( 5, STRING , SINGULAR, STATIC , OTHER, WifiConfigMessage, sn, ip, 0), - PB_FIELD( 6, STRING , SINGULAR, STATIC , OTHER, WifiConfigMessage, gw, sn, 0), - PB_FIELD( 7, STRING , SINGULAR, STATIC , OTHER, WifiConfigMessage, ntp, gw, 0), - PB_LAST_FIELD -}; - -const pb_field_t HttpConfigMessage_fields[5] = { - PB_FIELD( 1, UINT32 , SINGULAR, STATIC , FIRST, HttpConfigMessage, port, port, 0), - PB_FIELD( 2, STRING , SINGULAR, STATIC , OTHER, HttpConfigMessage, user, port, 0), - PB_FIELD( 3, STRING , SINGULAR, STATIC , OTHER, HttpConfigMessage, pass, user, 0), - PB_FIELD( 4, STRING , SINGULAR, STATIC , OTHER, HttpConfigMessage, ota_url, pass, 0), - PB_LAST_FIELD -}; - -const pb_field_t MqttConfigMessage_fields[10] = { - PB_FIELD( 1, STRING , SINGULAR, STATIC , FIRST, MqttConfigMessage, server, server, 0), - PB_FIELD( 2, UINT32 , SINGULAR, STATIC , OTHER, MqttConfigMessage, port, server, 0), - PB_FIELD( 3, STRING , SINGULAR, STATIC , OTHER, MqttConfigMessage, user, port, 0), - PB_FIELD( 4, STRING , SINGULAR, STATIC , OTHER, MqttConfigMessage, pass, user, 0), - PB_FIELD( 5, BOOL , SINGULAR, STATIC , OTHER, MqttConfigMessage, retain, pass, 0), - PB_FIELD( 6, STRING , SINGULAR, STATIC , OTHER, MqttConfigMessage, topic, retain, 0), - PB_FIELD( 7, BOOL , SINGULAR, STATIC , OTHER, MqttConfigMessage, discovery, topic, 0), - PB_FIELD( 8, STRING , SINGULAR, STATIC , OTHER, MqttConfigMessage, discovery_prefix, discovery, 0), - PB_FIELD( 9, UINT32 , SINGULAR, STATIC , OTHER, MqttConfigMessage, interval, discovery_prefix, 0), - PB_LAST_FIELD -}; - -const pb_field_t DebugConfigMessage_fields[4] = { - PB_FIELD( 1, UINT32 , SINGULAR, STATIC , FIRST, DebugConfigMessage, type, type, 0), - PB_FIELD( 2, STRING , SINGULAR, STATIC , OTHER, DebugConfigMessage, server, type, 0), - PB_FIELD( 3, UINT32 , SINGULAR, STATIC , OTHER, DebugConfigMessage, port, server, 0), - PB_LAST_FIELD -}; - - -/* Check that field information fits in pb_field_t */ -#if !defined(PB_FIELD_32BIT) -/* If you get an error here, it means that you need to define PB_FIELD_32BIT - * compile-time option. You can do that in pb.h or on compiler command line. - * - * The reason you need to do this is that some of your messages contain tag - * numbers or field sizes that are larger than what can fit in 8 or 16 bit - * field descriptors. - */ -PB_STATIC_ASSERT((pb_membersize(GlobalConfigMessage, wifi) < 65536 && pb_membersize(GlobalConfigMessage, http) < 65536 && pb_membersize(GlobalConfigMessage, mqtt) < 65536 && pb_membersize(GlobalConfigMessage, debug) < 65536 && pb_membersize(GlobalConfigMessage, module_cfg) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_GlobalConfigMessage_WifiConfigMessage_HttpConfigMessage_MqttConfigMessage_DebugConfigMessage) -#endif - -#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) -#error Field descriptor for GlobalConfigMessage.module_cfg is too large. Define PB_FIELD_16BIT to fix this. -#endif - - -/* @@protoc_insertion_point(eof) */ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.9.4 at Sat Feb 15 11:23:30 2020. */ + +#include "GlobalConfig.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t GlobalConfigMessage_fields[9] = { + PB_FIELD( 1, MESSAGE , SINGULAR, STATIC , FIRST, GlobalConfigMessage, wifi, wifi, &WifiConfigMessage_fields), + PB_FIELD( 2, MESSAGE , SINGULAR, STATIC , OTHER, GlobalConfigMessage, http, wifi, &HttpConfigMessage_fields), + PB_FIELD( 3, MESSAGE , SINGULAR, STATIC , OTHER, GlobalConfigMessage, mqtt, http, &MqttConfigMessage_fields), + PB_FIELD( 4, MESSAGE , SINGULAR, STATIC , OTHER, GlobalConfigMessage, debug, mqtt, &DebugConfigMessage_fields), + PB_FIELD( 5, UINT32 , SINGULAR, STATIC , OTHER, GlobalConfigMessage, cfg_version, debug, 0), + PB_FIELD( 6, UINT32 , SINGULAR, STATIC , OTHER, GlobalConfigMessage, module_crc, cfg_version, 0), + PB_FIELD( 7, BYTES , SINGULAR, STATIC , OTHER, GlobalConfigMessage, module_cfg, module_crc, 0), + PB_FIELD( 8, STRING , SINGULAR, STATIC , OTHER, GlobalConfigMessage, uid, module_cfg, 0), + PB_LAST_FIELD +}; + +const pb_field_t WifiConfigMessage_fields[8] = { + PB_FIELD( 1, STRING , SINGULAR, STATIC , FIRST, WifiConfigMessage, ssid, ssid, 0), + PB_FIELD( 2, STRING , SINGULAR, STATIC , OTHER, WifiConfigMessage, pass, ssid, 0), + PB_FIELD( 3, BOOL , SINGULAR, STATIC , OTHER, WifiConfigMessage, is_static, pass, 0), + PB_FIELD( 4, STRING , SINGULAR, STATIC , OTHER, WifiConfigMessage, ip, is_static, 0), + PB_FIELD( 5, STRING , SINGULAR, STATIC , OTHER, WifiConfigMessage, sn, ip, 0), + PB_FIELD( 6, STRING , SINGULAR, STATIC , OTHER, WifiConfigMessage, gw, sn, 0), + PB_FIELD( 7, STRING , SINGULAR, STATIC , OTHER, WifiConfigMessage, ntp, gw, 0), + PB_LAST_FIELD +}; + +const pb_field_t HttpConfigMessage_fields[5] = { + PB_FIELD( 1, UINT32 , SINGULAR, STATIC , FIRST, HttpConfigMessage, port, port, 0), + PB_FIELD( 2, STRING , SINGULAR, STATIC , OTHER, HttpConfigMessage, user, port, 0), + PB_FIELD( 3, STRING , SINGULAR, STATIC , OTHER, HttpConfigMessage, pass, user, 0), + PB_FIELD( 4, STRING , SINGULAR, STATIC , OTHER, HttpConfigMessage, ota_url, pass, 0), + PB_LAST_FIELD +}; + +const pb_field_t MqttConfigMessage_fields[10] = { + PB_FIELD( 1, STRING , SINGULAR, STATIC , FIRST, MqttConfigMessage, server, server, 0), + PB_FIELD( 2, UINT32 , SINGULAR, STATIC , OTHER, MqttConfigMessage, port, server, 0), + PB_FIELD( 3, STRING , SINGULAR, STATIC , OTHER, MqttConfigMessage, user, port, 0), + PB_FIELD( 4, STRING , SINGULAR, STATIC , OTHER, MqttConfigMessage, pass, user, 0), + PB_FIELD( 5, BOOL , SINGULAR, STATIC , OTHER, MqttConfigMessage, retain, pass, 0), + PB_FIELD( 6, STRING , SINGULAR, STATIC , OTHER, MqttConfigMessage, topic, retain, 0), + PB_FIELD( 7, BOOL , SINGULAR, STATIC , OTHER, MqttConfigMessage, discovery, topic, 0), + PB_FIELD( 8, STRING , SINGULAR, STATIC , OTHER, MqttConfigMessage, discovery_prefix, discovery, 0), + PB_FIELD( 9, UINT32 , SINGULAR, STATIC , OTHER, MqttConfigMessage, interval, discovery_prefix, 0), + PB_LAST_FIELD +}; + +const pb_field_t DebugConfigMessage_fields[4] = { + PB_FIELD( 1, UINT32 , SINGULAR, STATIC , FIRST, DebugConfigMessage, type, type, 0), + PB_FIELD( 2, STRING , SINGULAR, STATIC , OTHER, DebugConfigMessage, server, type, 0), + PB_FIELD( 3, UINT32 , SINGULAR, STATIC , OTHER, DebugConfigMessage, port, server, 0), + PB_LAST_FIELD +}; + + +/* Check that field information fits in pb_field_t */ +#if !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_32BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in 8 or 16 bit + * field descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(GlobalConfigMessage, wifi) < 65536 && pb_membersize(GlobalConfigMessage, http) < 65536 && pb_membersize(GlobalConfigMessage, mqtt) < 65536 && pb_membersize(GlobalConfigMessage, debug) < 65536 && pb_membersize(GlobalConfigMessage, module_cfg) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_GlobalConfigMessage_WifiConfigMessage_HttpConfigMessage_MqttConfigMessage_DebugConfigMessage) +#endif + +#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) +#error Field descriptor for GlobalConfigMessage.module_cfg is too large. Define PB_FIELD_16BIT to fix this. +#endif + + +/* @@protoc_insertion_point(eof) */ diff --git a/lib/esp_framework/src/Http.cpp b/lib/esp_framework/src/Http.cpp index 2ebac5b..eef8410 100644 --- a/lib/esp_framework/src/Http.cpp +++ b/lib/esp_framework/src/Http.cpp @@ -1,953 +1,953 @@ -#include -#include -#include -#include -#include "StreamString.h" -#include -#include "Http.h" -#include "Module.h" -#include "Rtc.h" - -ESP8266WebServer *Http::server; -bool Http::isBegin = false; -String Http::updaterError; - -void Http::handleRoot() -{ - if (captivePortal()) - { - return; - } - if (!checkAuth()) - { - return; - } - - server->setContentLength(CONTENT_LENGTH_UNKNOWN); - server->send(200, F("text/html"), ""); - String radioJs = ""); - - page += F("
"); - page += F("

{title}模块

"); - page.replace(F("{title}"), module ? module->getModuleCNName() : F("修复模式    ")); - - page += F(""); - server->sendContent(page); - - page = F("
"); - - // TAB 1 Start - uint8_t mode = WiFi.getMode(); - page += F("
"); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F("
WiFi状态
主机名{UID}
WiFi模式"); - if (mode == WIFI_STA) - { - page += F("STA"); - } - else if (mode == WIFI_AP) - { - page += F("AP"); - } - else if (mode == WIFI_AP_STA) - { - page += F("AP STA"); - } - page += F("
SSID{SSID}
RSSI{RSSI}dBm
开机时间{uptime}
空闲内存{free_mem} kB
IP地址{localIP}
DHCP{DHCP}
"); - page += F("
"); - page.replace(F("{UID}"), UID); - page.replace(F("{SSID}"), WiFi.SSID()); - page.replace(F("{RSSI}"), String(WiFi.RSSI())); - page.replace(F("{uptime}"), Rtc::msToHumanString(millis())); - page.replace(F("{free_mem}"), String(ESP.getFreeHeap() / 1024)); - page.replace(F("{localIP}"), WiFi.localIP().toString()); - page.replace(F("{DHCP}"), (!globalConfig.wifi.is_static ? F("DHCP") : F("静态IP"))); - // TAB 1 End - - server->sendContent(page); - - // TAB 2 Start - page = F("
"); - page += F("
"); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F("
WiFi名称信号
WiFi名称
WiFi密码
"); - page += F(""); - if (!WiFi.isConnected()) - { - radioJs += "scanWifi();"; - } - - page += F("
"); - page += F(""); - page += F(""); - radioJs += F("setRadioValue('dhcp', '{v}');"); - radioJs.replace(F("{v}"), globalConfig.wifi.is_static ? F("2") : F("1")); - - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F("
WIFI高级设置
DHCP"); - page += F("    "); - page += F(""); - page += F("
静态IP
子网掩码
网关
"); - page += F(""); - page.replace(F("{ip}"), globalConfig.wifi.ip); - page.replace(F("{sn}"), globalConfig.wifi.sn); - page.replace(F("{gw}"), globalConfig.wifi.gw); - - page += F("
"); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F("
MQTT设置
地址
端口    0为不启动mqtt
用户名
密码
主题
心跳上报间隔 秒  0为不上报
retain"); - page += F("    "); - page += F("
除非你知道它是干嘛的。"); - page += F("
状态{mqttconnected}
"); - page.replace(F("{server}"), globalConfig.mqtt.server); - page.replace(F("{port}"), String(globalConfig.mqtt.port)); - page.replace(F("{user}"), globalConfig.mqtt.user); - page.replace(F("{pass}"), globalConfig.mqtt.pass); - page.replace(F("{topic}"), globalConfig.mqtt.topic); - page.replace(F("{interval}"), String(globalConfig.mqtt.interval)); - radioJs += F("setRadioValue('retain', '{v}');"); - radioJs.replace(F("{v}"), globalConfig.mqtt.retain ? F("1") : F("0")); - page.replace(F("{mqttconnected}"), (Mqtt::mqttClient.connected() ? F("已连接") : F("未连接"))); - - page += F("
"); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - if (globalConfig.mqtt.discovery) - { - radioJs += F("id('discovery_btn').setAttribute('class', 'btn-danger');id('discovery_btn').innerHTML='关闭MQTT自动发现';"); - } - page += F("
MQTT自动发现
自发现状态{discovery}
自发现前缀
"); - page += F("
"); - page.replace(F("{discovery}"), globalConfig.mqtt.discovery ? F("已启动") : F("未启动")); - page.replace(F("{prefix}"), globalConfig.mqtt.discovery_prefix); - // TAB 2 End - - // TAB 3 Start - page += F("
"); - server->sendContent(page); - - if (module) - { - module->httpHtml(server); - } - - page = F("
"); - page += F(""); - page += F(""); - page += F(""); - page.replace(F("{UID}"), UID); - if ((1 & globalConfig.debug.type) == 1) - { - radioJs += F("setRadioValue('log_serial', '1');"); - } - if ((2 & globalConfig.debug.type) == 2) - { - radioJs += F("setRadioValue('log_syslog', '1');"); - } - if ((4 & globalConfig.debug.type) == 4) - { - radioJs += F("setRadioValue('log_web', '1');"); - } - if ((8 & globalConfig.debug.type) == 8) - { - radioJs += F("setRadioValue('log_serial1', '1');"); - } - - page += F(""); - page.replace(F("{server}"), globalConfig.debug.server); - page.replace(F("{port}"), String(globalConfig.debug.port)); - - page += F(""); - - page += F(""); - page += F("
模块设置
主机名 具有唯一性,留空默认
日志输出"); - page += F("    "); - page += F("    "); - page += F("    "); - page += F("    "); - page += F("
syslog服务器"); - page += F(" : "); - page += F("
NTP服务器"); - page += F(" 建议在获取时间失败时才填写"); - page.replace(F("{ntp}"), globalConfig.wifi.ntp); - page += F("
"); - - page += F("
"); - page += F(""); - page += F(""); - page += F("
"); - page += F("
"); - // TAB 3 End - server->sendContent(page); - - // TAB 4 Start - page = F("
"); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F("
硬件参数
ESP芯片ID{getChipId}
Flash芯片 ID{getFlashChipId}
Flash大小{getFlashChipRealSize} kB
固件Flash大小{getFlashChipSize} kB
固件大小{getSketchSize} kB
空闲程序空间{getFreeSketchSpace} kB
内核和SDK版本" ARDUINO_ESP8266_RELEASE "/{getSdkVersion}
重启原因{resetReason}
MAC地址{macAddress}
"); - page.replace(F("{getChipId}"), String(ESP.getChipId())); - page.replace(F("{getFlashChipId}"), String(ESP.getFlashChipId())); - page.replace(F("{getFlashChipSize}"), String(ESP.getFlashChipSize() / 1024)); - page.replace(F("{getFlashChipRealSize}"), String(ESP.getFlashChipRealSize() / 1024)); - page.replace(F("{getSketchSize}"), String(ESP.getSketchSize() / 1024)); - page.replace(F("{getFreeSketchSpace}"), String(ESP.getFreeSketchSpace() / 1024)); - page.replace(F("{getSdkVersion}"), ESP.getSdkVersion()); - page.replace(F("{macAddress}"), WiFi.macAddress()); - page.replace(F("{resetReason}"), ESP.getResetReason()); - - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F(""); - page += F("
固件升级
当前版本v{v}
编译时间{v1}
选择文件

"); - page += F(""); - page += F("
OTA更新
OTA地址
"); - page += F("
"); - page.replace(F("{v}"), module ? module->getModuleVersion() : F("0")); - page.replace(F("{v1}"), Rtc::GetBuildDateAndTime()); - page.replace(F("{ota_url}"), globalConfig.http.ota_url); - // TAB 4 End - server->sendContent(page); - - // TAB 5 Start - page = F("
"); - page += F("
"); - page += F(""); - page += F("
"); - // TAB 5 End - - page += F("
"); - radioJs += F(""); - - page += radioJs; - - page += F("
"); - - server->sendContent(page); -} - -void Http::handleMqtt() -{ - if (!checkAuth()) - { - return; - } - String topic = server->arg(F("mqtt_topic")); - if (topic.length() == 0) - { - Http::server->send(200, F("text/html"), F("{\"code\":0,\"msg\":\"MQTT主题不能为空\"}")); - return; - } - if (topic.indexOf("%prefix%/") == 0) - { - Http::server->send(200, F("text/html"), F("{\"code\":0,\"msg\":\"MQTT主题必须包含【%prefix%/】\"}")); - return; - } - strcpy(globalConfig.mqtt.server, server->arg(F("mqtt_server")).c_str()); - globalConfig.mqtt.port = server->arg(F("mqtt_port")).toInt(); - globalConfig.mqtt.retain = server->arg(F("retain")) == F("1"); - strcpy(globalConfig.mqtt.user, server->arg(F("mqtt_username")).c_str()); - strcpy(globalConfig.mqtt.pass, server->arg(F("mqtt_password")).c_str()); - strcpy(globalConfig.mqtt.topic, topic.c_str()); - globalConfig.mqtt.interval = server->arg(F("interval")).toInt(); - Config::saveConfig(); - Mqtt::setTopic(); - - if (Mqtt::mqttClient.connected()) - { - Mqtt::mqttClient.disconnect(); - } - - if (Mqtt::mqttConnect()) - { - Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"设置MQTT服务器成功,已连接。\",\"data\":{\"mqttconnected\":\"已连接\"}}")); - } - else - { - Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"设置MQTT服务器成功,未连接。\",\"data\":{\"mqttconnected\":\"未连接\"}}")); - } -} - -void Http::handledhcp() -{ - if (!checkAuth()) - { - return; - } - String ip = server->arg(F("static_ip")); - String netmask = server->arg(F("static_netmask")); - String gateway = server->arg(F("static_gateway")); - if (!Wifi::isIp(ip)) - { - Http::server->send(200, F("text/html"), F("{\"code\":0,\"msg\":\"IP地址错误\"}")); - return; - } - if (!Wifi::isIp(netmask)) - { - Http::server->send(200, F("text/html"), F("{\"code\":0,\"msg\":\"掩码地址错误\"}")); - return; - } - if (!Wifi::isIp(gateway)) - { - Http::server->send(200, F("text/html"), F("{\"code\":0,\"msg\":\"网关地址错误\"}")); - return; - } - - IPAddress static_ip; - IPAddress static_sn; - IPAddress static_gw; - static_ip.fromString(ip); - static_sn.fromString(netmask); - static_gw.fromString(gateway); - - if (!(static_ip.isV4() && static_sn.isV4() && (!static_gw.isSet() || static_gw.isV4()))) - { - Http::server->send(200, F("text/html"), F("{\"code\":0,\"msg\":\"IP地址或者网关错误\"}")); - return; - } - - if ((static_ip.v4() & static_sn.v4()) != (static_gw.v4() & static_sn.v4())) - { - Http::server->send(200, F("text/html"), F("{\"code\":0,\"msg\":\"网段错误\"}")); - return; - } - - bool old = globalConfig.wifi.is_static; - globalConfig.wifi.is_static = server->arg(F("dhcp")).equals(F("2")); - strcpy(globalConfig.wifi.ip, ip.c_str()); - strcpy(globalConfig.wifi.sn, netmask.c_str()); - strcpy(globalConfig.wifi.gw, gateway.c_str()); - Config::saveConfig(); - - if (old != globalConfig.wifi.is_static) - { - Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"设置DHCP信息成功,重启后生效\"}")); - } - else - { - Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"设置DHCP信息成功\"}")); - } -} - -void Http::handleScanWifi() -{ - if (!checkAuth()) - { - return; - } - int n = WiFi.scanNetworks(); - if (n == 0) - { - Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"\",\"data\":{\"list\":[]}}")); - //Http::server->send(200, F("text/html"), F("{\"code\":0,\"msg\":\"找不到网络,请重新试试。\"}")); - return; - } - - //sort networks - int indices[n]; - for (int i = 0; i < n; i++) - { - indices[i] = i; - } - - // RSSI排序 - for (int i = 0; i < n; i++) - { - for (int j = i + 1; j < n; j++) - { - if (WiFi.RSSI(indices[j]) > WiFi.RSSI(indices[i])) - { - std::swap(indices[i], indices[j]); - } - } - } - - // 删除重复项(必须对RSSI进行排序) - String cssid; - for (int i = 0; i < n; i++) - { - if (indices[i] == -1) - continue; - cssid = WiFi.SSID(indices[i]); - for (int j = i + 1; j < n; j++) - { - if (cssid == WiFi.SSID(indices[j])) - { - indices[j] = -1; // set dup aps to index -1 - } - } - } - - String data = ""; - for (int i = 0; i < n; i++) - { - if (indices[i] == -1) - continue; // skip dups - int RSSI = WiFi.RSSI(indices[i]); - int quality; - if (RSSI <= -100) - { - quality = 0; - } - else if (RSSI >= -50) - { - quality = 100; - } - else - { - quality = 2 * (RSSI + 100); - } - int _minimumQuality = -1; - if (_minimumQuality == -1 || _minimumQuality < quality) - { - data += ",{\"name\":\"" + WiFi.SSID(indices[i]) + "\",\"rssi\":\"" + RSSI + "\",\"quality\":" + quality + ",\"type\":" + WiFi.encryptionType(indices[i]) + "}"; - } - } - - Http::server->send(200, F("text/html"), "{\"code\":1,\"msg\":\"\",\"data\":{\"list\":[" + data.substring(1) + "]}}"); -} - -void Http::handleWifi() -{ - if (!checkAuth()) - { - return; - } - String wifi = server->arg(F("wifi_ssid")); - if (wifi == "") - { - Http::server->send(200, F("text/html"), F("{\"code\":0,\"msg\":\"WiFi名称不能为空。\"}")); - return; - } - String password = server->arg(F("wifi_password")); - - if (WiFi.getMode() == WIFI_STA) - { - strcpy(globalConfig.wifi.ssid, wifi.c_str()); - strcpy(globalConfig.wifi.pass, password.c_str()); - Config::saveConfig(); - Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"设置WiFi信息成功,重启模块(手动)使用新的Wifi信息连接。\"}")); - } - else - { - Wifi::tryConnect(wifi, password); - Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"尝试将ESP连接到网络。 如果失败,请重新连接到AP再试一次。\"}")); - } -} - -void Http::handleDiscovery() -{ - if (!checkAuth()) - { - return; - } - strcpy(globalConfig.mqtt.discovery_prefix, server->arg(F("discovery_prefix")).c_str()); - globalConfig.mqtt.discovery = !globalConfig.mqtt.discovery; - Config::saveConfig(); - - if (module) - { - module->mqttDiscovery(globalConfig.mqtt.discovery); - } - if (globalConfig.mqtt.discovery) - { - Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"已经打开MQTT自发现。\",\"data\":{\"discovery\":1}}")); - } - else - { - Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"已经关闭MQTT自发现。\",\"data\":{\"discovery\":0}}")); - } -} - -void Http::handleRestart() -{ - if (!checkAuth()) - { - return; - } - Config::saveConfig(); - Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"设备正在重启 . . .\"}")); - delay(200); - - Led::blinkLED(400, 4); - ESP.restart(); -} - -void Http::handleReset() -{ - if (!checkAuth()) - { - return; - } - Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"正在重置模块 . . . 设备将会重启。\"}")); - delay(200); - - Led::blinkLED(400, 4); - - Config::resetConfig(); - Config::saveConfig(); - ESP.restart(); -} - -void Http::handleOTA() -{ - if (!checkAuth()) - { - return; - } - strcpy(globalConfig.http.ota_url, server->arg(F("ota_url")).c_str()); - Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"如果成功后设备会重启 . . . \"}")); - Http::OTA(String(globalConfig.http.ota_url)); -} - -void Http::handleNotFound() -{ - if (captivePortal()) - { - return; - } - String message = F("File Not Found\n\n"); - message += F("URI: "); - message += server->uri(); - message += F("\nMethod: "); - message += (server->method() == HTTP_GET) ? F("GET") : F("POST"); - message += F("\nArguments: "); - message += server->args(); - message += F("\n"); - for (uint8_t i = 0; i < server->args(); i++) - { - message += " " + server->argName(i) + ": " + server->arg(i) + "\n"; - } - server->sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate")); - server->sendHeader(F("Pragma"), F("no-cache")); - server->sendHeader(F("Expires"), F("-1")); - server->sendHeader(F("Content-Length"), String(message.length())); - server->send(404, F("text/plain"), message); -} - -void Http::handleGetStatus() -{ - if (!checkAuth()) - { - return; - } - bool cflg = true; - uint8_t counter = 0; - if (server->hasArg(F("i"))) - { - counter = server->arg(F("i")).toInt(); - } - - server->setContentLength(CONTENT_LENGTH_UNKNOWN); - server->send(200, F("text/html"), ""); - String data = F("{\"code\":1,\"msg\":\"\",\"data\":{"); - data += F("\"mqttconnected\":\""); - data += Mqtt::mqttClient.connected() ? F("已连接") : F("未连接"); - - data += F("\",\"discovery\":"); - data += globalConfig.mqtt.discovery ? 1 : 0; - - data += F(",\"uptime\":\""); - data += Rtc::msToHumanString(millis()); - - data += F("\",\"ip\":\""); - if (Wifi::configPortalStart == 0 && WiFi.isConnected()) - { - data += WiFi.localIP().toString(); - } - else - { - data += F(""); - } - - data += F("\",\"free_mem\":"); - data += ESP.getFreeHeap() / 1024; - - if (module) - { - String tmp = module->httpGetStatus(server); - if (tmp.length() > 0) - { - data += F(","); - data += tmp; - } - } - - data += F(",\"logindex\":"); - data += Debug::webLogIndex; - - data += F(",\"log\":\""); - server->sendContent(data); - - if (counter != Debug::webLogIndex) - { - if (!counter) - { - counter = Debug::webLogIndex; - cflg = false; - } - do - { - char *tmp; - uint16_t len; - Debug::GetLog(counter, &tmp, &len); - if (len) - { - if (cflg) - { - server->sendContent("\\n"); - } - - size_t j = 0; - for (size_t i = 0; i < len - 1; i++) - { - char each = tmp[i]; - if (each == '\\' || each == '"') - { - tmpData[j++] = '\\'; - tmpData[j++] = each; - } - else if (each == '\b') - { - tmpData[j++] = '\\'; - tmpData[j++] = 'b'; - } - else if (each == '\f') - { - tmpData[j++] = '\\'; - tmpData[j++] = 'f'; - } - else if (each == '\n') - { - tmpData[j++] = '\\'; - tmpData[j++] = 'n'; - } - else if (each == '\r') - { - tmpData[j++] = '\\'; - tmpData[j++] = 'r'; - } - else if (each == '\t') - { - tmpData[j++] = '\\'; - tmpData[j++] = 't'; - } - else - { - tmpData[j++] = each; - } - } - tmpData[j++] = '\0'; - - server->sendContent(tmpData); - cflg = true; - } - counter++; - if (!counter) - { - counter++; - } // Skip log index 0 as it is not allowed - } while (counter != Debug::webLogIndex); - } - - server->sendContent(F("\"}}")); -} - -void Http::handleUpdate() -{ - // handler for the /update form POST (once file upload finishes) - Http::server->on("/update", HTTP_POST, [&]() { - if (!checkAuth()) - { - return; - } - if (Update.hasError()) - { - Debug::AddError(PSTR("Update error: %s"), Http::updaterError.c_str()); - Http::server->send(200, F("text/html"), String(F("Update error: ")) + Http::updaterError); - } - else - { - Config::saveConfig(); - Http::server->client().setNoDelay(true); - Http::server->send_P(200, PSTR("text/html"), PSTR("升级成功!正在重启 . . .")); - delay(100); - Http::server->client().stop(); - ESP.restart(); - } }, [&]() { - HTTPUpload &upload = Http::server->upload(); - if (upload.status == UPLOAD_FILE_START) - { - Http::updaterError = String(); - if (globalConfig.http.user[0] != 0 && globalConfig.http.pass[0] != 0 && server->client().localIP().toString() != "192.168.4.1" && !server->authenticate(globalConfig.http.user, globalConfig.http.pass)) - { - Debug::AddInfo(PSTR("Unauthenticated Update")); - return; - } - WiFiUDP::stopAll(); - Debug::AddInfo(PSTR("Update: %s"), upload.filename.c_str()); - uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; - if (!Update.begin(maxSketchSpace, U_FLASH))//start with max available size - { - StreamString str; - Update.printError(str); - Http::updaterError = str.c_str(); - } - } - else if (upload.status == UPLOAD_FILE_WRITE && !Http::updaterError.length()) - { - if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) - { - StreamString str; - Update.printError(str); - Http::updaterError = str.c_str(); - } - } - else if (upload.status == UPLOAD_FILE_END && !Http::updaterError.length()) - { - if (Update.end(true)) - { - Debug::AddInfo(PSTR("Update Success: %u Rebooting..."), upload.totalSize); - } - else - { - StreamString str; - Update.printError(str); - Http::updaterError = str.c_str(); - } - } - else if (upload.status == UPLOAD_FILE_ABORTED) - { - Update.end(); - Debug::AddInfo(PSTR("Update was aborted")); - } - delay(0); }); -} - -void Http::begin() -{ - if (isBegin) - { - return; - } - isBegin = true; - server = new ESP8266WebServer(); - - server->on(F("/"), handleRoot); - server->on(F("/mqtt"), handleMqtt); - server->on(F("/dhcp"), handledhcp); - server->on(F("/scan_wifi"), handleScanWifi); - server->on(F("/wifi"), handleWifi); - server->on(F("/discovery"), handleDiscovery); - server->on(F("/restart"), handleRestart); - server->on(F("/reset"), handleReset); - server->on(F("/module_setting"), handleModuleSetting); - server->on(F("/ota"), handleOTA); - server->on(F("/get_status"), handleGetStatus); - server->onNotFound(handleNotFound); - handleUpdate(); - - if (module) - { - module->httpAdd(server); - } - MDNS.begin(UID); - server->begin(globalConfig.http.port); - Debug::AddInfo(PSTR("HTTP server started port: %d"), globalConfig.http.port); -} - -void Http::stop() -{ - if (!isBegin) - { - return; - } - server->stop(); - Debug::AddInfo(PSTR("HTTP server stoped")); -} - -void Http::loop() -{ - if (isBegin) - { - server->handleClient(); - MDNS.update(); - } -} - -bool Http::captivePortal() -{ - if (!Wifi::isIp(server->hostHeader())) - { - //Debug::AddInfo(PSTR("Request redirected to captive portal")); - server->sendHeader(F("Location"), String(F("http://")) + server->client().localIP().toString(), true); - server->send(302, F("text/plain"), ""); // Empty content inhibits Content-length header so we have to close the socket ourselves. - server->client().stop(); // Stop is needed because we sent no content length - return true; - } - return false; -} - -void Http::handleModuleSetting() -{ - if (!checkAuth()) - { - return; - } - if (Http::server->hasArg(F("log_serial")) || Http::server->hasArg(F("log_serial1")) || Http::server->hasArg(F("log_syslog")) || Http::server->hasArg(F("log_web"))) - { - int t = 0; - if (Http::server->arg(F("log_serial")).equals(F("1"))) - { - t = t | 1; - } - if (Http::server->arg(F("log_serial1")).equals(F("1"))) - { - t = t | 8; - } - if (Http::server->arg(F("log_web")).equals(F("1"))) - { - t = t | 4; - } - - String log_syslog = Http::server->arg(F("log_syslog")); - if (log_syslog.equals(F("1"))) - { - t = t | 2; - String log_syslog_host = Http::server->arg(F("log_syslog_host")); - String log_syslog_port = Http::server->arg(F("log_syslog_port")); - if (log_syslog_host.length() == 0) - { - Http::server->send(200, F("text/html"), F("{\"code\":0,\"msg\":\"syslog服务器不能为空\"}")); - return; - } - strcpy(globalConfig.debug.server, log_syslog_host.c_str()); - globalConfig.debug.port = log_syslog_port.toInt(); - WiFi.hostByName(globalConfig.debug.server, Debug::ip); - } - - if (Http::server->arg(F("log_serial1")).equals(F("1"))) - { - Serial1.begin(115200); - } - globalConfig.debug.type = t; - } - - String ntp = Http::server->arg(F("ntp")); - if ((globalConfig.wifi.ntp[0] == '\0' && ntp.length() > 0) || (globalConfig.wifi.ntp[0] != '\0' && ntp.length() == 0)) - { - strcpy(globalConfig.wifi.ntp, ntp.c_str()); - Rtc::init(); - } - else - { - strcpy(globalConfig.wifi.ntp, ntp.c_str()); - } - - String uid = Http::server->arg(F("uid")); - strcpy(globalConfig.uid, uid.c_str()); - Config::saveConfig(); - if (uid.length() == 0 || strncmp(globalConfig.uid, UID, uid.length()) != 0) - { - if (globalConfig.mqtt.discovery && module) - { - module->mqttDiscovery(false); - } - Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"修改了重要配置 . . . 正在重启中。\"}")); - Led::blinkLED(400, 4); - ESP.restart(); - } - else - { - Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"已经修改成功\"}")); - } -} - -bool Http::checkAuth() -{ - if (globalConfig.http.user[0] != 0 && globalConfig.http.pass[0] != 0 && server->client().localIP().toString() != "192.168.4.1") - { - if (!server->authenticate(globalConfig.http.user, globalConfig.http.pass)) - { - server->requestAuthentication(); - return false; - } - } - return true; -} - -void Http::OTA(String url) -{ - if (url.indexOf(F("%04d")) != -1) - { - url.replace("%04d", String(ESP.getChipId() & 0x1fff)); - } - else if (url.indexOf(F("%d")) != -1) - { - url.replace("%d", String(ESP.getChipId())); - } - url.replace(F("%hostname%"), UID); - url.replace(F("%module%"), module ? module->getModuleName() : ""); - - Config::saveConfig(); - Debug::AddInfo(PSTR("OTA Url: %s"), url.c_str()); - Led::blinkLED(200, 5); - WiFiClient OTAclient; - if (ESPhttpUpdate.update(OTAclient, url, (module ? module->getModuleVersion() : "")) == HTTP_UPDATE_FAILED) - { - Debug::AddError(PSTR("HTTP_UPDATE_FAILD Error (%d): %s"), ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); - } -} +#include +#include +#include +#include +#include "StreamString.h" +#include +#include "Http.h" +#include "Module.h" +#include "Rtc.h" + +ESP8266WebServer *Http::server; +bool Http::isBegin = false; +String Http::updaterError; + +void Http::handleRoot() +{ + if (captivePortal()) + { + return; + } + if (!checkAuth()) + { + return; + } + + server->setContentLength(CONTENT_LENGTH_UNKNOWN); + server->send(200, F("text/html"), ""); + String radioJs = ""); + + page += F("
"); + page += F("

{title}模块

"); + page.replace(F("{title}"), module ? module->getModuleCNName() : F("修复模式    ")); + + page += F(""); + server->sendContent(page); + + page = F("
"); + + // TAB 1 Start + uint8_t mode = WiFi.getMode(); + page += F("
"); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F("
WiFi状态
主机名{UID}
WiFi模式"); + if (mode == WIFI_STA) + { + page += F("STA"); + } + else if (mode == WIFI_AP) + { + page += F("AP"); + } + else if (mode == WIFI_AP_STA) + { + page += F("AP STA"); + } + page += F("
SSID{SSID}
RSSI{RSSI}dBm
开机时间{uptime}
空闲内存{free_mem} kB
IP地址{localIP}
DHCP{DHCP}
"); + page += F("
"); + page.replace(F("{UID}"), UID); + page.replace(F("{SSID}"), WiFi.SSID()); + page.replace(F("{RSSI}"), String(WiFi.RSSI())); + page.replace(F("{uptime}"), Rtc::msToHumanString(millis())); + page.replace(F("{free_mem}"), String(ESP.getFreeHeap() / 1024)); + page.replace(F("{localIP}"), WiFi.localIP().toString()); + page.replace(F("{DHCP}"), (!globalConfig.wifi.is_static ? F("DHCP") : F("静态IP"))); + // TAB 1 End + + server->sendContent(page); + + // TAB 2 Start + page = F("
"); + page += F("
"); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F("
WiFi名称信号
WiFi名称
WiFi密码
"); + page += F(""); + if (!WiFi.isConnected()) + { + radioJs += "scanWifi();"; + } + + page += F("
"); + page += F(""); + page += F(""); + radioJs += F("setRadioValue('dhcp', '{v}');"); + radioJs.replace(F("{v}"), globalConfig.wifi.is_static ? F("2") : F("1")); + + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F("
WIFI高级设置
DHCP"); + page += F("    "); + page += F(""); + page += F("
静态IP
子网掩码
网关
"); + page += F(""); + page.replace(F("{ip}"), globalConfig.wifi.ip); + page.replace(F("{sn}"), globalConfig.wifi.sn); + page.replace(F("{gw}"), globalConfig.wifi.gw); + + page += F("
"); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F("
MQTT设置
地址
端口    0为不启动mqtt
用户名
密码
主题
心跳上报间隔 秒  0为不上报
retain"); + page += F("    "); + page += F("
除非你知道它是干嘛的。"); + page += F("
状态{mqttconnected}
"); + page.replace(F("{server}"), globalConfig.mqtt.server); + page.replace(F("{port}"), String(globalConfig.mqtt.port)); + page.replace(F("{user}"), globalConfig.mqtt.user); + page.replace(F("{pass}"), globalConfig.mqtt.pass); + page.replace(F("{topic}"), globalConfig.mqtt.topic); + page.replace(F("{interval}"), String(globalConfig.mqtt.interval)); + radioJs += F("setRadioValue('retain', '{v}');"); + radioJs.replace(F("{v}"), globalConfig.mqtt.retain ? F("1") : F("0")); + page.replace(F("{mqttconnected}"), (Mqtt::mqttClient.connected() ? F("已连接") : F("未连接"))); + + page += F("
"); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + if (globalConfig.mqtt.discovery) + { + radioJs += F("id('discovery_btn').setAttribute('class', 'btn-danger');id('discovery_btn').innerHTML='关闭MQTT自动发现';"); + } + page += F("
MQTT自动发现
自发现状态{discovery}
自发现前缀
"); + page += F("
"); + page.replace(F("{discovery}"), globalConfig.mqtt.discovery ? F("已启动") : F("未启动")); + page.replace(F("{prefix}"), globalConfig.mqtt.discovery_prefix); + // TAB 2 End + + // TAB 3 Start + page += F("
"); + server->sendContent(page); + + if (module) + { + module->httpHtml(server); + } + + page = F("
"); + page += F(""); + page += F(""); + page += F(""); + page.replace(F("{UID}"), UID); + if ((1 & globalConfig.debug.type) == 1) + { + radioJs += F("setRadioValue('log_serial', '1');"); + } + if ((2 & globalConfig.debug.type) == 2) + { + radioJs += F("setRadioValue('log_syslog', '1');"); + } + if ((4 & globalConfig.debug.type) == 4) + { + radioJs += F("setRadioValue('log_web', '1');"); + } + if ((8 & globalConfig.debug.type) == 8) + { + radioJs += F("setRadioValue('log_serial1', '1');"); + } + + page += F(""); + page.replace(F("{server}"), globalConfig.debug.server); + page.replace(F("{port}"), String(globalConfig.debug.port)); + + page += F(""); + + page += F(""); + page += F("
模块设置
主机名 具有唯一性,留空默认
日志输出"); + page += F("    "); + page += F("    "); + page += F("    "); + page += F("    "); + page += F("
syslog服务器"); + page += F(" : "); + page += F("
NTP服务器"); + page += F(" 建议在获取时间失败时才填写"); + page.replace(F("{ntp}"), globalConfig.wifi.ntp); + page += F("
"); + + page += F("
"); + page += F(""); + page += F(""); + page += F("
"); + page += F("
"); + // TAB 3 End + server->sendContent(page); + + // TAB 4 Start + page = F("
"); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F("
硬件参数
ESP芯片ID{getChipId}
Flash芯片 ID{getFlashChipId}
Flash大小{getFlashChipRealSize} kB
固件Flash大小{getFlashChipSize} kB
固件大小{getSketchSize} kB
空闲程序空间{getFreeSketchSpace} kB
内核和SDK版本" ARDUINO_ESP8266_RELEASE "/{getSdkVersion}
重启原因{resetReason}
MAC地址{macAddress}
"); + page.replace(F("{getChipId}"), String(ESP.getChipId())); + page.replace(F("{getFlashChipId}"), String(ESP.getFlashChipId())); + page.replace(F("{getFlashChipSize}"), String(ESP.getFlashChipSize() / 1024)); + page.replace(F("{getFlashChipRealSize}"), String(ESP.getFlashChipRealSize() / 1024)); + page.replace(F("{getSketchSize}"), String(ESP.getSketchSize() / 1024)); + page.replace(F("{getFreeSketchSpace}"), String(ESP.getFreeSketchSpace() / 1024)); + page.replace(F("{getSdkVersion}"), ESP.getSdkVersion()); + page.replace(F("{macAddress}"), WiFi.macAddress()); + page.replace(F("{resetReason}"), ESP.getResetReason()); + + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F(""); + page += F("
固件升级
当前版本v{v}
编译时间{v1}
选择文件

"); + page += F(""); + page += F("
OTA更新
OTA地址
"); + page += F("
"); + page.replace(F("{v}"), module ? module->getModuleVersion() : F("0")); + page.replace(F("{v1}"), Rtc::GetBuildDateAndTime()); + page.replace(F("{ota_url}"), globalConfig.http.ota_url); + // TAB 4 End + server->sendContent(page); + + // TAB 5 Start + page = F("
"); + page += F("
"); + page += F(""); + page += F("
"); + // TAB 5 End + + page += F("
"); + radioJs += F(""); + + page += radioJs; + + page += F("
"); + + server->sendContent(page); +} + +void Http::handleMqtt() +{ + if (!checkAuth()) + { + return; + } + String topic = server->arg(F("mqtt_topic")); + if (topic.length() == 0) + { + Http::server->send(200, F("text/html"), F("{\"code\":0,\"msg\":\"MQTT主题不能为空\"}")); + return; + } + if (topic.indexOf("%prefix%/") == 0) + { + Http::server->send(200, F("text/html"), F("{\"code\":0,\"msg\":\"MQTT主题必须包含【%prefix%/】\"}")); + return; + } + strcpy(globalConfig.mqtt.server, server->arg(F("mqtt_server")).c_str()); + globalConfig.mqtt.port = server->arg(F("mqtt_port")).toInt(); + globalConfig.mqtt.retain = server->arg(F("retain")) == F("1"); + strcpy(globalConfig.mqtt.user, server->arg(F("mqtt_username")).c_str()); + strcpy(globalConfig.mqtt.pass, server->arg(F("mqtt_password")).c_str()); + strcpy(globalConfig.mqtt.topic, topic.c_str()); + globalConfig.mqtt.interval = server->arg(F("interval")).toInt(); + Config::saveConfig(); + Mqtt::setTopic(); + + if (Mqtt::mqttClient.connected()) + { + Mqtt::mqttClient.disconnect(); + } + + if (Mqtt::mqttConnect()) + { + Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"设置MQTT服务器成功,已连接。\",\"data\":{\"mqttconnected\":\"已连接\"}}")); + } + else + { + Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"设置MQTT服务器成功,未连接。\",\"data\":{\"mqttconnected\":\"未连接\"}}")); + } +} + +void Http::handledhcp() +{ + if (!checkAuth()) + { + return; + } + String ip = server->arg(F("static_ip")); + String netmask = server->arg(F("static_netmask")); + String gateway = server->arg(F("static_gateway")); + if (!Wifi::isIp(ip)) + { + Http::server->send(200, F("text/html"), F("{\"code\":0,\"msg\":\"IP地址错误\"}")); + return; + } + if (!Wifi::isIp(netmask)) + { + Http::server->send(200, F("text/html"), F("{\"code\":0,\"msg\":\"掩码地址错误\"}")); + return; + } + if (!Wifi::isIp(gateway)) + { + Http::server->send(200, F("text/html"), F("{\"code\":0,\"msg\":\"网关地址错误\"}")); + return; + } + + IPAddress static_ip; + IPAddress static_sn; + IPAddress static_gw; + static_ip.fromString(ip); + static_sn.fromString(netmask); + static_gw.fromString(gateway); + + if (!(static_ip.isV4() && static_sn.isV4() && (!static_gw.isSet() || static_gw.isV4()))) + { + Http::server->send(200, F("text/html"), F("{\"code\":0,\"msg\":\"IP地址或者网关错误\"}")); + return; + } + + if ((static_ip.v4() & static_sn.v4()) != (static_gw.v4() & static_sn.v4())) + { + Http::server->send(200, F("text/html"), F("{\"code\":0,\"msg\":\"网段错误\"}")); + return; + } + + bool old = globalConfig.wifi.is_static; + globalConfig.wifi.is_static = server->arg(F("dhcp")).equals(F("2")); + strcpy(globalConfig.wifi.ip, ip.c_str()); + strcpy(globalConfig.wifi.sn, netmask.c_str()); + strcpy(globalConfig.wifi.gw, gateway.c_str()); + Config::saveConfig(); + + if (old != globalConfig.wifi.is_static) + { + Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"设置DHCP信息成功,重启后生效\"}")); + } + else + { + Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"设置DHCP信息成功\"}")); + } +} + +void Http::handleScanWifi() +{ + if (!checkAuth()) + { + return; + } + int n = WiFi.scanNetworks(); + if (n == 0) + { + Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"\",\"data\":{\"list\":[]}}")); + //Http::server->send(200, F("text/html"), F("{\"code\":0,\"msg\":\"找不到网络,请重新试试。\"}")); + return; + } + + //sort networks + int indices[n]; + for (int i = 0; i < n; i++) + { + indices[i] = i; + } + + // RSSI排序 + for (int i = 0; i < n; i++) + { + for (int j = i + 1; j < n; j++) + { + if (WiFi.RSSI(indices[j]) > WiFi.RSSI(indices[i])) + { + std::swap(indices[i], indices[j]); + } + } + } + + // 删除重复项(必须对RSSI进行排序) + String cssid; + for (int i = 0; i < n; i++) + { + if (indices[i] == -1) + continue; + cssid = WiFi.SSID(indices[i]); + for (int j = i + 1; j < n; j++) + { + if (cssid == WiFi.SSID(indices[j])) + { + indices[j] = -1; // set dup aps to index -1 + } + } + } + + String data = ""; + for (int i = 0; i < n; i++) + { + if (indices[i] == -1) + continue; // skip dups + int RSSI = WiFi.RSSI(indices[i]); + int quality; + if (RSSI <= -100) + { + quality = 0; + } + else if (RSSI >= -50) + { + quality = 100; + } + else + { + quality = 2 * (RSSI + 100); + } + int _minimumQuality = -1; + if (_minimumQuality == -1 || _minimumQuality < quality) + { + data += ",{\"name\":\"" + WiFi.SSID(indices[i]) + "\",\"rssi\":\"" + RSSI + "\",\"quality\":" + quality + ",\"type\":" + WiFi.encryptionType(indices[i]) + "}"; + } + } + + Http::server->send(200, F("text/html"), "{\"code\":1,\"msg\":\"\",\"data\":{\"list\":[" + data.substring(1) + "]}}"); +} + +void Http::handleWifi() +{ + if (!checkAuth()) + { + return; + } + String wifi = server->arg(F("wifi_ssid")); + if (wifi == "") + { + Http::server->send(200, F("text/html"), F("{\"code\":0,\"msg\":\"WiFi名称不能为空。\"}")); + return; + } + String password = server->arg(F("wifi_password")); + + if (WiFi.getMode() == WIFI_STA) + { + strcpy(globalConfig.wifi.ssid, wifi.c_str()); + strcpy(globalConfig.wifi.pass, password.c_str()); + Config::saveConfig(); + Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"设置WiFi信息成功,重启模块(手动)使用新的Wifi信息连接。\"}")); + } + else + { + Wifi::tryConnect(wifi, password); + Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"尝试将ESP连接到网络。 如果失败,请重新连接到AP再试一次。\"}")); + } +} + +void Http::handleDiscovery() +{ + if (!checkAuth()) + { + return; + } + strcpy(globalConfig.mqtt.discovery_prefix, server->arg(F("discovery_prefix")).c_str()); + globalConfig.mqtt.discovery = !globalConfig.mqtt.discovery; + Config::saveConfig(); + + if (module) + { + module->mqttDiscovery(globalConfig.mqtt.discovery); + } + if (globalConfig.mqtt.discovery) + { + Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"已经打开MQTT自发现。\",\"data\":{\"discovery\":1}}")); + } + else + { + Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"已经关闭MQTT自发现。\",\"data\":{\"discovery\":0}}")); + } +} + +void Http::handleRestart() +{ + if (!checkAuth()) + { + return; + } + Config::saveConfig(); + Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"设备正在重启 . . .\"}")); + delay(200); + + Led::blinkLED(400, 4); + ESP.restart(); +} + +void Http::handleReset() +{ + if (!checkAuth()) + { + return; + } + Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"正在重置模块 . . . 设备将会重启。\"}")); + delay(200); + + Led::blinkLED(400, 4); + + Config::resetConfig(); + Config::saveConfig(); + ESP.restart(); +} + +void Http::handleOTA() +{ + if (!checkAuth()) + { + return; + } + strcpy(globalConfig.http.ota_url, server->arg(F("ota_url")).c_str()); + Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"如果成功后设备会重启 . . . \"}")); + Http::OTA(String(globalConfig.http.ota_url)); +} + +void Http::handleNotFound() +{ + if (captivePortal()) + { + return; + } + String message = F("File Not Found\n\n"); + message += F("URI: "); + message += server->uri(); + message += F("\nMethod: "); + message += (server->method() == HTTP_GET) ? F("GET") : F("POST"); + message += F("\nArguments: "); + message += server->args(); + message += F("\n"); + for (uint8_t i = 0; i < server->args(); i++) + { + message += " " + server->argName(i) + ": " + server->arg(i) + "\n"; + } + server->sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate")); + server->sendHeader(F("Pragma"), F("no-cache")); + server->sendHeader(F("Expires"), F("-1")); + server->sendHeader(F("Content-Length"), String(message.length())); + server->send(404, F("text/plain"), message); +} + +void Http::handleGetStatus() +{ + if (!checkAuth()) + { + return; + } + bool cflg = true; + uint8_t counter = 0; + if (server->hasArg(F("i"))) + { + counter = server->arg(F("i")).toInt(); + } + + server->setContentLength(CONTENT_LENGTH_UNKNOWN); + server->send(200, F("text/html"), ""); + String data = F("{\"code\":1,\"msg\":\"\",\"data\":{"); + data += F("\"mqttconnected\":\""); + data += Mqtt::mqttClient.connected() ? F("已连接") : F("未连接"); + + data += F("\",\"discovery\":"); + data += globalConfig.mqtt.discovery ? 1 : 0; + + data += F(",\"uptime\":\""); + data += Rtc::msToHumanString(millis()); + + data += F("\",\"ip\":\""); + if (Wifi::configPortalStart == 0 && WiFi.isConnected()) + { + data += WiFi.localIP().toString(); + } + else + { + data += F(""); + } + + data += F("\",\"free_mem\":"); + data += ESP.getFreeHeap() / 1024; + + if (module) + { + String tmp = module->httpGetStatus(server); + if (tmp.length() > 0) + { + data += F(","); + data += tmp; + } + } + + data += F(",\"logindex\":"); + data += Debug::webLogIndex; + + data += F(",\"log\":\""); + server->sendContent(data); + + if (counter != Debug::webLogIndex) + { + if (!counter) + { + counter = Debug::webLogIndex; + cflg = false; + } + do + { + char *tmp; + uint16_t len; + Debug::GetLog(counter, &tmp, &len); + if (len) + { + if (cflg) + { + server->sendContent("\\n"); + } + + size_t j = 0; + for (size_t i = 0; i < len - 1; i++) + { + char each = tmp[i]; + if (each == '\\' || each == '"') + { + tmpData[j++] = '\\'; + tmpData[j++] = each; + } + else if (each == '\b') + { + tmpData[j++] = '\\'; + tmpData[j++] = 'b'; + } + else if (each == '\f') + { + tmpData[j++] = '\\'; + tmpData[j++] = 'f'; + } + else if (each == '\n') + { + tmpData[j++] = '\\'; + tmpData[j++] = 'n'; + } + else if (each == '\r') + { + tmpData[j++] = '\\'; + tmpData[j++] = 'r'; + } + else if (each == '\t') + { + tmpData[j++] = '\\'; + tmpData[j++] = 't'; + } + else + { + tmpData[j++] = each; + } + } + tmpData[j++] = '\0'; + + server->sendContent(tmpData); + cflg = true; + } + counter++; + if (!counter) + { + counter++; + } // Skip log index 0 as it is not allowed + } while (counter != Debug::webLogIndex); + } + + server->sendContent(F("\"}}")); +} + +void Http::handleUpdate() +{ + // handler for the /update form POST (once file upload finishes) + Http::server->on("/update", HTTP_POST, [&]() { + if (!checkAuth()) + { + return; + } + if (Update.hasError()) + { + Debug::AddError(PSTR("Update error: %s"), Http::updaterError.c_str()); + Http::server->send(200, F("text/html"), String(F("Update error: ")) + Http::updaterError); + } + else + { + Config::saveConfig(); + Http::server->client().setNoDelay(true); + Http::server->send_P(200, PSTR("text/html"), PSTR("升级成功!正在重启 . . .")); + delay(100); + Http::server->client().stop(); + ESP.restart(); + } }, [&]() { + HTTPUpload &upload = Http::server->upload(); + if (upload.status == UPLOAD_FILE_START) + { + Http::updaterError = String(); + if (globalConfig.http.user[0] != 0 && globalConfig.http.pass[0] != 0 && server->client().localIP().toString() != "192.168.4.1" && !server->authenticate(globalConfig.http.user, globalConfig.http.pass)) + { + Debug::AddInfo(PSTR("Unauthenticated Update")); + return; + } + WiFiUDP::stopAll(); + Debug::AddInfo(PSTR("Update: %s"), upload.filename.c_str()); + uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; + if (!Update.begin(maxSketchSpace, U_FLASH))//start with max available size + { + StreamString str; + Update.printError(str); + Http::updaterError = str.c_str(); + } + } + else if (upload.status == UPLOAD_FILE_WRITE && !Http::updaterError.length()) + { + if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) + { + StreamString str; + Update.printError(str); + Http::updaterError = str.c_str(); + } + } + else if (upload.status == UPLOAD_FILE_END && !Http::updaterError.length()) + { + if (Update.end(true)) + { + Debug::AddInfo(PSTR("Update Success: %u Rebooting..."), upload.totalSize); + } + else + { + StreamString str; + Update.printError(str); + Http::updaterError = str.c_str(); + } + } + else if (upload.status == UPLOAD_FILE_ABORTED) + { + Update.end(); + Debug::AddInfo(PSTR("Update was aborted")); + } + delay(0); }); +} + +void Http::begin() +{ + if (isBegin) + { + return; + } + isBegin = true; + server = new ESP8266WebServer(); + + server->on(F("/"), handleRoot); + server->on(F("/mqtt"), handleMqtt); + server->on(F("/dhcp"), handledhcp); + server->on(F("/scan_wifi"), handleScanWifi); + server->on(F("/wifi"), handleWifi); + server->on(F("/discovery"), handleDiscovery); + server->on(F("/restart"), handleRestart); + server->on(F("/reset"), handleReset); + server->on(F("/module_setting"), handleModuleSetting); + server->on(F("/ota"), handleOTA); + server->on(F("/get_status"), handleGetStatus); + server->onNotFound(handleNotFound); + handleUpdate(); + + if (module) + { + module->httpAdd(server); + } + MDNS.begin(UID); + server->begin(globalConfig.http.port); + Debug::AddInfo(PSTR("HTTP server started port: %d"), globalConfig.http.port); +} + +void Http::stop() +{ + if (!isBegin) + { + return; + } + server->stop(); + Debug::AddInfo(PSTR("HTTP server stoped")); +} + +void Http::loop() +{ + if (isBegin) + { + server->handleClient(); + MDNS.update(); + } +} + +bool Http::captivePortal() +{ + if (!Wifi::isIp(server->hostHeader())) + { + //Debug::AddInfo(PSTR("Request redirected to captive portal")); + server->sendHeader(F("Location"), String(F("http://")) + server->client().localIP().toString(), true); + server->send(302, F("text/plain"), ""); // Empty content inhibits Content-length header so we have to close the socket ourselves. + server->client().stop(); // Stop is needed because we sent no content length + return true; + } + return false; +} + +void Http::handleModuleSetting() +{ + if (!checkAuth()) + { + return; + } + if (Http::server->hasArg(F("log_serial")) || Http::server->hasArg(F("log_serial1")) || Http::server->hasArg(F("log_syslog")) || Http::server->hasArg(F("log_web"))) + { + int t = 0; + if (Http::server->arg(F("log_serial")).equals(F("1"))) + { + t = t | 1; + } + if (Http::server->arg(F("log_serial1")).equals(F("1"))) + { + t = t | 8; + } + if (Http::server->arg(F("log_web")).equals(F("1"))) + { + t = t | 4; + } + + String log_syslog = Http::server->arg(F("log_syslog")); + if (log_syslog.equals(F("1"))) + { + t = t | 2; + String log_syslog_host = Http::server->arg(F("log_syslog_host")); + String log_syslog_port = Http::server->arg(F("log_syslog_port")); + if (log_syslog_host.length() == 0) + { + Http::server->send(200, F("text/html"), F("{\"code\":0,\"msg\":\"syslog服务器不能为空\"}")); + return; + } + strcpy(globalConfig.debug.server, log_syslog_host.c_str()); + globalConfig.debug.port = log_syslog_port.toInt(); + WiFi.hostByName(globalConfig.debug.server, Debug::ip); + } + + if (Http::server->arg(F("log_serial1")).equals(F("1"))) + { + Serial1.begin(115200); + } + globalConfig.debug.type = t; + } + + String ntp = Http::server->arg(F("ntp")); + if ((globalConfig.wifi.ntp[0] == '\0' && ntp.length() > 0) || (globalConfig.wifi.ntp[0] != '\0' && ntp.length() == 0)) + { + strcpy(globalConfig.wifi.ntp, ntp.c_str()); + Rtc::init(); + } + else + { + strcpy(globalConfig.wifi.ntp, ntp.c_str()); + } + + String uid = Http::server->arg(F("uid")); + strcpy(globalConfig.uid, uid.c_str()); + Config::saveConfig(); + if (uid.length() == 0 || strncmp(globalConfig.uid, UID, uid.length()) != 0) + { + if (globalConfig.mqtt.discovery && module) + { + module->mqttDiscovery(false); + } + Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"修改了重要配置 . . . 正在重启中。\"}")); + Led::blinkLED(400, 4); + ESP.restart(); + } + else + { + Http::server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"已经修改成功\"}")); + } +} + +bool Http::checkAuth() +{ + if (globalConfig.http.user[0] != 0 && globalConfig.http.pass[0] != 0 && server->client().localIP().toString() != "192.168.4.1") + { + if (!server->authenticate(globalConfig.http.user, globalConfig.http.pass)) + { + server->requestAuthentication(); + return false; + } + } + return true; +} + +void Http::OTA(String url) +{ + if (url.indexOf(F("%04d")) != -1) + { + url.replace("%04d", String(ESP.getChipId() & 0x1fff)); + } + else if (url.indexOf(F("%d")) != -1) + { + url.replace("%d", String(ESP.getChipId())); + } + url.replace(F("%hostname%"), UID); + url.replace(F("%module%"), module ? module->getModuleName() : ""); + + Config::saveConfig(); + Debug::AddInfo(PSTR("OTA Url: %s"), url.c_str()); + Led::blinkLED(200, 5); + WiFiClient OTAclient; + if (ESPhttpUpdate.update(OTAclient, url, (module ? module->getModuleVersion() : "")) == HTTP_UPDATE_FAILED) + { + Debug::AddError(PSTR("HTTP_UPDATE_FAILD Error (%d): %s"), ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); + } +} diff --git a/lib/esp_framework/src/Led.cpp b/lib/esp_framework/src/Led.cpp index 56e1bee..995c37c 100644 --- a/lib/esp_framework/src/Led.cpp +++ b/lib/esp_framework/src/Led.cpp @@ -1,113 +1,113 @@ -#include -#include "Led.h" -#include "Module.h" - -Ticker *Led::ledTicker; -Ticker *Led::ledTicker2; -uint8_t Led::io = 99; -uint8_t Led::light; -uint8_t Led::ledType = 0; -bool Led::isOn = false; - -void Led::init(uint8_t _io, uint8_t _light) -{ - io = _io; - light = _light; - pinMode(io, OUTPUT); - - Led::ledType = 0; - ledTicker = new Ticker(); - ledTicker2 = new Ticker(); - - off(); -} - -void Led::loop() -{ - if (io == 99) - { - return; - } - if (module && module->moduleLed()) - { - if (ledTicker->active()) - { - ledTicker->detach(); - } - Led::ledType = 3; - } - else if (WiFi.status() != WL_CONNECTED) - { - if (Led::ledType != 0) - { - Led::ledType = 0; - ledTicker->attach(0.2, []() { toggle(); }); - } - } - else if (!Mqtt::mqttClient.connected()) - { - if (Led::ledType != 1) - { - Led::ledType = 1; - ledTicker->attach(0.3, []() { toggle(); }); - } - } - else - { - if (Led::ledType != 2) - { - Led::ledType = 2; - ledTicker->attach(5, led, 200); - } - } -} - -void Led::on() -{ - if (io != 99 && !isOn) - { - isOn = true; - digitalWrite(io, light); - } -} - -void Led::off() -{ - if (io != 99 && isOn) - { - isOn = false; - digitalWrite(io, !light); - } -} - -void Led::toggle() -{ - isOn ? off() : on(); -} - -void Led::led(int ms) -{ - if (io != 99) - { - on(); - ledTicker2->once_ms(ms, []() { off(); }); - } -} - -void Led::blinkLED(int duration, int n) -{ - if (io == 99) - { - return; - } - for (int i = 0; i < n; i++) - { - on(); - delay(duration); - off(); - if (n != i + 1) - { - delay(duration); - } - } -} +#include +#include "Led.h" +#include "Module.h" + +Ticker *Led::ledTicker; +Ticker *Led::ledTicker2; +uint8_t Led::io = 99; +uint8_t Led::light; +uint8_t Led::ledType = 0; +bool Led::isOn = false; + +void Led::init(uint8_t _io, uint8_t _light) +{ + io = _io; + light = _light; + pinMode(io, OUTPUT); + + Led::ledType = 0; + ledTicker = new Ticker(); + ledTicker2 = new Ticker(); + + off(); +} + +void Led::loop() +{ + if (io == 99) + { + return; + } + if (module && module->moduleLed()) + { + if (ledTicker->active()) + { + ledTicker->detach(); + } + Led::ledType = 3; + } + else if (WiFi.status() != WL_CONNECTED) + { + if (Led::ledType != 0) + { + Led::ledType = 0; + ledTicker->attach(0.2, []() { toggle(); }); + } + } + else if (!Mqtt::mqttClient.connected()) + { + if (Led::ledType != 1) + { + Led::ledType = 1; + ledTicker->attach(0.3, []() { toggle(); }); + } + } + else + { + if (Led::ledType != 2) + { + Led::ledType = 2; + ledTicker->attach(5, led, 200); + } + } +} + +void Led::on() +{ + if (io != 99 && !isOn) + { + isOn = true; + digitalWrite(io, light); + } +} + +void Led::off() +{ + if (io != 99 && isOn) + { + isOn = false; + digitalWrite(io, !light); + } +} + +void Led::toggle() +{ + isOn ? off() : on(); +} + +void Led::led(int ms) +{ + if (io != 99) + { + on(); + ledTicker2->once_ms(ms, []() { off(); }); + } +} + +void Led::blinkLED(int duration, int n) +{ + if (io == 99) + { + return; + } + for (int i = 0; i < n; i++) + { + on(); + delay(duration); + off(); + if (n != i + 1) + { + delay(duration); + } + } +} diff --git a/lib/esp_framework/src/Mqtt.cpp b/lib/esp_framework/src/Mqtt.cpp index 521c948..8923e34 100644 --- a/lib/esp_framework/src/Mqtt.cpp +++ b/lib/esp_framework/src/Mqtt.cpp @@ -1,210 +1,210 @@ -#include -#include "Mqtt.h" -#include "Module.h" - -String Mqtt::topicCmnd; -String Mqtt::topicStat; -String Mqtt::topicTele; -uint8_t Mqtt::operationFlag = 0; -PubSubClient Mqtt::mqttClient; -uint32_t Mqtt::lastReconnectAttempt = 0; // 最后尝试重连时间 -uint32_t Mqtt::kMqttReconnectTime = 30000; // 重新连接尝试之间的延迟(ms) -std::function Mqtt::connectedcallback = NULL; - -bool Mqtt::mqttConnect() -{ - if (WiFi.status() != WL_CONNECTED) - { - Debug::AddInfo(PSTR("wifi disconnected")); - return false; - } - if (globalConfig.mqtt.port == 0) - { - Debug::AddInfo(PSTR("no set mqtt info")); - return false; - } - if (mqttClient.connected()) - { - return true; - } - - Debug::AddInfo(PSTR("client mqtt not connected, trying to connect to %s:%d Broker"), globalConfig.mqtt.server, globalConfig.mqtt.port); - mqttClient.setServer(globalConfig.mqtt.server, globalConfig.mqtt.port); - - if (mqttClient.connect(UID, globalConfig.mqtt.user, globalConfig.mqtt.pass, getTeleTopic(F("availability")).c_str(), 0, true, "offline")) - { - Debug::AddInfo(PSTR("successful client mqtt connection")); - if (connectedcallback != NULL) - { - connectedcallback(); - } - } - else - { - Debug::AddInfo(PSTR("Connecting to %s:%d Broker . . failed, rc=%d"), globalConfig.mqtt.server, globalConfig.mqtt.port, mqttClient.state()); - } - - return mqttClient.connected(); -} - -void Mqtt::doReportHeartbeat() -{ - char message[250]; - sprintf(message, "{\"UID\":\"%s\",\"SSID\":\"%s\",\"RSSI\":\"%s\",\"Version\":\"%s\",\"ip\":\"%s\",\"mac\":\"%s\",\"freeMem\":%d,\"uptime\":%d}", - UID, WiFi.SSID().c_str(), String(WiFi.RSSI()).c_str(), (module ? module->getModuleVersion().c_str() : "0"), WiFi.localIP().toString().c_str(), WiFi.macAddress().c_str(), ESP.getFreeHeap(), millis() / 1000); - //Debug::AddInfo(PSTR("%s"), message); - publish(getTeleTopic(F("HEARTBEAT")), message); -} - -void Mqtt::availability() -{ - publish(getTeleTopic(F("availability")), "online", true); -} - -void Mqtt::perSecondDo() -{ - bitSet(operationFlag, 0); -} - -void Mqtt::loop() -{ - if (WiFi.status() != WL_CONNECTED || globalConfig.mqtt.port == 0) - { - return; - } - uint32_t now = millis(); - if (!mqttClient.connected()) - { - if (now - lastReconnectAttempt > kMqttReconnectTime || lastReconnectAttempt == 0) - { - lastReconnectAttempt = now; - if (mqttConnect()) - { - lastReconnectAttempt = 0; - availability(); - if (globalConfig.mqtt.interval > 0) - { - doReportHeartbeat(); - } - } - } - } - else - { - mqttClient.loop(); - if (bitRead(operationFlag, 0)) - { - bitClear(operationFlag, 0); - if (globalConfig.mqtt.interval > 0 && (perSecond % globalConfig.mqtt.interval) == 0) - { - doReportHeartbeat(); - } - if (perSecond % 3609 == 0) - { - availability(); - } - } - } -} - -void Mqtt::setTopic() -{ - topicCmnd = getTopic(0, ""); - topicStat = getTopic(1, ""); - topicTele = getTopic(2, ""); -} - -String Mqtt::getCmndTopic(String topic) -{ - return topicCmnd + topic; -} - -String Mqtt::getStatTopic(String topic) -{ - return topicStat + topic; -} -String Mqtt::getTeleTopic(String topic) -{ - return topicTele + topic; -} - -void Mqtt::mqttSetLoopCallback(MQTT_CALLBACK_SIGNATURE) -{ - mqttClient.setCallback(callback); -} - -void Mqtt::mqttSetConnectedCallback(MQTT_CONNECTED_CALLBACK_SIGNATURE) -{ - Mqtt::connectedcallback = connectedcallback; -} - -PubSubClient &Mqtt::setClient(Client &client) -{ - setTopic(); - return mqttClient.setClient(client); -} -bool Mqtt::publish(String topic, const char *payload) -{ - return mqttClient.publish(topic.c_str(), payload); -} - -bool Mqtt::publish(String topic, const char *payload, bool retained) -{ - return mqttClient.publish(topic.c_str(), payload, retained); -} - -bool Mqtt::publish(const char *topic, const char *payload) -{ - return mqttClient.publish(topic, payload); -} -bool Mqtt::publish(const char *topic, const char *payload, bool retained) -{ - return mqttClient.publish(topic, payload, retained); -} -bool Mqtt::publish(const char *topic, const uint8_t *payload, unsigned int plength) -{ - return mqttClient.publish(topic, payload, plength); -} -bool Mqtt::publish(const char *topic, const uint8_t *payload, unsigned int plength, bool retained) -{ - return mqttClient.publish(topic, payload, plength, retained); -} -bool Mqtt::publish_P(const char *topic, const char *payload, bool retained) -{ - return mqttClient.publish_P(topic, payload, retained); -} -bool Mqtt::publish_P(const char *topic, const uint8_t *payload, unsigned int plength, bool retained) -{ - return mqttClient.publish_P(topic, payload, plength, retained); -} - -bool Mqtt::subscribe(String topic) -{ - return mqttClient.subscribe(topic.c_str()); -} -bool Mqtt::subscribe(String topic, uint8_t qos) -{ - return mqttClient.subscribe(topic.c_str(), qos); -} -bool Mqtt::unsubscribe(String topic) -{ - return mqttClient.unsubscribe(topic.c_str()); -} - -String Mqtt::getTopic(uint8_t prefix, String subtopic) -{ - // 0: Cmnd 1:Stat 2:Tele - String fulltopic = String(globalConfig.mqtt.topic); - if ((0 == prefix) && (-1 == fulltopic.indexOf(F("%prefix%")))) - { - fulltopic += F("/%prefix%"); // Need prefix for commands to handle mqtt topic loops - } - fulltopic.replace(F("%prefix%"), (prefix == 0 ? F("cmnd") : ((prefix == 1 ? F("stat") : F("tele"))))); - fulltopic.replace(F("%hostname%"), UID); - fulltopic.replace(F("%module%"), module ? module->getModuleName() : F("module")); - fulltopic.replace(F("#"), ""); - fulltopic.replace(F("//"), "/"); - if (!fulltopic.endsWith(F("/"))) - fulltopic += F("/"); - return fulltopic + subtopic; -} +#include +#include "Mqtt.h" +#include "Module.h" + +String Mqtt::topicCmnd; +String Mqtt::topicStat; +String Mqtt::topicTele; +uint8_t Mqtt::operationFlag = 0; +PubSubClient Mqtt::mqttClient; +uint32_t Mqtt::lastReconnectAttempt = 0; // 最后尝试重连时间 +uint32_t Mqtt::kMqttReconnectTime = 30000; // 重新连接尝试之间的延迟(ms) +std::function Mqtt::connectedcallback = NULL; + +bool Mqtt::mqttConnect() +{ + if (WiFi.status() != WL_CONNECTED) + { + Debug::AddInfo(PSTR("wifi disconnected")); + return false; + } + if (globalConfig.mqtt.port == 0) + { + Debug::AddInfo(PSTR("no set mqtt info")); + return false; + } + if (mqttClient.connected()) + { + return true; + } + + Debug::AddInfo(PSTR("client mqtt not connected, trying to connect to %s:%d Broker"), globalConfig.mqtt.server, globalConfig.mqtt.port); + mqttClient.setServer(globalConfig.mqtt.server, globalConfig.mqtt.port); + + if (mqttClient.connect(UID, globalConfig.mqtt.user, globalConfig.mqtt.pass, getTeleTopic(F("availability")).c_str(), 0, true, "offline")) + { + Debug::AddInfo(PSTR("successful client mqtt connection")); + if (connectedcallback != NULL) + { + connectedcallback(); + } + } + else + { + Debug::AddInfo(PSTR("Connecting to %s:%d Broker . . failed, rc=%d"), globalConfig.mqtt.server, globalConfig.mqtt.port, mqttClient.state()); + } + + return mqttClient.connected(); +} + +void Mqtt::doReportHeartbeat() +{ + char message[250]; + sprintf(message, "{\"UID\":\"%s\",\"SSID\":\"%s\",\"RSSI\":\"%s\",\"Version\":\"%s\",\"ip\":\"%s\",\"mac\":\"%s\",\"freeMem\":%d,\"uptime\":%d}", + UID, WiFi.SSID().c_str(), String(WiFi.RSSI()).c_str(), (module ? module->getModuleVersion().c_str() : "0"), WiFi.localIP().toString().c_str(), WiFi.macAddress().c_str(), ESP.getFreeHeap(), millis() / 1000); + //Debug::AddInfo(PSTR("%s"), message); + publish(getTeleTopic(F("HEARTBEAT")), message); +} + +void Mqtt::availability() +{ + publish(getTeleTopic(F("availability")), "online", true); +} + +void Mqtt::perSecondDo() +{ + bitSet(operationFlag, 0); +} + +void Mqtt::loop() +{ + if (WiFi.status() != WL_CONNECTED || globalConfig.mqtt.port == 0) + { + return; + } + uint32_t now = millis(); + if (!mqttClient.connected()) + { + if (now - lastReconnectAttempt > kMqttReconnectTime || lastReconnectAttempt == 0) + { + lastReconnectAttempt = now; + if (mqttConnect()) + { + lastReconnectAttempt = 0; + availability(); + if (globalConfig.mqtt.interval > 0) + { + doReportHeartbeat(); + } + } + } + } + else + { + mqttClient.loop(); + if (bitRead(operationFlag, 0)) + { + bitClear(operationFlag, 0); + if (globalConfig.mqtt.interval > 0 && (perSecond % globalConfig.mqtt.interval) == 0) + { + doReportHeartbeat(); + } + if (perSecond % 3609 == 0) + { + availability(); + } + } + } +} + +void Mqtt::setTopic() +{ + topicCmnd = getTopic(0, ""); + topicStat = getTopic(1, ""); + topicTele = getTopic(2, ""); +} + +String Mqtt::getCmndTopic(String topic) +{ + return topicCmnd + topic; +} + +String Mqtt::getStatTopic(String topic) +{ + return topicStat + topic; +} +String Mqtt::getTeleTopic(String topic) +{ + return topicTele + topic; +} + +void Mqtt::mqttSetLoopCallback(MQTT_CALLBACK_SIGNATURE) +{ + mqttClient.setCallback(callback); +} + +void Mqtt::mqttSetConnectedCallback(MQTT_CONNECTED_CALLBACK_SIGNATURE) +{ + Mqtt::connectedcallback = connectedcallback; +} + +PubSubClient &Mqtt::setClient(Client &client) +{ + setTopic(); + return mqttClient.setClient(client); +} +bool Mqtt::publish(String topic, const char *payload) +{ + return mqttClient.publish(topic.c_str(), payload); +} + +bool Mqtt::publish(String topic, const char *payload, bool retained) +{ + return mqttClient.publish(topic.c_str(), payload, retained); +} + +bool Mqtt::publish(const char *topic, const char *payload) +{ + return mqttClient.publish(topic, payload); +} +bool Mqtt::publish(const char *topic, const char *payload, bool retained) +{ + return mqttClient.publish(topic, payload, retained); +} +bool Mqtt::publish(const char *topic, const uint8_t *payload, unsigned int plength) +{ + return mqttClient.publish(topic, payload, plength); +} +bool Mqtt::publish(const char *topic, const uint8_t *payload, unsigned int plength, bool retained) +{ + return mqttClient.publish(topic, payload, plength, retained); +} +bool Mqtt::publish_P(const char *topic, const char *payload, bool retained) +{ + return mqttClient.publish_P(topic, payload, retained); +} +bool Mqtt::publish_P(const char *topic, const uint8_t *payload, unsigned int plength, bool retained) +{ + return mqttClient.publish_P(topic, payload, plength, retained); +} + +bool Mqtt::subscribe(String topic) +{ + return mqttClient.subscribe(topic.c_str()); +} +bool Mqtt::subscribe(String topic, uint8_t qos) +{ + return mqttClient.subscribe(topic.c_str(), qos); +} +bool Mqtt::unsubscribe(String topic) +{ + return mqttClient.unsubscribe(topic.c_str()); +} + +String Mqtt::getTopic(uint8_t prefix, String subtopic) +{ + // 0: Cmnd 1:Stat 2:Tele + String fulltopic = String(globalConfig.mqtt.topic); + if ((0 == prefix) && (-1 == fulltopic.indexOf(F("%prefix%")))) + { + fulltopic += F("/%prefix%"); // Need prefix for commands to handle mqtt topic loops + } + fulltopic.replace(F("%prefix%"), (prefix == 0 ? F("cmnd") : ((prefix == 1 ? F("stat") : F("tele"))))); + fulltopic.replace(F("%hostname%"), UID); + fulltopic.replace(F("%module%"), module ? module->getModuleName() : F("module")); + fulltopic.replace(F("#"), ""); + fulltopic.replace(F("//"), "/"); + if (!fulltopic.endsWith(F("/"))) + fulltopic += F("/"); + return fulltopic + subtopic; +} diff --git a/lib/esp_framework/src/Rtc.cpp b/lib/esp_framework/src/Rtc.cpp index 3bbff2d..e452058 100644 --- a/lib/esp_framework/src/Rtc.cpp +++ b/lib/esp_framework/src/Rtc.cpp @@ -1,236 +1,236 @@ -#include -#include "Rtc.h" -#include "sntp.h" -#include "Debug.h" - -RtcReboot Rtc::rtcReboot; -uint32_t Rtc::rtcRebootCrc = 0; -TIME_T Rtc::rtcTime; -uint32_t Rtc::utcTime; -uint8_t Rtc::operationFlag = 0; -static const uint8_t kDaysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // API starts months from 1, this array starts from 0 -static const char kMonthNamesEnglish[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; - -String Rtc::GetBuildDateAndTime() -{ - // "2017-03-07T11:08:02" - ISO8601:2004 - char bdt[21]; - char *p; - char mdate[] = __DATE__; // "Mar 7 2017" - char *smonth = mdate; - int day = 0; - int year = 0; - - // sscanf(mdate, "%s %d %d", bdt, &day, &year); // Not implemented in 2.3.0 and probably too much code - uint8_t i = 0; - for (char *str = strtok_r(mdate, " ", &p); str && i < 3; str = strtok_r(NULL, " ", &p)) - { - switch (i++) - { - case 0: // Month - smonth = str; - break; - case 1: // Day - day = atoi(str); - break; - case 2: // Year - year = atoi(str); - } - } - int month = (strstr(kMonthNamesEnglish, smonth) - kMonthNamesEnglish) / 3 + 1; - snprintf_P(bdt, sizeof(bdt), PSTR("%d-%02d-%02d %s"), year, month, day, __TIME__); - return String(bdt); // 2017-03-07T11:08:02 -} - -String Rtc::msToHumanString(uint32_t const msecs) -{ - uint32_t totalseconds = msecs / 1000; - if (totalseconds == 0) - { - return F("0T00:00:00"); - } - - // Note: millis() can only count up to 45 days, so uint8_t is safe. - uint8_t days = totalseconds / (60 * 60 * 24); - uint8_t hours = (totalseconds / (60 * 60)) % 24; - uint8_t minutes = (totalseconds / 60) % 60; - uint8_t seconds = totalseconds % 60; - - char dt[16]; - snprintf_P(dt, sizeof(dt), PSTR("%dT%02d:%02d:%02d"), days, hours, minutes, seconds); - return String(dt); -} - -String Rtc::timeSince(uint32_t const start) -{ - if (start == 0) - return F("Never"); - uint32_t diff = 0; - uint32_t now = millis(); - if (start < now) - diff = now - start; - else - diff = UINT32_MAX - start + now; - return msToHumanString(diff) + " ago"; -} - -void Rtc::breakTime(uint32_t time_input, TIME_T &tm) -{ - // break the given time_input into time components - // this is a more compact version of the C library localtime function - // note that year is offset from 1970 !!! - - uint8_t year; - uint8_t month; - uint8_t month_length; - uint32_t time; - unsigned long days; - - time = time_input; - tm.second = time % 60; - time /= 60; // now it is minutes - tm.minute = time % 60; - time /= 60; // now it is hours - tm.hour = time % 24; - time /= 24; // now it is days - tm.days = time; - tm.day_of_week = ((time + 4) % 7) + 1; // Sunday is day 1 - - year = 0; - days = 0; - while ((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) - { - year++; - } - tm.year = year + 1970; // year is offset from 1970 - - days -= LEAP_YEAR(year) ? 366 : 365; - time -= days; // now it is days in this year, starting at 0 - tm.day_of_year = time; - - days = 0; - month = 0; - month_length = 0; - for (month = 0; month < 12; month++) - { - if (1 == month) - { // february - if (LEAP_YEAR(year)) - { - month_length = 29; - } - else - { - month_length = 28; - } - } - else - { - month_length = kDaysInMonth[month]; - } - - if (time >= month_length) - { - time -= month_length; - } - else - { - break; - } - } - tm.month = month + 1; // jan is month 1 - tm.day_of_month = time + 1; // day of month - tm.valid = (time_input > 1451602800); // 2016-01-01 -} - -void Rtc::loop() -{ - if (bitRead(operationFlag, 0)) - { - bitClear(operationFlag, 0); - getNtp(); - } -} - -void Rtc::getNtp() -{ - if (WiFi.status() == WL_CONNECTED) - { - uint32_t ntp_time = sntp_get_current_timestamp(); - if (ntp_time > 1451602800) - { - utcTime = ntp_time; - breakTime(utcTime, rtcTime); - Debug::AddInfo(PSTR("NTP: %04d-%02d-%02d %02d:%02d:%02d"), rtcTime.year, rtcTime.month, rtcTime.day_of_month, rtcTime.hour, rtcTime.minute, rtcTime.second); - } - } -} - -void Rtc::perSecondDo() -{ - bool isAdd = false; - if (utcTime == 0 || perSecond % 600 == 0) - { - bitSet(operationFlag, 0); - } - if (utcTime > 0) - { - utcTime += 1; - breakTime(utcTime, rtcTime); - //Debug::AddInfo(PSTR("Ticker: %04d-%02d-%02d %02d:%02d:%02d"), rtcTime.year, rtcTime.month, rtcTime.day_of_month, rtcTime.hour, rtcTime.minute, rtcTime.second); - } -} - -void Rtc::init() -{ - if (globalConfig.wifi.ntp[0] != '\0') - { - Debug::AddInfo(PSTR("NTP Server: %s"), globalConfig.wifi.ntp); - sntp_setservername(0, globalConfig.wifi.ntp); - } - else - { - Debug::AddInfo(PSTR("NTP Server: default")); - sntp_setservername(0, (char *)"120.25.115.20"); - sntp_setservername(1, (char *)"203.107.6.88"); - sntp_setservername(2, (char *)"ntp3.aliyun.com"); - } - sntp_stop(); - sntp_set_timezone(8); - sntp_init(); - utcTime = 0; -} - -uint32_t Rtc::getRtcRebootCrc() -{ - uint32_t crc = 0; - uint8_t *bytes = (uint8_t *)&rtcReboot; - for (uint32_t i = 0; i < sizeof(RtcReboot); i++) - { - crc += bytes[i] * (i + 1); - } - return crc; -} - -void Rtc::rtcRebootLoad() -{ - ESP.rtcUserMemoryRead(100 - sizeof(RtcReboot), (uint32_t *)&rtcReboot, sizeof(RtcReboot)); // 0x280 - if (rtcReboot.valid != RTC_MEM_VALID) - { - memset(&rtcReboot, 0, sizeof(RtcReboot)); - rtcReboot.valid = RTC_MEM_VALID; - rtcReboot.fast_reboot_count = 0; - rtcRebootSave(); - } - rtcRebootCrc = getRtcRebootCrc(); -} - -void Rtc::rtcRebootSave() -{ - if (getRtcRebootCrc() != rtcRebootCrc) - { - rtcReboot.valid = RTC_MEM_VALID; - ESP.rtcUserMemoryWrite(100 - sizeof(RtcReboot), (uint32_t *)&rtcReboot, sizeof(RtcReboot)); - rtcRebootCrc = getRtcRebootCrc(); - } -} +#include +#include "Rtc.h" +#include "sntp.h" +#include "Debug.h" + +RtcReboot Rtc::rtcReboot; +uint32_t Rtc::rtcRebootCrc = 0; +TIME_T Rtc::rtcTime; +uint32_t Rtc::utcTime; +uint8_t Rtc::operationFlag = 0; +static const uint8_t kDaysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // API starts months from 1, this array starts from 0 +static const char kMonthNamesEnglish[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; + +String Rtc::GetBuildDateAndTime() +{ + // "2017-03-07T11:08:02" - ISO8601:2004 + char bdt[21]; + char *p; + char mdate[] = __DATE__; // "Mar 7 2017" + char *smonth = mdate; + int day = 0; + int year = 0; + + // sscanf(mdate, "%s %d %d", bdt, &day, &year); // Not implemented in 2.3.0 and probably too much code + uint8_t i = 0; + for (char *str = strtok_r(mdate, " ", &p); str && i < 3; str = strtok_r(NULL, " ", &p)) + { + switch (i++) + { + case 0: // Month + smonth = str; + break; + case 1: // Day + day = atoi(str); + break; + case 2: // Year + year = atoi(str); + } + } + int month = (strstr(kMonthNamesEnglish, smonth) - kMonthNamesEnglish) / 3 + 1; + snprintf_P(bdt, sizeof(bdt), PSTR("%d-%02d-%02d %s"), year, month, day, __TIME__); + return String(bdt); // 2017-03-07T11:08:02 +} + +String Rtc::msToHumanString(uint32_t const msecs) +{ + uint32_t totalseconds = msecs / 1000; + if (totalseconds == 0) + { + return F("0T00:00:00"); + } + + // Note: millis() can only count up to 45 days, so uint8_t is safe. + uint8_t days = totalseconds / (60 * 60 * 24); + uint8_t hours = (totalseconds / (60 * 60)) % 24; + uint8_t minutes = (totalseconds / 60) % 60; + uint8_t seconds = totalseconds % 60; + + char dt[16]; + snprintf_P(dt, sizeof(dt), PSTR("%dT%02d:%02d:%02d"), days, hours, minutes, seconds); + return String(dt); +} + +String Rtc::timeSince(uint32_t const start) +{ + if (start == 0) + return F("Never"); + uint32_t diff = 0; + uint32_t now = millis(); + if (start < now) + diff = now - start; + else + diff = UINT32_MAX - start + now; + return msToHumanString(diff) + " ago"; +} + +void Rtc::breakTime(uint32_t time_input, TIME_T &tm) +{ + // break the given time_input into time components + // this is a more compact version of the C library localtime function + // note that year is offset from 1970 !!! + + uint8_t year; + uint8_t month; + uint8_t month_length; + uint32_t time; + unsigned long days; + + time = time_input; + tm.second = time % 60; + time /= 60; // now it is minutes + tm.minute = time % 60; + time /= 60; // now it is hours + tm.hour = time % 24; + time /= 24; // now it is days + tm.days = time; + tm.day_of_week = ((time + 4) % 7) + 1; // Sunday is day 1 + + year = 0; + days = 0; + while ((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) + { + year++; + } + tm.year = year + 1970; // year is offset from 1970 + + days -= LEAP_YEAR(year) ? 366 : 365; + time -= days; // now it is days in this year, starting at 0 + tm.day_of_year = time; + + days = 0; + month = 0; + month_length = 0; + for (month = 0; month < 12; month++) + { + if (1 == month) + { // february + if (LEAP_YEAR(year)) + { + month_length = 29; + } + else + { + month_length = 28; + } + } + else + { + month_length = kDaysInMonth[month]; + } + + if (time >= month_length) + { + time -= month_length; + } + else + { + break; + } + } + tm.month = month + 1; // jan is month 1 + tm.day_of_month = time + 1; // day of month + tm.valid = (time_input > 1451602800); // 2016-01-01 +} + +void Rtc::loop() +{ + if (bitRead(operationFlag, 0)) + { + bitClear(operationFlag, 0); + getNtp(); + } +} + +void Rtc::getNtp() +{ + if (WiFi.status() == WL_CONNECTED) + { + uint32_t ntp_time = sntp_get_current_timestamp(); + if (ntp_time > 1451602800) + { + utcTime = ntp_time; + breakTime(utcTime, rtcTime); + Debug::AddInfo(PSTR("NTP: %04d-%02d-%02d %02d:%02d:%02d"), rtcTime.year, rtcTime.month, rtcTime.day_of_month, rtcTime.hour, rtcTime.minute, rtcTime.second); + } + } +} + +void Rtc::perSecondDo() +{ + bool isAdd = false; + if (utcTime == 0 || perSecond % 600 == 0) + { + bitSet(operationFlag, 0); + } + if (utcTime > 0) + { + utcTime += 1; + breakTime(utcTime, rtcTime); + //Debug::AddInfo(PSTR("Ticker: %04d-%02d-%02d %02d:%02d:%02d"), rtcTime.year, rtcTime.month, rtcTime.day_of_month, rtcTime.hour, rtcTime.minute, rtcTime.second); + } +} + +void Rtc::init() +{ + if (globalConfig.wifi.ntp[0] != '\0') + { + Debug::AddInfo(PSTR("NTP Server: %s"), globalConfig.wifi.ntp); + sntp_setservername(0, globalConfig.wifi.ntp); + } + else + { + Debug::AddInfo(PSTR("NTP Server: default")); + sntp_setservername(0, (char *)"120.25.115.20"); + sntp_setservername(1, (char *)"203.107.6.88"); + sntp_setservername(2, (char *)"ntp3.aliyun.com"); + } + sntp_stop(); + sntp_set_timezone(8); + sntp_init(); + utcTime = 0; +} + +uint32_t Rtc::getRtcRebootCrc() +{ + uint32_t crc = 0; + uint8_t *bytes = (uint8_t *)&rtcReboot; + for (uint32_t i = 0; i < sizeof(RtcReboot); i++) + { + crc += bytes[i] * (i + 1); + } + return crc; +} + +void Rtc::rtcRebootLoad() +{ + ESP.rtcUserMemoryRead(100 - sizeof(RtcReboot), (uint32_t *)&rtcReboot, sizeof(RtcReboot)); // 0x280 + if (rtcReboot.valid != RTC_MEM_VALID) + { + memset(&rtcReboot, 0, sizeof(RtcReboot)); + rtcReboot.valid = RTC_MEM_VALID; + rtcReboot.fast_reboot_count = 0; + rtcRebootSave(); + } + rtcRebootCrc = getRtcRebootCrc(); +} + +void Rtc::rtcRebootSave() +{ + if (getRtcRebootCrc() != rtcRebootCrc) + { + rtcReboot.valid = RTC_MEM_VALID; + ESP.rtcUserMemoryWrite(100 - sizeof(RtcReboot), (uint32_t *)&rtcReboot, sizeof(RtcReboot)); + rtcRebootCrc = getRtcRebootCrc(); + } +} diff --git a/lib/esp_framework/src/Util.cpp b/lib/esp_framework/src/Util.cpp index d85a4a7..917c9f3 100644 --- a/lib/esp_framework/src/Util.cpp +++ b/lib/esp_framework/src/Util.cpp @@ -1,82 +1,82 @@ -#include "Util.h" - -char *Util::strlowr(char *str) -{ - char *orign = str; - for (; *str != '\0'; str++) - *str = tolower(*str); - return orign; -} - -char *Util::strupr(char *str) -{ - char *orign = str; - for (; *str != '\0'; str++) - *str = toupper(*str); - return orign; -} - -uint16_t Util::hex2Str(uint8_t *bin, uint16_t bin_size, char *buff, bool needBlank) -{ - const char *set = "0123456789ABCDEF"; - char *nptr = buff; - if (NULL == buff) - { - return -1; - } - uint16_t len = needBlank ? (bin_size * 2 + bin_size) : (bin_size * 2 + 1); - while (bin_size--) - { - *nptr++ = set[(*bin) >> 4]; - *nptr++ = set[(*bin++) & 0xF]; - if (needBlank && bin_size > 0) - { - *nptr++ = ' '; - } - } - *nptr = '\0'; - return len; -} - -char *Util::dtostrfd(double number, unsigned char prec, char *s) -{ - if ((isnan(number)) || (isinf(number))) - { // Fix for JSON output (https://stackoverflow.com/questions/1423081/json-left-out-infinity-and-nan-json-status-in-ecmascript) - strcpy(s, "null"); - return s; - } - else - { - return dtostrf(number, 1, prec, s); - } -} - -uint32_t Util::SqrtInt(uint32_t num) -{ - if (num <= 1) - { - return num; - } - - uint32_t x = num / 2; - uint32_t y; - do - { - y = (x + num / x) / 2; - if (y >= x) - { - return x; - } - x = y; - } while (true); -} - -uint32_t Util::RoundSqrtInt(uint32_t num) -{ - uint32_t s = SqrtInt(4 * num); - if (s & 1) - { - s++; - } - return s / 2; +#include "Util.h" + +char *Util::strlowr(char *str) +{ + char *orign = str; + for (; *str != '\0'; str++) + *str = tolower(*str); + return orign; +} + +char *Util::strupr(char *str) +{ + char *orign = str; + for (; *str != '\0'; str++) + *str = toupper(*str); + return orign; +} + +uint16_t Util::hex2Str(uint8_t *bin, uint16_t bin_size, char *buff, bool needBlank) +{ + const char *set = "0123456789ABCDEF"; + char *nptr = buff; + if (NULL == buff) + { + return -1; + } + uint16_t len = needBlank ? (bin_size * 2 + bin_size) : (bin_size * 2 + 1); + while (bin_size--) + { + *nptr++ = set[(*bin) >> 4]; + *nptr++ = set[(*bin++) & 0xF]; + if (needBlank && bin_size > 0) + { + *nptr++ = ' '; + } + } + *nptr = '\0'; + return len; +} + +char *Util::dtostrfd(double number, unsigned char prec, char *s) +{ + if ((isnan(number)) || (isinf(number))) + { // Fix for JSON output (https://stackoverflow.com/questions/1423081/json-left-out-infinity-and-nan-json-status-in-ecmascript) + strcpy(s, "null"); + return s; + } + else + { + return dtostrf(number, 1, prec, s); + } +} + +uint32_t Util::SqrtInt(uint32_t num) +{ + if (num <= 1) + { + return num; + } + + uint32_t x = num / 2; + uint32_t y; + do + { + y = (x + num / x) / 2; + if (y >= x) + { + return x; + } + x = y; + } while (true); +} + +uint32_t Util::RoundSqrtInt(uint32_t num) +{ + uint32_t s = SqrtInt(4 * num); + if (s & 1) + { + s++; + } + return s / 2; } \ No newline at end of file diff --git a/lib/esp_framework/src/Wifi.cpp b/lib/esp_framework/src/Wifi.cpp index 4a34c67..77d532a 100644 --- a/lib/esp_framework/src/Wifi.cpp +++ b/lib/esp_framework/src/Wifi.cpp @@ -1,215 +1,215 @@ -#include -#include -#include -#include -#include "Wifi.h" -#include "Debug.h" - -WiFiClient Wifi::wifiClient; -WiFiEventHandler Wifi::STAGotIP; -//WiFiEventHandler Wifi::STADisconnected; -bool Wifi::isDHCP = true; - -unsigned long Wifi::configPortalStart = 0; -//unsigned long Wifi::connectStart = 0; -bool Wifi::connect = false; -String Wifi::_ssid = ""; -String Wifi::_pass = ""; -DNSServer *Wifi::dnsServer; - -void Wifi::connectWifi() -{ - delay(50); - if (globalConfig.wifi.ssid[0] != '\0') - { - setupWifi(); - } - else - { - setupWifiManager(true); - } -} - -void Wifi::setupWifi() -{ - WiFi.persistent(false); // Solve possible wifi init errors (re-add at 6.2.1.16 #4044, #4083) - WiFi.disconnect(true); // Delete SDK wifi config - delay(200); - WiFi.mode(WIFI_STA); - WiFi.setAutoConnect(true); - WiFi.setAutoReconnect(true); - WiFi.hostname(UID); - Debug::AddInfo(PSTR("Connecting to %s %s Wifi"), globalConfig.wifi.ssid, globalConfig.wifi.pass); - STAGotIP = WiFi.onStationModeGotIP([](const WiFiEventStationModeGotIP &event) { - //connectStart = 0; - Debug::AddInfo(PSTR("WiFi1 connected. SSID: %s IP address: %s"), WiFi.SSID().c_str(), WiFi.localIP().toString().c_str()); - if (globalConfig.wifi.is_static && String(globalConfig.wifi.ip).equals(WiFi.localIP().toString())) - { - isDHCP = false; - } - /* - STADisconnected = WiFi.onStationModeDisconnected([](const WiFiEventStationModeDisconnected &event) { - if (connectStart == 0) - { - connectStart = millis() + ConnectTimeOut * 1000; - } - Debug::AddInfo(PSTR("onStationModeDisconnected")); - STADisconnected = NULL; - }); - */ - }); - if (globalConfig.wifi.is_static) - { - isDHCP = false; - IPAddress static_ip; - IPAddress static_sn; - IPAddress static_gw; - static_ip.fromString(globalConfig.wifi.ip); - static_sn.fromString(globalConfig.wifi.sn); - static_gw.fromString(globalConfig.wifi.gw); - Debug::AddInfo(PSTR("Custom STA IP/GW/Subnet: %s %s %s"), globalConfig.wifi.ip, globalConfig.wifi.sn, globalConfig.wifi.gw); - WiFi.config(static_ip, static_gw, static_sn); - } - - //connectStart = millis(); - WiFi.begin(globalConfig.wifi.ssid, globalConfig.wifi.pass); -} - -void Wifi::setupWifiManager(bool resetSettings) -{ - if (resetSettings) - { - Debug::AddInfo(PSTR("WifiManager ResetSettings")); - Config::resetConfig(); - - Debug::AddInfo(PSTR("settings invalidated")); - Debug::AddInfo(PSTR("THIS MAY CAUSE AP NOT TO START UP PROPERLY. YOU NEED TO COMMENT IT OUT AFTER ERASING THE DATA.")); - WiFi.disconnect(true); - } - //WiFi.setAutoConnect(true); - //WiFi.setAutoReconnect(true); - WiFi.hostname(UID); - STAGotIP = WiFi.onStationModeGotIP([](const WiFiEventStationModeGotIP &event) { - Debug::AddInfo(PSTR("WiFi2 connected. SSID: %s IP address: %s"), WiFi.SSID().c_str(), WiFi.localIP().toString().c_str()); - }); - - configPortalStart = millis(); - if (WiFi.isConnected()) - { - WiFi.mode(WIFI_AP_STA); - Debug::AddInfo(PSTR("SET AP STA Mode")); - } - else - { - WiFi.persistent(false); - WiFi.disconnect(); - WiFi.mode(WIFI_AP_STA); - WiFi.persistent(true); - Debug::AddInfo(PSTR("SET AP Mode")); - } - - connect = false; - WiFi.softAP(UID); - delay(500); - Debug::AddInfo(PSTR("AP IP address: %s"), WiFi.softAPIP().toString().c_str()); - - dnsServer = new DNSServer(); - dnsServer->setErrorReplyCode(DNSReplyCode::NoError); - dnsServer->start(53, "*", WiFi.softAPIP()); -} - -void Wifi::tryConnect(String ssid, String pass) -{ - _ssid = ssid; - _pass = pass; - connect = true; -} - -void Wifi::loop() -{ - /* - if (connectStart > 0 && millis() > connectStart + (ConnectTimeOut * 1000)) - { - connectStart = 0; - if (!WiFi.isConnected()) - { - setupWifiManager(false); - return; - } - } - */ - if (configPortalStart == 0) - { - return; - } - dnsServer->processNextRequest(); - - if (connect) - { - connect = false; - Debug::AddInfo(PSTR("Connecting to new AP")); - - WiFi.begin(_ssid.c_str(), _pass.c_str()); - Debug::AddInfo(PSTR("Waiting for connection result with time out")); - } - - if (_ssid.length() > 0 && WiFi.isConnected()) - { - strcpy(globalConfig.wifi.ssid, _ssid.c_str()); - strcpy(globalConfig.wifi.pass, _pass.c_str()); - Config::saveConfig(); - - // 为了使WEB获取到IP 2秒后才关闭AP - Ticker *ticker = new Ticker(); - ticker->attach(3, []() { - WiFi.mode(WIFI_STA); - Debug::AddInfo(PSTR("SET STA Mode")); - ESP.reset(); - }); - - Debug::AddInfo(PSTR("WiFi connected. SSID: %s IP address: %s"), WiFi.SSID().c_str(), WiFi.localIP().toString().c_str()); - - dnsServer->stop(); - configPortalStart = 0; - _ssid = ""; - _pass = ""; - return; - } - - // 检查是否超时 - if (millis() > configPortalStart + (ConfigPortalTimeOut * 1000)) - { - dnsServer->stop(); - configPortalStart = 0; - _ssid = ""; - _pass = ""; - Debug::AddInfo(PSTR("startConfigPortal TimeOut")); - if (WiFi.isConnected()) - { - // 为了使WEB获取到IP 2秒后才关闭AP - Ticker *ticker = new Ticker(); - ticker->attach(3, []() { - WiFi.mode(WIFI_STA); - Debug::AddInfo(PSTR("SET STA Mode")); - ESP.reset(); - }); - } - else - { - Debug::AddInfo(PSTR("Wifi failed to connect and hit timeout. Rebooting...")); - delay(3000); - ESP.reset(); // 重置,可能进入深度睡眠状态 - delay(5000); - } - } -} - -bool Wifi::isIp(String str) -{ - int a, b, c, d; - if ((sscanf(str.c_str(), "%d.%d.%d.%d", &a, &b, &c, &d) == 4) && (a >= 0 && a <= 255) && (b >= 0 && b <= 255) && (c >= 0 && c <= 255) && (d >= 0 && d <= 255)) - { - return true; - } - return false; +#include +#include +#include +#include +#include "Wifi.h" +#include "Debug.h" + +WiFiClient Wifi::wifiClient; +WiFiEventHandler Wifi::STAGotIP; +//WiFiEventHandler Wifi::STADisconnected; +bool Wifi::isDHCP = true; + +unsigned long Wifi::configPortalStart = 0; +//unsigned long Wifi::connectStart = 0; +bool Wifi::connect = false; +String Wifi::_ssid = ""; +String Wifi::_pass = ""; +DNSServer *Wifi::dnsServer; + +void Wifi::connectWifi() +{ + delay(50); + if (globalConfig.wifi.ssid[0] != '\0') + { + setupWifi(); + } + else + { + setupWifiManager(true); + } +} + +void Wifi::setupWifi() +{ + WiFi.persistent(false); // Solve possible wifi init errors (re-add at 6.2.1.16 #4044, #4083) + WiFi.disconnect(true); // Delete SDK wifi config + delay(200); + WiFi.mode(WIFI_STA); + WiFi.setAutoConnect(true); + WiFi.setAutoReconnect(true); + WiFi.hostname(UID); + Debug::AddInfo(PSTR("Connecting to %s %s Wifi"), globalConfig.wifi.ssid, globalConfig.wifi.pass); + STAGotIP = WiFi.onStationModeGotIP([](const WiFiEventStationModeGotIP &event) { + //connectStart = 0; + Debug::AddInfo(PSTR("WiFi1 connected. SSID: %s IP address: %s"), WiFi.SSID().c_str(), WiFi.localIP().toString().c_str()); + if (globalConfig.wifi.is_static && String(globalConfig.wifi.ip).equals(WiFi.localIP().toString())) + { + isDHCP = false; + } + /* + STADisconnected = WiFi.onStationModeDisconnected([](const WiFiEventStationModeDisconnected &event) { + if (connectStart == 0) + { + connectStart = millis() + ConnectTimeOut * 1000; + } + Debug::AddInfo(PSTR("onStationModeDisconnected")); + STADisconnected = NULL; + }); + */ + }); + if (globalConfig.wifi.is_static) + { + isDHCP = false; + IPAddress static_ip; + IPAddress static_sn; + IPAddress static_gw; + static_ip.fromString(globalConfig.wifi.ip); + static_sn.fromString(globalConfig.wifi.sn); + static_gw.fromString(globalConfig.wifi.gw); + Debug::AddInfo(PSTR("Custom STA IP/GW/Subnet: %s %s %s"), globalConfig.wifi.ip, globalConfig.wifi.sn, globalConfig.wifi.gw); + WiFi.config(static_ip, static_gw, static_sn); + } + + //connectStart = millis(); + WiFi.begin(globalConfig.wifi.ssid, globalConfig.wifi.pass); +} + +void Wifi::setupWifiManager(bool resetSettings) +{ + if (resetSettings) + { + Debug::AddInfo(PSTR("WifiManager ResetSettings")); + Config::resetConfig(); + + Debug::AddInfo(PSTR("settings invalidated")); + Debug::AddInfo(PSTR("THIS MAY CAUSE AP NOT TO START UP PROPERLY. YOU NEED TO COMMENT IT OUT AFTER ERASING THE DATA.")); + WiFi.disconnect(true); + } + //WiFi.setAutoConnect(true); + //WiFi.setAutoReconnect(true); + WiFi.hostname(UID); + STAGotIP = WiFi.onStationModeGotIP([](const WiFiEventStationModeGotIP &event) { + Debug::AddInfo(PSTR("WiFi2 connected. SSID: %s IP address: %s"), WiFi.SSID().c_str(), WiFi.localIP().toString().c_str()); + }); + + configPortalStart = millis(); + if (WiFi.isConnected()) + { + WiFi.mode(WIFI_AP_STA); + Debug::AddInfo(PSTR("SET AP STA Mode")); + } + else + { + WiFi.persistent(false); + WiFi.disconnect(); + WiFi.mode(WIFI_AP_STA); + WiFi.persistent(true); + Debug::AddInfo(PSTR("SET AP Mode")); + } + + connect = false; + WiFi.softAP(UID); + delay(500); + Debug::AddInfo(PSTR("AP IP address: %s"), WiFi.softAPIP().toString().c_str()); + + dnsServer = new DNSServer(); + dnsServer->setErrorReplyCode(DNSReplyCode::NoError); + dnsServer->start(53, "*", WiFi.softAPIP()); +} + +void Wifi::tryConnect(String ssid, String pass) +{ + _ssid = ssid; + _pass = pass; + connect = true; +} + +void Wifi::loop() +{ + /* + if (connectStart > 0 && millis() > connectStart + (ConnectTimeOut * 1000)) + { + connectStart = 0; + if (!WiFi.isConnected()) + { + setupWifiManager(false); + return; + } + } + */ + if (configPortalStart == 0) + { + return; + } + dnsServer->processNextRequest(); + + if (connect) + { + connect = false; + Debug::AddInfo(PSTR("Connecting to new AP")); + + WiFi.begin(_ssid.c_str(), _pass.c_str()); + Debug::AddInfo(PSTR("Waiting for connection result with time out")); + } + + if (_ssid.length() > 0 && WiFi.isConnected()) + { + strcpy(globalConfig.wifi.ssid, _ssid.c_str()); + strcpy(globalConfig.wifi.pass, _pass.c_str()); + Config::saveConfig(); + + // 为了使WEB获取到IP 2秒后才关闭AP + Ticker *ticker = new Ticker(); + ticker->attach(3, []() { + WiFi.mode(WIFI_STA); + Debug::AddInfo(PSTR("SET STA Mode")); + ESP.reset(); + }); + + Debug::AddInfo(PSTR("WiFi connected. SSID: %s IP address: %s"), WiFi.SSID().c_str(), WiFi.localIP().toString().c_str()); + + dnsServer->stop(); + configPortalStart = 0; + _ssid = ""; + _pass = ""; + return; + } + + // 检查是否超时 + if (millis() > configPortalStart + (ConfigPortalTimeOut * 1000)) + { + dnsServer->stop(); + configPortalStart = 0; + _ssid = ""; + _pass = ""; + Debug::AddInfo(PSTR("startConfigPortal TimeOut")); + if (WiFi.isConnected()) + { + // 为了使WEB获取到IP 2秒后才关闭AP + Ticker *ticker = new Ticker(); + ticker->attach(3, []() { + WiFi.mode(WIFI_STA); + Debug::AddInfo(PSTR("SET STA Mode")); + ESP.reset(); + }); + } + else + { + Debug::AddInfo(PSTR("Wifi failed to connect and hit timeout. Rebooting...")); + delay(3000); + ESP.reset(); // 重置,可能进入深度睡眠状态 + delay(5000); + } + } +} + +bool Wifi::isIp(String str) +{ + int a, b, c, d; + if ((sscanf(str.c_str(), "%d.%d.%d.%d", &a, &b, &c, &d) == 4) && (a >= 0 && a <= 255) && (b >= 0 && b <= 255) && (c >= 0 && c <= 255) && (d >= 0 && d <= 255)) + { + return true; + } + return false; } \ No newline at end of file diff --git a/nanopb/DC1Config.proto b/nanopb/DC1Config.proto index babba26..867be9c 100644 --- a/nanopb/DC1Config.proto +++ b/nanopb/DC1Config.proto @@ -1,27 +1,27 @@ - -syntax = "proto3"; - -import "nanopb.proto"; - -message DC1ConfigMessage { - uint32 last_state = 1 [(nanopb).int_size = IS_8]; - uint32 power_on_state = 2 [(nanopb).int_size = IS_8]; - uint32 power_mode = 3 [(nanopb).int_size = IS_8]; - uint32 logo_led = 4 [(nanopb).int_size = IS_8]; - uint32 wifi_led = 5 [(nanopb).int_size = IS_8]; - uint32 sub_kinkage = 6 [(nanopb).int_size = IS_8]; - - uint32 report_interval = 20 [(nanopb).int_size = IS_16]; - uint32 energy_power_delta = 21 [(nanopb).int_size = IS_16]; - uint32 energy_power_calibration = 22 [(nanopb).int_size = IS_64]; - uint32 energy_voltage_calibration = 23 [(nanopb).int_size = IS_64]; - uint32 energy_current_calibration = 24 [(nanopb).int_size = IS_64]; - - uint32 energy_kWhtoday = 25 [(nanopb).int_size = IS_64]; - uint32 energy_kWhyesterday = 26 [(nanopb).int_size = IS_64]; - uint32 energy_kWhtotal = 27 [(nanopb).int_size = IS_64]; - uint32 energy_kWhdoy = 28 [(nanopb).int_size = IS_16]; - uint32 energy_kWhtotal_time = 29 [(nanopb).int_size = IS_32]; - uint32 energy_max_power = 30 [(nanopb).int_size = IS_16]; - -} + +syntax = "proto3"; + +import "nanopb.proto"; + +message DC1ConfigMessage { + uint32 last_state = 1 [(nanopb).int_size = IS_8]; + uint32 power_on_state = 2 [(nanopb).int_size = IS_8]; + uint32 power_mode = 3 [(nanopb).int_size = IS_8]; + uint32 logo_led = 4 [(nanopb).int_size = IS_8]; + uint32 wifi_led = 5 [(nanopb).int_size = IS_8]; + uint32 sub_kinkage = 6 [(nanopb).int_size = IS_8]; + + uint32 report_interval = 20 [(nanopb).int_size = IS_16]; + uint32 energy_power_delta = 21 [(nanopb).int_size = IS_16]; + uint32 energy_power_calibration = 22 [(nanopb).int_size = IS_64]; + uint32 energy_voltage_calibration = 23 [(nanopb).int_size = IS_64]; + uint32 energy_current_calibration = 24 [(nanopb).int_size = IS_64]; + + uint32 energy_kWhtoday = 25 [(nanopb).int_size = IS_64]; + uint32 energy_kWhyesterday = 26 [(nanopb).int_size = IS_64]; + uint32 energy_kWhtotal = 27 [(nanopb).int_size = IS_64]; + uint32 energy_kWhdoy = 28 [(nanopb).int_size = IS_16]; + uint32 energy_kWhtotal_time = 29 [(nanopb).int_size = IS_32]; + uint32 energy_max_power = 30 [(nanopb).int_size = IS_16]; + +} diff --git a/nanopb/descriptor.proto b/nanopb/descriptor.proto index 8697a50..a29d266 100644 --- a/nanopb/descriptor.proto +++ b/nanopb/descriptor.proto @@ -1,872 +1,872 @@ -// Protocol Buffers - Google's data interchange format -// Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Author: kenton@google.com (Kenton Varda) -// Based on original Protocol Buffers design by -// Sanjay Ghemawat, Jeff Dean, and others. -// -// The messages in this file describe the definitions found in .proto files. -// A valid .proto file can be translated directly to a FileDescriptorProto -// without any other information (e.g. without reading its imports). - - -syntax = "proto2"; - -package google.protobuf; -option go_package = "github.com/golang/protobuf/protoc-gen-go/descriptor;descriptor"; -option java_package = "com.google.protobuf"; -option java_outer_classname = "DescriptorProtos"; -option csharp_namespace = "Google.Protobuf.Reflection"; -option objc_class_prefix = "GPB"; -option cc_enable_arenas = true; - -// descriptor.proto must be optimized for speed because reflection-based -// algorithms don't work during bootstrapping. -option optimize_for = SPEED; - -// The protocol compiler can output a FileDescriptorSet containing the .proto -// files it parses. -message FileDescriptorSet { - repeated FileDescriptorProto file = 1; -} - -// Describes a complete .proto file. -message FileDescriptorProto { - optional string name = 1; // file name, relative to root of source tree - optional string package = 2; // e.g. "foo", "foo.bar", etc. - - // Names of files imported by this file. - repeated string dependency = 3; - // Indexes of the public imported files in the dependency list above. - repeated int32 public_dependency = 10; - // Indexes of the weak imported files in the dependency list. - // For Google-internal migration only. Do not use. - repeated int32 weak_dependency = 11; - - // All top-level definitions in this file. - repeated DescriptorProto message_type = 4; - repeated EnumDescriptorProto enum_type = 5; - repeated ServiceDescriptorProto service = 6; - repeated FieldDescriptorProto extension = 7; - - optional FileOptions options = 8; - - // This field contains optional information about the original source code. - // You may safely remove this entire field without harming runtime - // functionality of the descriptors -- the information is needed only by - // development tools. - optional SourceCodeInfo source_code_info = 9; - - // The syntax of the proto file. - // The supported values are "proto2" and "proto3". - optional string syntax = 12; -} - -// Describes a message type. -message DescriptorProto { - optional string name = 1; - - repeated FieldDescriptorProto field = 2; - repeated FieldDescriptorProto extension = 6; - - repeated DescriptorProto nested_type = 3; - repeated EnumDescriptorProto enum_type = 4; - - message ExtensionRange { - optional int32 start = 1; - optional int32 end = 2; - - optional ExtensionRangeOptions options = 3; - } - repeated ExtensionRange extension_range = 5; - - repeated OneofDescriptorProto oneof_decl = 8; - - optional MessageOptions options = 7; - - // Range of reserved tag numbers. Reserved tag numbers may not be used by - // fields or extension ranges in the same message. Reserved ranges may - // not overlap. - message ReservedRange { - optional int32 start = 1; // Inclusive. - optional int32 end = 2; // Exclusive. - } - repeated ReservedRange reserved_range = 9; - // Reserved field names, which may not be used by fields in the same message. - // A given name may only be reserved once. - repeated string reserved_name = 10; -} - -message ExtensionRangeOptions { - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - -// Describes a field within a message. -message FieldDescriptorProto { - enum Type { - // 0 is reserved for errors. - // Order is weird for historical reasons. - TYPE_DOUBLE = 1; - TYPE_FLOAT = 2; - // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if - // negative values are likely. - TYPE_INT64 = 3; - TYPE_UINT64 = 4; - // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if - // negative values are likely. - TYPE_INT32 = 5; - TYPE_FIXED64 = 6; - TYPE_FIXED32 = 7; - TYPE_BOOL = 8; - TYPE_STRING = 9; - // Tag-delimited aggregate. - // Group type is deprecated and not supported in proto3. However, Proto3 - // implementations should still be able to parse the group wire format and - // treat group fields as unknown fields. - TYPE_GROUP = 10; - TYPE_MESSAGE = 11; // Length-delimited aggregate. - - // New in version 2. - TYPE_BYTES = 12; - TYPE_UINT32 = 13; - TYPE_ENUM = 14; - TYPE_SFIXED32 = 15; - TYPE_SFIXED64 = 16; - TYPE_SINT32 = 17; // Uses ZigZag encoding. - TYPE_SINT64 = 18; // Uses ZigZag encoding. - }; - - enum Label { - // 0 is reserved for errors - LABEL_OPTIONAL = 1; - LABEL_REQUIRED = 2; - LABEL_REPEATED = 3; - }; - - optional string name = 1; - optional int32 number = 3; - optional Label label = 4; - - // If type_name is set, this need not be set. If both this and type_name - // are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP. - optional Type type = 5; - - // For message and enum types, this is the name of the type. If the name - // starts with a '.', it is fully-qualified. Otherwise, C++-like scoping - // rules are used to find the type (i.e. first the nested types within this - // message are searched, then within the parent, on up to the root - // namespace). - optional string type_name = 6; - - // For extensions, this is the name of the type being extended. It is - // resolved in the same manner as type_name. - optional string extendee = 2; - - // For numeric types, contains the original text representation of the value. - // For booleans, "true" or "false". - // For strings, contains the default text contents (not escaped in any way). - // For bytes, contains the C escaped value. All bytes >= 128 are escaped. - // TODO(kenton): Base-64 encode? - optional string default_value = 7; - - // If set, gives the index of a oneof in the containing type's oneof_decl - // list. This field is a member of that oneof. - optional int32 oneof_index = 9; - - // JSON name of this field. The value is set by protocol compiler. If the - // user has set a "json_name" option on this field, that option's value - // will be used. Otherwise, it's deduced from the field's name by converting - // it to camelCase. - optional string json_name = 10; - - optional FieldOptions options = 8; -} - -// Describes a oneof. -message OneofDescriptorProto { - optional string name = 1; - optional OneofOptions options = 2; -} - -// Describes an enum type. -message EnumDescriptorProto { - optional string name = 1; - - repeated EnumValueDescriptorProto value = 2; - - optional EnumOptions options = 3; - - // Range of reserved numeric values. Reserved values may not be used by - // entries in the same enum. Reserved ranges may not overlap. - // - // Note that this is distinct from DescriptorProto.ReservedRange in that it - // is inclusive such that it can appropriately represent the entire int32 - // domain. - message EnumReservedRange { - optional int32 start = 1; // Inclusive. - optional int32 end = 2; // Inclusive. - } - - // Range of reserved numeric values. Reserved numeric values may not be used - // by enum values in the same enum declaration. Reserved ranges may not - // overlap. - repeated EnumReservedRange reserved_range = 4; - - // Reserved enum value names, which may not be reused. A given name may only - // be reserved once. - repeated string reserved_name = 5; -} - -// Describes a value within an enum. -message EnumValueDescriptorProto { - optional string name = 1; - optional int32 number = 2; - - optional EnumValueOptions options = 3; -} - -// Describes a service. -message ServiceDescriptorProto { - optional string name = 1; - repeated MethodDescriptorProto method = 2; - - optional ServiceOptions options = 3; -} - -// Describes a method of a service. -message MethodDescriptorProto { - optional string name = 1; - - // Input and output type names. These are resolved in the same way as - // FieldDescriptorProto.type_name, but must refer to a message type. - optional string input_type = 2; - optional string output_type = 3; - - optional MethodOptions options = 4; - - // Identifies if client streams multiple client messages - optional bool client_streaming = 5 [default=false]; - // Identifies if server streams multiple server messages - optional bool server_streaming = 6 [default=false]; -} - - -// =================================================================== -// Options - -// Each of the definitions above may have "options" attached. These are -// just annotations which may cause code to be generated slightly differently -// or may contain hints for code that manipulates protocol messages. -// -// Clients may define custom options as extensions of the *Options messages. -// These extensions may not yet be known at parsing time, so the parser cannot -// store the values in them. Instead it stores them in a field in the *Options -// message called uninterpreted_option. This field must have the same name -// across all *Options messages. We then use this field to populate the -// extensions when we build a descriptor, at which point all protos have been -// parsed and so all extensions are known. -// -// Extension numbers for custom options may be chosen as follows: -// * For options which will only be used within a single application or -// organization, or for experimental options, use field numbers 50000 -// through 99999. It is up to you to ensure that you do not use the -// same number for multiple options. -// * For options which will be published and used publicly by multiple -// independent entities, e-mail protobuf-global-extension-registry@google.com -// to reserve extension numbers. Simply provide your project name (e.g. -// Objective-C plugin) and your project website (if available) -- there's no -// need to explain how you intend to use them. Usually you only need one -// extension number. You can declare multiple options with only one extension -// number by putting them in a sub-message. See the Custom Options section of -// the docs for examples: -// https://developers.google.com/protocol-buffers/docs/proto#options -// If this turns out to be popular, a web service will be set up -// to automatically assign option numbers. - - -message FileOptions { - - // Sets the Java package where classes generated from this .proto will be - // placed. By default, the proto package is used, but this is often - // inappropriate because proto packages do not normally start with backwards - // domain names. - optional string java_package = 1; - - - // If set, all the classes from the .proto file are wrapped in a single - // outer class with the given name. This applies to both Proto1 - // (equivalent to the old "--one_java_file" option) and Proto2 (where - // a .proto always translates to a single class, but you may want to - // explicitly choose the class name). - optional string java_outer_classname = 8; - - // If set true, then the Java code generator will generate a separate .java - // file for each top-level message, enum, and service defined in the .proto - // file. Thus, these types will *not* be nested inside the outer class - // named by java_outer_classname. However, the outer class will still be - // generated to contain the file's getDescriptor() method as well as any - // top-level extensions defined in the file. - optional bool java_multiple_files = 10 [default=false]; - - // This option does nothing. - optional bool java_generate_equals_and_hash = 20 [deprecated=true]; - - // If set true, then the Java2 code generator will generate code that - // throws an exception whenever an attempt is made to assign a non-UTF-8 - // byte sequence to a string field. - // Message reflection will do the same. - // However, an extension field still accepts non-UTF-8 byte sequences. - // This option has no effect on when used with the lite runtime. - optional bool java_string_check_utf8 = 27 [default=false]; - - - // Generated classes can be optimized for speed or code size. - enum OptimizeMode { - SPEED = 1; // Generate complete code for parsing, serialization, - // etc. - CODE_SIZE = 2; // Use ReflectionOps to implement these methods. - LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime. - } - optional OptimizeMode optimize_for = 9 [default=SPEED]; - - // Sets the Go package where structs generated from this .proto will be - // placed. If omitted, the Go package will be derived from the following: - // - The basename of the package import path, if provided. - // - Otherwise, the package statement in the .proto file, if present. - // - Otherwise, the basename of the .proto file, without extension. - optional string go_package = 11; - - - - // Should generic services be generated in each language? "Generic" services - // are not specific to any particular RPC system. They are generated by the - // main code generators in each language (without additional plugins). - // Generic services were the only kind of service generation supported by - // early versions of google.protobuf. - // - // Generic services are now considered deprecated in favor of using plugins - // that generate code specific to your particular RPC system. Therefore, - // these default to false. Old code which depends on generic services should - // explicitly set them to true. - optional bool cc_generic_services = 16 [default=false]; - optional bool java_generic_services = 17 [default=false]; - optional bool py_generic_services = 18 [default=false]; - optional bool php_generic_services = 42 [default=false]; - - // Is this file deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for everything in the file, or it will be completely ignored; in the very - // least, this is a formalization for deprecating files. - optional bool deprecated = 23 [default=false]; - - // Enables the use of arenas for the proto messages in this file. This applies - // only to generated classes for C++. - optional bool cc_enable_arenas = 31 [default=false]; - - - // Sets the objective c class prefix which is prepended to all objective c - // generated classes from this .proto. There is no default. - optional string objc_class_prefix = 36; - - // Namespace for generated classes; defaults to the package. - optional string csharp_namespace = 37; - - // By default Swift generators will take the proto package and CamelCase it - // replacing '.' with underscore and use that to prefix the types/symbols - // defined. When this options is provided, they will use this value instead - // to prefix the types/symbols defined. - optional string swift_prefix = 39; - - // Sets the php class prefix which is prepended to all php generated classes - // from this .proto. Default is empty. - optional string php_class_prefix = 40; - - // Use this option to change the namespace of php generated classes. Default - // is empty. When this option is empty, the package name will be used for - // determining the namespace. - optional string php_namespace = 41; - - // The parser stores options it doesn't recognize here. - // See the documentation for the "Options" section above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. - // See the documentation for the "Options" section above. - extensions 1000 to max; - - reserved 38; -} - -message MessageOptions { - // Set true to use the old proto1 MessageSet wire format for extensions. - // This is provided for backwards-compatibility with the MessageSet wire - // format. You should not use this for any other reason: It's less - // efficient, has fewer features, and is more complicated. - // - // The message must be defined exactly as follows: - // message Foo { - // option message_set_wire_format = true; - // extensions 4 to max; - // } - // Note that the message cannot have any defined fields; MessageSets only - // have extensions. - // - // All extensions of your type must be singular messages; e.g. they cannot - // be int32s, enums, or repeated messages. - // - // Because this is an option, the above two restrictions are not enforced by - // the protocol compiler. - optional bool message_set_wire_format = 1 [default=false]; - - // Disables the generation of the standard "descriptor()" accessor, which can - // conflict with a field of the same name. This is meant to make migration - // from proto1 easier; new code should avoid fields named "descriptor". - optional bool no_standard_descriptor_accessor = 2 [default=false]; - - // Is this message deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the message, or it will be completely ignored; in the very least, - // this is a formalization for deprecating messages. - optional bool deprecated = 3 [default=false]; - - // Whether the message is an automatically generated map entry type for the - // maps field. - // - // For maps fields: - // map map_field = 1; - // The parsed descriptor looks like: - // message MapFieldEntry { - // option map_entry = true; - // optional KeyType key = 1; - // optional ValueType value = 2; - // } - // repeated MapFieldEntry map_field = 1; - // - // Implementations may choose not to generate the map_entry=true message, but - // use a native map in the target language to hold the keys and values. - // The reflection APIs in such implementions still need to work as - // if the field is a repeated message field. - // - // NOTE: Do not set the option in .proto files. Always use the maps syntax - // instead. The option should only be implicitly set by the proto compiler - // parser. - optional bool map_entry = 7; - - reserved 8; // javalite_serializable - reserved 9; // javanano_as_lite - - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - -message FieldOptions { - // The ctype option instructs the C++ code generator to use a different - // representation of the field than it normally would. See the specific - // options below. This option is not yet implemented in the open source - // release -- sorry, we'll try to include it in a future version! - optional CType ctype = 1 [default = STRING]; - enum CType { - // Default mode. - STRING = 0; - - CORD = 1; - - STRING_PIECE = 2; - } - // The packed option can be enabled for repeated primitive fields to enable - // a more efficient representation on the wire. Rather than repeatedly - // writing the tag and type for each element, the entire array is encoded as - // a single length-delimited blob. In proto3, only explicit setting it to - // false will avoid using packed encoding. - optional bool packed = 2; - - // The jstype option determines the JavaScript type used for values of the - // field. The option is permitted only for 64 bit integral and fixed types - // (int64, uint64, sint64, fixed64, sfixed64). A field with jstype JS_STRING - // is represented as JavaScript string, which avoids loss of precision that - // can happen when a large value is converted to a floating point JavaScript. - // Specifying JS_NUMBER for the jstype causes the generated JavaScript code to - // use the JavaScript "number" type. The behavior of the default option - // JS_NORMAL is implementation dependent. - // - // This option is an enum to permit additional types to be added, e.g. - // goog.math.Integer. - optional JSType jstype = 6 [default = JS_NORMAL]; - enum JSType { - // Use the default type. - JS_NORMAL = 0; - - // Use JavaScript strings. - JS_STRING = 1; - - // Use JavaScript numbers. - JS_NUMBER = 2; - } - - // Should this field be parsed lazily? Lazy applies only to message-type - // fields. It means that when the outer message is initially parsed, the - // inner message's contents will not be parsed but instead stored in encoded - // form. The inner message will actually be parsed when it is first accessed. - // - // This is only a hint. Implementations are free to choose whether to use - // eager or lazy parsing regardless of the value of this option. However, - // setting this option true suggests that the protocol author believes that - // using lazy parsing on this field is worth the additional bookkeeping - // overhead typically needed to implement it. - // - // This option does not affect the public interface of any generated code; - // all method signatures remain the same. Furthermore, thread-safety of the - // interface is not affected by this option; const methods remain safe to - // call from multiple threads concurrently, while non-const methods continue - // to require exclusive access. - // - // - // Note that implementations may choose not to check required fields within - // a lazy sub-message. That is, calling IsInitialized() on the outer message - // may return true even if the inner message has missing required fields. - // This is necessary because otherwise the inner message would have to be - // parsed in order to perform the check, defeating the purpose of lazy - // parsing. An implementation which chooses not to check required fields - // must be consistent about it. That is, for any particular sub-message, the - // implementation must either *always* check its required fields, or *never* - // check its required fields, regardless of whether or not the message has - // been parsed. - optional bool lazy = 5 [default=false]; - - // Is this field deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for accessors, or it will be completely ignored; in the very least, this - // is a formalization for deprecating fields. - optional bool deprecated = 3 [default=false]; - - // For Google-internal migration only. Do not use. - optional bool weak = 10 [default=false]; - - - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; - - reserved 4; // removed jtype -} - -message OneofOptions { - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - -message EnumOptions { - - // Set this option to true to allow mapping different tag names to the same - // value. - optional bool allow_alias = 2; - - // Is this enum deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the enum, or it will be completely ignored; in the very least, this - // is a formalization for deprecating enums. - optional bool deprecated = 3 [default=false]; - - reserved 5; // javanano_as_lite - - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - -message EnumValueOptions { - // Is this enum value deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the enum value, or it will be completely ignored; in the very least, - // this is a formalization for deprecating enum values. - optional bool deprecated = 1 [default=false]; - - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - -message ServiceOptions { - - // Note: Field numbers 1 through 32 are reserved for Google's internal RPC - // framework. We apologize for hoarding these numbers to ourselves, but - // we were already using them long before we decided to release Protocol - // Buffers. - - // Is this service deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the service, or it will be completely ignored; in the very least, - // this is a formalization for deprecating services. - optional bool deprecated = 33 [default=false]; - - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - -message MethodOptions { - - // Note: Field numbers 1 through 32 are reserved for Google's internal RPC - // framework. We apologize for hoarding these numbers to ourselves, but - // we were already using them long before we decided to release Protocol - // Buffers. - - // Is this method deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the method, or it will be completely ignored; in the very least, - // this is a formalization for deprecating methods. - optional bool deprecated = 33 [default=false]; - - // Is this method side-effect-free (or safe in HTTP parlance), or idempotent, - // or neither? HTTP based RPC implementation may choose GET verb for safe - // methods, and PUT verb for idempotent methods instead of the default POST. - enum IdempotencyLevel { - IDEMPOTENCY_UNKNOWN = 0; - NO_SIDE_EFFECTS = 1; // implies idempotent - IDEMPOTENT = 2; // idempotent, but may have side effects - } - optional IdempotencyLevel idempotency_level = - 34 [default=IDEMPOTENCY_UNKNOWN]; - - // The parser stores options it doesn't recognize here. See above. - repeated UninterpretedOption uninterpreted_option = 999; - - // Clients can define custom options in extensions of this message. See above. - extensions 1000 to max; -} - - -// A message representing a option the parser does not recognize. This only -// appears in options protos created by the compiler::Parser class. -// DescriptorPool resolves these when building Descriptor objects. Therefore, -// options protos in descriptor objects (e.g. returned by Descriptor::options(), -// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions -// in them. -message UninterpretedOption { - // The name of the uninterpreted option. Each string represents a segment in - // a dot-separated name. is_extension is true iff a segment represents an - // extension (denoted with parentheses in options specs in .proto files). - // E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents - // "foo.(bar.baz).qux". - message NamePart { - required string name_part = 1; - required bool is_extension = 2; - } - repeated NamePart name = 2; - - // The value of the uninterpreted option, in whatever type the tokenizer - // identified it as during parsing. Exactly one of these should be set. - optional string identifier_value = 3; - optional uint64 positive_int_value = 4; - optional int64 negative_int_value = 5; - optional double double_value = 6; - optional bytes string_value = 7; - optional string aggregate_value = 8; -} - -// =================================================================== -// Optional source code info - -// Encapsulates information about the original source file from which a -// FileDescriptorProto was generated. -message SourceCodeInfo { - // A Location identifies a piece of source code in a .proto file which - // corresponds to a particular definition. This information is intended - // to be useful to IDEs, code indexers, documentation generators, and similar - // tools. - // - // For example, say we have a file like: - // message Foo { - // optional string foo = 1; - // } - // Let's look at just the field definition: - // optional string foo = 1; - // ^ ^^ ^^ ^ ^^^ - // a bc de f ghi - // We have the following locations: - // span path represents - // [a,i) [ 4, 0, 2, 0 ] The whole field definition. - // [a,b) [ 4, 0, 2, 0, 4 ] The label (optional). - // [c,d) [ 4, 0, 2, 0, 5 ] The type (string). - // [e,f) [ 4, 0, 2, 0, 1 ] The name (foo). - // [g,h) [ 4, 0, 2, 0, 3 ] The number (1). - // - // Notes: - // - A location may refer to a repeated field itself (i.e. not to any - // particular index within it). This is used whenever a set of elements are - // logically enclosed in a single code segment. For example, an entire - // extend block (possibly containing multiple extension definitions) will - // have an outer location whose path refers to the "extensions" repeated - // field without an index. - // - Multiple locations may have the same path. This happens when a single - // logical declaration is spread out across multiple places. The most - // obvious example is the "extend" block again -- there may be multiple - // extend blocks in the same scope, each of which will have the same path. - // - A location's span is not always a subset of its parent's span. For - // example, the "extendee" of an extension declaration appears at the - // beginning of the "extend" block and is shared by all extensions within - // the block. - // - Just because a location's span is a subset of some other location's span - // does not mean that it is a descendent. For example, a "group" defines - // both a type and a field in a single declaration. Thus, the locations - // corresponding to the type and field and their components will overlap. - // - Code which tries to interpret locations should probably be designed to - // ignore those that it doesn't understand, as more types of locations could - // be recorded in the future. - repeated Location location = 1; - message Location { - // Identifies which part of the FileDescriptorProto was defined at this - // location. - // - // Each element is a field number or an index. They form a path from - // the root FileDescriptorProto to the place where the definition. For - // example, this path: - // [ 4, 3, 2, 7, 1 ] - // refers to: - // file.message_type(3) // 4, 3 - // .field(7) // 2, 7 - // .name() // 1 - // This is because FileDescriptorProto.message_type has field number 4: - // repeated DescriptorProto message_type = 4; - // and DescriptorProto.field has field number 2: - // repeated FieldDescriptorProto field = 2; - // and FieldDescriptorProto.name has field number 1: - // optional string name = 1; - // - // Thus, the above path gives the location of a field name. If we removed - // the last element: - // [ 4, 3, 2, 7 ] - // this path refers to the whole field declaration (from the beginning - // of the label to the terminating semicolon). - repeated int32 path = 1 [packed=true]; - - // Always has exactly three or four elements: start line, start column, - // end line (optional, otherwise assumed same as start line), end column. - // These are packed into a single field for efficiency. Note that line - // and column numbers are zero-based -- typically you will want to add - // 1 to each before displaying to a user. - repeated int32 span = 2 [packed=true]; - - // If this SourceCodeInfo represents a complete declaration, these are any - // comments appearing before and after the declaration which appear to be - // attached to the declaration. - // - // A series of line comments appearing on consecutive lines, with no other - // tokens appearing on those lines, will be treated as a single comment. - // - // leading_detached_comments will keep paragraphs of comments that appear - // before (but not connected to) the current element. Each paragraph, - // separated by empty lines, will be one comment element in the repeated - // field. - // - // Only the comment content is provided; comment markers (e.g. //) are - // stripped out. For block comments, leading whitespace and an asterisk - // will be stripped from the beginning of each line other than the first. - // Newlines are included in the output. - // - // Examples: - // - // optional int32 foo = 1; // Comment attached to foo. - // // Comment attached to bar. - // optional int32 bar = 2; - // - // optional string baz = 3; - // // Comment attached to baz. - // // Another line attached to baz. - // - // // Comment attached to qux. - // // - // // Another line attached to qux. - // optional double qux = 4; - // - // // Detached comment for corge. This is not leading or trailing comments - // // to qux or corge because there are blank lines separating it from - // // both. - // - // // Detached comment for corge paragraph 2. - // - // optional string corge = 5; - // /* Block comment attached - // * to corge. Leading asterisks - // * will be removed. */ - // /* Block comment attached to - // * grault. */ - // optional int32 grault = 6; - // - // // ignored detached comments. - optional string leading_comments = 3; - optional string trailing_comments = 4; - repeated string leading_detached_comments = 6; - } -} - -// Describes the relationship between generated code and its original source -// file. A GeneratedCodeInfo message is associated with only one generated -// source file, but may contain references to different source .proto files. -message GeneratedCodeInfo { - // An Annotation connects some span of text in generated code to an element - // of its generating .proto file. - repeated Annotation annotation = 1; - message Annotation { - // Identifies the element in the original source .proto file. This field - // is formatted the same as SourceCodeInfo.Location.path. - repeated int32 path = 1 [packed=true]; - - // Identifies the filesystem path to the original source .proto. - optional string source_file = 2; - - // Identifies the starting offset in bytes in the generated code - // that relates to the identified object. - optional int32 begin = 3; - - // Identifies the ending offset in bytes in the generated code that - // relates to the identified offset. The end offset should be one past - // the last relevant byte (so the length of the text = end - begin). - optional int32 end = 4; - } -} +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// The messages in this file describe the definitions found in .proto files. +// A valid .proto file can be translated directly to a FileDescriptorProto +// without any other information (e.g. without reading its imports). + + +syntax = "proto2"; + +package google.protobuf; +option go_package = "github.com/golang/protobuf/protoc-gen-go/descriptor;descriptor"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "DescriptorProtos"; +option csharp_namespace = "Google.Protobuf.Reflection"; +option objc_class_prefix = "GPB"; +option cc_enable_arenas = true; + +// descriptor.proto must be optimized for speed because reflection-based +// algorithms don't work during bootstrapping. +option optimize_for = SPEED; + +// The protocol compiler can output a FileDescriptorSet containing the .proto +// files it parses. +message FileDescriptorSet { + repeated FileDescriptorProto file = 1; +} + +// Describes a complete .proto file. +message FileDescriptorProto { + optional string name = 1; // file name, relative to root of source tree + optional string package = 2; // e.g. "foo", "foo.bar", etc. + + // Names of files imported by this file. + repeated string dependency = 3; + // Indexes of the public imported files in the dependency list above. + repeated int32 public_dependency = 10; + // Indexes of the weak imported files in the dependency list. + // For Google-internal migration only. Do not use. + repeated int32 weak_dependency = 11; + + // All top-level definitions in this file. + repeated DescriptorProto message_type = 4; + repeated EnumDescriptorProto enum_type = 5; + repeated ServiceDescriptorProto service = 6; + repeated FieldDescriptorProto extension = 7; + + optional FileOptions options = 8; + + // This field contains optional information about the original source code. + // You may safely remove this entire field without harming runtime + // functionality of the descriptors -- the information is needed only by + // development tools. + optional SourceCodeInfo source_code_info = 9; + + // The syntax of the proto file. + // The supported values are "proto2" and "proto3". + optional string syntax = 12; +} + +// Describes a message type. +message DescriptorProto { + optional string name = 1; + + repeated FieldDescriptorProto field = 2; + repeated FieldDescriptorProto extension = 6; + + repeated DescriptorProto nested_type = 3; + repeated EnumDescriptorProto enum_type = 4; + + message ExtensionRange { + optional int32 start = 1; + optional int32 end = 2; + + optional ExtensionRangeOptions options = 3; + } + repeated ExtensionRange extension_range = 5; + + repeated OneofDescriptorProto oneof_decl = 8; + + optional MessageOptions options = 7; + + // Range of reserved tag numbers. Reserved tag numbers may not be used by + // fields or extension ranges in the same message. Reserved ranges may + // not overlap. + message ReservedRange { + optional int32 start = 1; // Inclusive. + optional int32 end = 2; // Exclusive. + } + repeated ReservedRange reserved_range = 9; + // Reserved field names, which may not be used by fields in the same message. + // A given name may only be reserved once. + repeated string reserved_name = 10; +} + +message ExtensionRangeOptions { + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +// Describes a field within a message. +message FieldDescriptorProto { + enum Type { + // 0 is reserved for errors. + // Order is weird for historical reasons. + TYPE_DOUBLE = 1; + TYPE_FLOAT = 2; + // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if + // negative values are likely. + TYPE_INT64 = 3; + TYPE_UINT64 = 4; + // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if + // negative values are likely. + TYPE_INT32 = 5; + TYPE_FIXED64 = 6; + TYPE_FIXED32 = 7; + TYPE_BOOL = 8; + TYPE_STRING = 9; + // Tag-delimited aggregate. + // Group type is deprecated and not supported in proto3. However, Proto3 + // implementations should still be able to parse the group wire format and + // treat group fields as unknown fields. + TYPE_GROUP = 10; + TYPE_MESSAGE = 11; // Length-delimited aggregate. + + // New in version 2. + TYPE_BYTES = 12; + TYPE_UINT32 = 13; + TYPE_ENUM = 14; + TYPE_SFIXED32 = 15; + TYPE_SFIXED64 = 16; + TYPE_SINT32 = 17; // Uses ZigZag encoding. + TYPE_SINT64 = 18; // Uses ZigZag encoding. + }; + + enum Label { + // 0 is reserved for errors + LABEL_OPTIONAL = 1; + LABEL_REQUIRED = 2; + LABEL_REPEATED = 3; + }; + + optional string name = 1; + optional int32 number = 3; + optional Label label = 4; + + // If type_name is set, this need not be set. If both this and type_name + // are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP. + optional Type type = 5; + + // For message and enum types, this is the name of the type. If the name + // starts with a '.', it is fully-qualified. Otherwise, C++-like scoping + // rules are used to find the type (i.e. first the nested types within this + // message are searched, then within the parent, on up to the root + // namespace). + optional string type_name = 6; + + // For extensions, this is the name of the type being extended. It is + // resolved in the same manner as type_name. + optional string extendee = 2; + + // For numeric types, contains the original text representation of the value. + // For booleans, "true" or "false". + // For strings, contains the default text contents (not escaped in any way). + // For bytes, contains the C escaped value. All bytes >= 128 are escaped. + // TODO(kenton): Base-64 encode? + optional string default_value = 7; + + // If set, gives the index of a oneof in the containing type's oneof_decl + // list. This field is a member of that oneof. + optional int32 oneof_index = 9; + + // JSON name of this field. The value is set by protocol compiler. If the + // user has set a "json_name" option on this field, that option's value + // will be used. Otherwise, it's deduced from the field's name by converting + // it to camelCase. + optional string json_name = 10; + + optional FieldOptions options = 8; +} + +// Describes a oneof. +message OneofDescriptorProto { + optional string name = 1; + optional OneofOptions options = 2; +} + +// Describes an enum type. +message EnumDescriptorProto { + optional string name = 1; + + repeated EnumValueDescriptorProto value = 2; + + optional EnumOptions options = 3; + + // Range of reserved numeric values. Reserved values may not be used by + // entries in the same enum. Reserved ranges may not overlap. + // + // Note that this is distinct from DescriptorProto.ReservedRange in that it + // is inclusive such that it can appropriately represent the entire int32 + // domain. + message EnumReservedRange { + optional int32 start = 1; // Inclusive. + optional int32 end = 2; // Inclusive. + } + + // Range of reserved numeric values. Reserved numeric values may not be used + // by enum values in the same enum declaration. Reserved ranges may not + // overlap. + repeated EnumReservedRange reserved_range = 4; + + // Reserved enum value names, which may not be reused. A given name may only + // be reserved once. + repeated string reserved_name = 5; +} + +// Describes a value within an enum. +message EnumValueDescriptorProto { + optional string name = 1; + optional int32 number = 2; + + optional EnumValueOptions options = 3; +} + +// Describes a service. +message ServiceDescriptorProto { + optional string name = 1; + repeated MethodDescriptorProto method = 2; + + optional ServiceOptions options = 3; +} + +// Describes a method of a service. +message MethodDescriptorProto { + optional string name = 1; + + // Input and output type names. These are resolved in the same way as + // FieldDescriptorProto.type_name, but must refer to a message type. + optional string input_type = 2; + optional string output_type = 3; + + optional MethodOptions options = 4; + + // Identifies if client streams multiple client messages + optional bool client_streaming = 5 [default=false]; + // Identifies if server streams multiple server messages + optional bool server_streaming = 6 [default=false]; +} + + +// =================================================================== +// Options + +// Each of the definitions above may have "options" attached. These are +// just annotations which may cause code to be generated slightly differently +// or may contain hints for code that manipulates protocol messages. +// +// Clients may define custom options as extensions of the *Options messages. +// These extensions may not yet be known at parsing time, so the parser cannot +// store the values in them. Instead it stores them in a field in the *Options +// message called uninterpreted_option. This field must have the same name +// across all *Options messages. We then use this field to populate the +// extensions when we build a descriptor, at which point all protos have been +// parsed and so all extensions are known. +// +// Extension numbers for custom options may be chosen as follows: +// * For options which will only be used within a single application or +// organization, or for experimental options, use field numbers 50000 +// through 99999. It is up to you to ensure that you do not use the +// same number for multiple options. +// * For options which will be published and used publicly by multiple +// independent entities, e-mail protobuf-global-extension-registry@google.com +// to reserve extension numbers. Simply provide your project name (e.g. +// Objective-C plugin) and your project website (if available) -- there's no +// need to explain how you intend to use them. Usually you only need one +// extension number. You can declare multiple options with only one extension +// number by putting them in a sub-message. See the Custom Options section of +// the docs for examples: +// https://developers.google.com/protocol-buffers/docs/proto#options +// If this turns out to be popular, a web service will be set up +// to automatically assign option numbers. + + +message FileOptions { + + // Sets the Java package where classes generated from this .proto will be + // placed. By default, the proto package is used, but this is often + // inappropriate because proto packages do not normally start with backwards + // domain names. + optional string java_package = 1; + + + // If set, all the classes from the .proto file are wrapped in a single + // outer class with the given name. This applies to both Proto1 + // (equivalent to the old "--one_java_file" option) and Proto2 (where + // a .proto always translates to a single class, but you may want to + // explicitly choose the class name). + optional string java_outer_classname = 8; + + // If set true, then the Java code generator will generate a separate .java + // file for each top-level message, enum, and service defined in the .proto + // file. Thus, these types will *not* be nested inside the outer class + // named by java_outer_classname. However, the outer class will still be + // generated to contain the file's getDescriptor() method as well as any + // top-level extensions defined in the file. + optional bool java_multiple_files = 10 [default=false]; + + // This option does nothing. + optional bool java_generate_equals_and_hash = 20 [deprecated=true]; + + // If set true, then the Java2 code generator will generate code that + // throws an exception whenever an attempt is made to assign a non-UTF-8 + // byte sequence to a string field. + // Message reflection will do the same. + // However, an extension field still accepts non-UTF-8 byte sequences. + // This option has no effect on when used with the lite runtime. + optional bool java_string_check_utf8 = 27 [default=false]; + + + // Generated classes can be optimized for speed or code size. + enum OptimizeMode { + SPEED = 1; // Generate complete code for parsing, serialization, + // etc. + CODE_SIZE = 2; // Use ReflectionOps to implement these methods. + LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime. + } + optional OptimizeMode optimize_for = 9 [default=SPEED]; + + // Sets the Go package where structs generated from this .proto will be + // placed. If omitted, the Go package will be derived from the following: + // - The basename of the package import path, if provided. + // - Otherwise, the package statement in the .proto file, if present. + // - Otherwise, the basename of the .proto file, without extension. + optional string go_package = 11; + + + + // Should generic services be generated in each language? "Generic" services + // are not specific to any particular RPC system. They are generated by the + // main code generators in each language (without additional plugins). + // Generic services were the only kind of service generation supported by + // early versions of google.protobuf. + // + // Generic services are now considered deprecated in favor of using plugins + // that generate code specific to your particular RPC system. Therefore, + // these default to false. Old code which depends on generic services should + // explicitly set them to true. + optional bool cc_generic_services = 16 [default=false]; + optional bool java_generic_services = 17 [default=false]; + optional bool py_generic_services = 18 [default=false]; + optional bool php_generic_services = 42 [default=false]; + + // Is this file deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for everything in the file, or it will be completely ignored; in the very + // least, this is a formalization for deprecating files. + optional bool deprecated = 23 [default=false]; + + // Enables the use of arenas for the proto messages in this file. This applies + // only to generated classes for C++. + optional bool cc_enable_arenas = 31 [default=false]; + + + // Sets the objective c class prefix which is prepended to all objective c + // generated classes from this .proto. There is no default. + optional string objc_class_prefix = 36; + + // Namespace for generated classes; defaults to the package. + optional string csharp_namespace = 37; + + // By default Swift generators will take the proto package and CamelCase it + // replacing '.' with underscore and use that to prefix the types/symbols + // defined. When this options is provided, they will use this value instead + // to prefix the types/symbols defined. + optional string swift_prefix = 39; + + // Sets the php class prefix which is prepended to all php generated classes + // from this .proto. Default is empty. + optional string php_class_prefix = 40; + + // Use this option to change the namespace of php generated classes. Default + // is empty. When this option is empty, the package name will be used for + // determining the namespace. + optional string php_namespace = 41; + + // The parser stores options it doesn't recognize here. + // See the documentation for the "Options" section above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. + // See the documentation for the "Options" section above. + extensions 1000 to max; + + reserved 38; +} + +message MessageOptions { + // Set true to use the old proto1 MessageSet wire format for extensions. + // This is provided for backwards-compatibility with the MessageSet wire + // format. You should not use this for any other reason: It's less + // efficient, has fewer features, and is more complicated. + // + // The message must be defined exactly as follows: + // message Foo { + // option message_set_wire_format = true; + // extensions 4 to max; + // } + // Note that the message cannot have any defined fields; MessageSets only + // have extensions. + // + // All extensions of your type must be singular messages; e.g. they cannot + // be int32s, enums, or repeated messages. + // + // Because this is an option, the above two restrictions are not enforced by + // the protocol compiler. + optional bool message_set_wire_format = 1 [default=false]; + + // Disables the generation of the standard "descriptor()" accessor, which can + // conflict with a field of the same name. This is meant to make migration + // from proto1 easier; new code should avoid fields named "descriptor". + optional bool no_standard_descriptor_accessor = 2 [default=false]; + + // Is this message deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the message, or it will be completely ignored; in the very least, + // this is a formalization for deprecating messages. + optional bool deprecated = 3 [default=false]; + + // Whether the message is an automatically generated map entry type for the + // maps field. + // + // For maps fields: + // map map_field = 1; + // The parsed descriptor looks like: + // message MapFieldEntry { + // option map_entry = true; + // optional KeyType key = 1; + // optional ValueType value = 2; + // } + // repeated MapFieldEntry map_field = 1; + // + // Implementations may choose not to generate the map_entry=true message, but + // use a native map in the target language to hold the keys and values. + // The reflection APIs in such implementions still need to work as + // if the field is a repeated message field. + // + // NOTE: Do not set the option in .proto files. Always use the maps syntax + // instead. The option should only be implicitly set by the proto compiler + // parser. + optional bool map_entry = 7; + + reserved 8; // javalite_serializable + reserved 9; // javanano_as_lite + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message FieldOptions { + // The ctype option instructs the C++ code generator to use a different + // representation of the field than it normally would. See the specific + // options below. This option is not yet implemented in the open source + // release -- sorry, we'll try to include it in a future version! + optional CType ctype = 1 [default = STRING]; + enum CType { + // Default mode. + STRING = 0; + + CORD = 1; + + STRING_PIECE = 2; + } + // The packed option can be enabled for repeated primitive fields to enable + // a more efficient representation on the wire. Rather than repeatedly + // writing the tag and type for each element, the entire array is encoded as + // a single length-delimited blob. In proto3, only explicit setting it to + // false will avoid using packed encoding. + optional bool packed = 2; + + // The jstype option determines the JavaScript type used for values of the + // field. The option is permitted only for 64 bit integral and fixed types + // (int64, uint64, sint64, fixed64, sfixed64). A field with jstype JS_STRING + // is represented as JavaScript string, which avoids loss of precision that + // can happen when a large value is converted to a floating point JavaScript. + // Specifying JS_NUMBER for the jstype causes the generated JavaScript code to + // use the JavaScript "number" type. The behavior of the default option + // JS_NORMAL is implementation dependent. + // + // This option is an enum to permit additional types to be added, e.g. + // goog.math.Integer. + optional JSType jstype = 6 [default = JS_NORMAL]; + enum JSType { + // Use the default type. + JS_NORMAL = 0; + + // Use JavaScript strings. + JS_STRING = 1; + + // Use JavaScript numbers. + JS_NUMBER = 2; + } + + // Should this field be parsed lazily? Lazy applies only to message-type + // fields. It means that when the outer message is initially parsed, the + // inner message's contents will not be parsed but instead stored in encoded + // form. The inner message will actually be parsed when it is first accessed. + // + // This is only a hint. Implementations are free to choose whether to use + // eager or lazy parsing regardless of the value of this option. However, + // setting this option true suggests that the protocol author believes that + // using lazy parsing on this field is worth the additional bookkeeping + // overhead typically needed to implement it. + // + // This option does not affect the public interface of any generated code; + // all method signatures remain the same. Furthermore, thread-safety of the + // interface is not affected by this option; const methods remain safe to + // call from multiple threads concurrently, while non-const methods continue + // to require exclusive access. + // + // + // Note that implementations may choose not to check required fields within + // a lazy sub-message. That is, calling IsInitialized() on the outer message + // may return true even if the inner message has missing required fields. + // This is necessary because otherwise the inner message would have to be + // parsed in order to perform the check, defeating the purpose of lazy + // parsing. An implementation which chooses not to check required fields + // must be consistent about it. That is, for any particular sub-message, the + // implementation must either *always* check its required fields, or *never* + // check its required fields, regardless of whether or not the message has + // been parsed. + optional bool lazy = 5 [default=false]; + + // Is this field deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for accessors, or it will be completely ignored; in the very least, this + // is a formalization for deprecating fields. + optional bool deprecated = 3 [default=false]; + + // For Google-internal migration only. Do not use. + optional bool weak = 10 [default=false]; + + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; + + reserved 4; // removed jtype +} + +message OneofOptions { + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message EnumOptions { + + // Set this option to true to allow mapping different tag names to the same + // value. + optional bool allow_alias = 2; + + // Is this enum deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the enum, or it will be completely ignored; in the very least, this + // is a formalization for deprecating enums. + optional bool deprecated = 3 [default=false]; + + reserved 5; // javanano_as_lite + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message EnumValueOptions { + // Is this enum value deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the enum value, or it will be completely ignored; in the very least, + // this is a formalization for deprecating enum values. + optional bool deprecated = 1 [default=false]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message ServiceOptions { + + // Note: Field numbers 1 through 32 are reserved for Google's internal RPC + // framework. We apologize for hoarding these numbers to ourselves, but + // we were already using them long before we decided to release Protocol + // Buffers. + + // Is this service deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the service, or it will be completely ignored; in the very least, + // this is a formalization for deprecating services. + optional bool deprecated = 33 [default=false]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message MethodOptions { + + // Note: Field numbers 1 through 32 are reserved for Google's internal RPC + // framework. We apologize for hoarding these numbers to ourselves, but + // we were already using them long before we decided to release Protocol + // Buffers. + + // Is this method deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the method, or it will be completely ignored; in the very least, + // this is a formalization for deprecating methods. + optional bool deprecated = 33 [default=false]; + + // Is this method side-effect-free (or safe in HTTP parlance), or idempotent, + // or neither? HTTP based RPC implementation may choose GET verb for safe + // methods, and PUT verb for idempotent methods instead of the default POST. + enum IdempotencyLevel { + IDEMPOTENCY_UNKNOWN = 0; + NO_SIDE_EFFECTS = 1; // implies idempotent + IDEMPOTENT = 2; // idempotent, but may have side effects + } + optional IdempotencyLevel idempotency_level = + 34 [default=IDEMPOTENCY_UNKNOWN]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + + +// A message representing a option the parser does not recognize. This only +// appears in options protos created by the compiler::Parser class. +// DescriptorPool resolves these when building Descriptor objects. Therefore, +// options protos in descriptor objects (e.g. returned by Descriptor::options(), +// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions +// in them. +message UninterpretedOption { + // The name of the uninterpreted option. Each string represents a segment in + // a dot-separated name. is_extension is true iff a segment represents an + // extension (denoted with parentheses in options specs in .proto files). + // E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents + // "foo.(bar.baz).qux". + message NamePart { + required string name_part = 1; + required bool is_extension = 2; + } + repeated NamePart name = 2; + + // The value of the uninterpreted option, in whatever type the tokenizer + // identified it as during parsing. Exactly one of these should be set. + optional string identifier_value = 3; + optional uint64 positive_int_value = 4; + optional int64 negative_int_value = 5; + optional double double_value = 6; + optional bytes string_value = 7; + optional string aggregate_value = 8; +} + +// =================================================================== +// Optional source code info + +// Encapsulates information about the original source file from which a +// FileDescriptorProto was generated. +message SourceCodeInfo { + // A Location identifies a piece of source code in a .proto file which + // corresponds to a particular definition. This information is intended + // to be useful to IDEs, code indexers, documentation generators, and similar + // tools. + // + // For example, say we have a file like: + // message Foo { + // optional string foo = 1; + // } + // Let's look at just the field definition: + // optional string foo = 1; + // ^ ^^ ^^ ^ ^^^ + // a bc de f ghi + // We have the following locations: + // span path represents + // [a,i) [ 4, 0, 2, 0 ] The whole field definition. + // [a,b) [ 4, 0, 2, 0, 4 ] The label (optional). + // [c,d) [ 4, 0, 2, 0, 5 ] The type (string). + // [e,f) [ 4, 0, 2, 0, 1 ] The name (foo). + // [g,h) [ 4, 0, 2, 0, 3 ] The number (1). + // + // Notes: + // - A location may refer to a repeated field itself (i.e. not to any + // particular index within it). This is used whenever a set of elements are + // logically enclosed in a single code segment. For example, an entire + // extend block (possibly containing multiple extension definitions) will + // have an outer location whose path refers to the "extensions" repeated + // field without an index. + // - Multiple locations may have the same path. This happens when a single + // logical declaration is spread out across multiple places. The most + // obvious example is the "extend" block again -- there may be multiple + // extend blocks in the same scope, each of which will have the same path. + // - A location's span is not always a subset of its parent's span. For + // example, the "extendee" of an extension declaration appears at the + // beginning of the "extend" block and is shared by all extensions within + // the block. + // - Just because a location's span is a subset of some other location's span + // does not mean that it is a descendent. For example, a "group" defines + // both a type and a field in a single declaration. Thus, the locations + // corresponding to the type and field and their components will overlap. + // - Code which tries to interpret locations should probably be designed to + // ignore those that it doesn't understand, as more types of locations could + // be recorded in the future. + repeated Location location = 1; + message Location { + // Identifies which part of the FileDescriptorProto was defined at this + // location. + // + // Each element is a field number or an index. They form a path from + // the root FileDescriptorProto to the place where the definition. For + // example, this path: + // [ 4, 3, 2, 7, 1 ] + // refers to: + // file.message_type(3) // 4, 3 + // .field(7) // 2, 7 + // .name() // 1 + // This is because FileDescriptorProto.message_type has field number 4: + // repeated DescriptorProto message_type = 4; + // and DescriptorProto.field has field number 2: + // repeated FieldDescriptorProto field = 2; + // and FieldDescriptorProto.name has field number 1: + // optional string name = 1; + // + // Thus, the above path gives the location of a field name. If we removed + // the last element: + // [ 4, 3, 2, 7 ] + // this path refers to the whole field declaration (from the beginning + // of the label to the terminating semicolon). + repeated int32 path = 1 [packed=true]; + + // Always has exactly three or four elements: start line, start column, + // end line (optional, otherwise assumed same as start line), end column. + // These are packed into a single field for efficiency. Note that line + // and column numbers are zero-based -- typically you will want to add + // 1 to each before displaying to a user. + repeated int32 span = 2 [packed=true]; + + // If this SourceCodeInfo represents a complete declaration, these are any + // comments appearing before and after the declaration which appear to be + // attached to the declaration. + // + // A series of line comments appearing on consecutive lines, with no other + // tokens appearing on those lines, will be treated as a single comment. + // + // leading_detached_comments will keep paragraphs of comments that appear + // before (but not connected to) the current element. Each paragraph, + // separated by empty lines, will be one comment element in the repeated + // field. + // + // Only the comment content is provided; comment markers (e.g. //) are + // stripped out. For block comments, leading whitespace and an asterisk + // will be stripped from the beginning of each line other than the first. + // Newlines are included in the output. + // + // Examples: + // + // optional int32 foo = 1; // Comment attached to foo. + // // Comment attached to bar. + // optional int32 bar = 2; + // + // optional string baz = 3; + // // Comment attached to baz. + // // Another line attached to baz. + // + // // Comment attached to qux. + // // + // // Another line attached to qux. + // optional double qux = 4; + // + // // Detached comment for corge. This is not leading or trailing comments + // // to qux or corge because there are blank lines separating it from + // // both. + // + // // Detached comment for corge paragraph 2. + // + // optional string corge = 5; + // /* Block comment attached + // * to corge. Leading asterisks + // * will be removed. */ + // /* Block comment attached to + // * grault. */ + // optional int32 grault = 6; + // + // // ignored detached comments. + optional string leading_comments = 3; + optional string trailing_comments = 4; + repeated string leading_detached_comments = 6; + } +} + +// Describes the relationship between generated code and its original source +// file. A GeneratedCodeInfo message is associated with only one generated +// source file, but may contain references to different source .proto files. +message GeneratedCodeInfo { + // An Annotation connects some span of text in generated code to an element + // of its generating .proto file. + repeated Annotation annotation = 1; + message Annotation { + // Identifies the element in the original source .proto file. This field + // is formatted the same as SourceCodeInfo.Location.path. + repeated int32 path = 1 [packed=true]; + + // Identifies the filesystem path to the original source .proto. + optional string source_file = 2; + + // Identifies the starting offset in bytes in the generated code + // that relates to the identified object. + optional int32 begin = 3; + + // Identifies the ending offset in bytes in the generated code that + // relates to the identified offset. The end offset should be one past + // the last relevant byte (so the length of the text = end - begin). + optional int32 end = 4; + } +} diff --git a/nanopb/generator.bat b/nanopb/generator.bat index 1bb6dba..3ea722c 100644 --- a/nanopb/generator.bat +++ b/nanopb/generator.bat @@ -1,7 +1,7 @@ -cd /d %~dp0 - -D:\Workspaces_Smarthome\esp\nanopb\generator-bin\protoc.exe --nanopb_out=. DC1Config.proto -copy DC1Config.pb.h ..\include /y -copy DC1Config.pb.c ..\src /y -del DC1Config.pb.h -del DC1Config.pb.c +cd /d %~dp0 + +D:\Workspaces_Smarthome\esp\nanopb\generator-bin\protoc.exe --nanopb_out=. DC1Config.proto +copy DC1Config.pb.h ..\include /y +copy DC1Config.pb.c ..\src /y +del DC1Config.pb.h +del DC1Config.pb.c diff --git a/nanopb/nanopb.proto b/nanopb/nanopb.proto index e89eea1..978da53 100644 --- a/nanopb/nanopb.proto +++ b/nanopb/nanopb.proto @@ -1,124 +1,124 @@ -// Custom options for defining: -// - Maximum size of string/bytes -// - Maximum number of elements in array -// -// These are used by nanopb to generate statically allocable structures -// for memory-limited environments. - -syntax = "proto2"; -import "descriptor.proto"; - -option java_package = "fi.kapsi.koti.jpa.nanopb"; - -enum FieldType { - FT_DEFAULT = 0; // Automatically decide field type, generate static field if possible. - FT_CALLBACK = 1; // Always generate a callback field. - FT_POINTER = 4; // Always generate a dynamically allocated field. - FT_STATIC = 2; // Generate a static field or raise an exception if not possible. - FT_IGNORE = 3; // Ignore the field completely. - FT_INLINE = 5; // Legacy option, use the separate 'fixed_length' option instead -} - -enum IntSize { - IS_DEFAULT = 0; // Default, 32/64bit based on type in .proto - IS_8 = 8; - IS_16 = 16; - IS_32 = 32; - IS_64 = 64; -} - -enum TypenameMangling { - M_NONE = 0; // Default, no typename mangling - M_STRIP_PACKAGE = 1; // Strip current package name - M_FLATTEN = 2; // Only use last path component -} - -// This is the inner options message, which basically defines options for -// a field. When it is used in message or file scope, it applies to all -// fields. -message NanoPBOptions { - // Allocated size for 'bytes' and 'string' fields. - // For string fields, this should include the space for null terminator. - optional int32 max_size = 1; - - // Maximum length for 'string' fields. Setting this is equivalent - // to setting max_size to a value of length+1. - optional int32 max_length = 14; - - // Allocated number of entries in arrays ('repeated' fields) - optional int32 max_count = 2; - - // Size of integer fields. Can save some memory if you don't need - // full 32 bits for the value. - optional IntSize int_size = 7 [default = IS_DEFAULT]; - - // Force type of field (callback or static allocation) - optional FieldType type = 3 [default = FT_DEFAULT]; - - // Use long names for enums, i.e. EnumName_EnumValue. - optional bool long_names = 4 [default = true]; - - // Add 'packed' attribute to generated structs. - // Note: this cannot be used on CPUs that break on unaligned - // accesses to variables. - optional bool packed_struct = 5 [default = false]; - - // Add 'packed' attribute to generated enums. - optional bool packed_enum = 10 [default = false]; - - // Skip this message - optional bool skip_message = 6 [default = false]; - - // Generate oneof fields as normal optional fields instead of union. - optional bool no_unions = 8 [default = false]; - - // integer type tag for a message - optional uint32 msgid = 9; - - // decode oneof as anonymous union - optional bool anonymous_oneof = 11 [default = false]; - - // Proto3 singular field does not generate a "has_" flag - optional bool proto3 = 12 [default = false]; - - // Generate an enum->string mapping function (can take up lots of space). - optional bool enum_to_string = 13 [default = false]; - - // Generate bytes arrays with fixed length - optional bool fixed_length = 15 [default = false]; - - // Generate repeated field with fixed count - optional bool fixed_count = 16 [default = false]; - - // Mangle long names - optional TypenameMangling mangle_names = 17 [default = M_NONE]; -} - -// Extensions to protoc 'Descriptor' type in order to define options -// inside a .proto file. -// -// Protocol Buffers extension number registry -// -------------------------------- -// Project: Nanopb -// Contact: Petteri Aimonen -// Web site: http://kapsi.fi/~jpa/nanopb -// Extensions: 1010 (all types) -// -------------------------------- - -extend google.protobuf.FileOptions { - optional NanoPBOptions nanopb_fileopt = 1010; -} - -extend google.protobuf.MessageOptions { - optional NanoPBOptions nanopb_msgopt = 1010; -} - -extend google.protobuf.EnumOptions { - optional NanoPBOptions nanopb_enumopt = 1010; -} - -extend google.protobuf.FieldOptions { - optional NanoPBOptions nanopb = 1010; -} - - +// Custom options for defining: +// - Maximum size of string/bytes +// - Maximum number of elements in array +// +// These are used by nanopb to generate statically allocable structures +// for memory-limited environments. + +syntax = "proto2"; +import "descriptor.proto"; + +option java_package = "fi.kapsi.koti.jpa.nanopb"; + +enum FieldType { + FT_DEFAULT = 0; // Automatically decide field type, generate static field if possible. + FT_CALLBACK = 1; // Always generate a callback field. + FT_POINTER = 4; // Always generate a dynamically allocated field. + FT_STATIC = 2; // Generate a static field or raise an exception if not possible. + FT_IGNORE = 3; // Ignore the field completely. + FT_INLINE = 5; // Legacy option, use the separate 'fixed_length' option instead +} + +enum IntSize { + IS_DEFAULT = 0; // Default, 32/64bit based on type in .proto + IS_8 = 8; + IS_16 = 16; + IS_32 = 32; + IS_64 = 64; +} + +enum TypenameMangling { + M_NONE = 0; // Default, no typename mangling + M_STRIP_PACKAGE = 1; // Strip current package name + M_FLATTEN = 2; // Only use last path component +} + +// This is the inner options message, which basically defines options for +// a field. When it is used in message or file scope, it applies to all +// fields. +message NanoPBOptions { + // Allocated size for 'bytes' and 'string' fields. + // For string fields, this should include the space for null terminator. + optional int32 max_size = 1; + + // Maximum length for 'string' fields. Setting this is equivalent + // to setting max_size to a value of length+1. + optional int32 max_length = 14; + + // Allocated number of entries in arrays ('repeated' fields) + optional int32 max_count = 2; + + // Size of integer fields. Can save some memory if you don't need + // full 32 bits for the value. + optional IntSize int_size = 7 [default = IS_DEFAULT]; + + // Force type of field (callback or static allocation) + optional FieldType type = 3 [default = FT_DEFAULT]; + + // Use long names for enums, i.e. EnumName_EnumValue. + optional bool long_names = 4 [default = true]; + + // Add 'packed' attribute to generated structs. + // Note: this cannot be used on CPUs that break on unaligned + // accesses to variables. + optional bool packed_struct = 5 [default = false]; + + // Add 'packed' attribute to generated enums. + optional bool packed_enum = 10 [default = false]; + + // Skip this message + optional bool skip_message = 6 [default = false]; + + // Generate oneof fields as normal optional fields instead of union. + optional bool no_unions = 8 [default = false]; + + // integer type tag for a message + optional uint32 msgid = 9; + + // decode oneof as anonymous union + optional bool anonymous_oneof = 11 [default = false]; + + // Proto3 singular field does not generate a "has_" flag + optional bool proto3 = 12 [default = false]; + + // Generate an enum->string mapping function (can take up lots of space). + optional bool enum_to_string = 13 [default = false]; + + // Generate bytes arrays with fixed length + optional bool fixed_length = 15 [default = false]; + + // Generate repeated field with fixed count + optional bool fixed_count = 16 [default = false]; + + // Mangle long names + optional TypenameMangling mangle_names = 17 [default = M_NONE]; +} + +// Extensions to protoc 'Descriptor' type in order to define options +// inside a .proto file. +// +// Protocol Buffers extension number registry +// -------------------------------- +// Project: Nanopb +// Contact: Petteri Aimonen +// Web site: http://kapsi.fi/~jpa/nanopb +// Extensions: 1010 (all types) +// -------------------------------- + +extend google.protobuf.FileOptions { + optional NanoPBOptions nanopb_fileopt = 1010; +} + +extend google.protobuf.MessageOptions { + optional NanoPBOptions nanopb_msgopt = 1010; +} + +extend google.protobuf.EnumOptions { + optional NanoPBOptions nanopb_enumopt = 1010; +} + +extend google.protobuf.FieldOptions { + optional NanoPBOptions nanopb = 1010; +} + + diff --git a/platformio.ini b/platformio.ini index c92e4c6..cfed5c7 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,58 +1,58 @@ -; PlatformIO Project Configuration File -; -; Build options: build flags, source filter, extra scripting -; Upload options: custom port, speed and extra flags -; Library options: dependencies, extra library storages -; -; Please visit documentation for the other options and examples -; http://docs.platformio.org/en/stable/projectconf.html - -[platformio] -default_envs = dc1 - -[env] -framework = arduino -board = esp01_1m -board_build.f_cpu = 80000000L -board_build.flash_mode = dout -board_build.ldscript = eagle.flash.1m.ld - -; *** Esp8266 core for Arduino version 2.6.1 -platform = espressif8266@2.3.3 -build_flags = -D NDEBUG - -mtarget-align - -Wl,-Map,firmware.map -; -Wl,-Teagle.flash.1m.ld - -DBEARSSL_SSL_BASIC -; NONOSDK22x_190703 = 2.2.2-dev(38a443e) - -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703 -; lwIP 2 - Higher Bandwidth no Features - -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH -; VTABLES in Flash - -DVTABLES_IN_FLASH -; No exception code in firmware - -fno-exceptions - -lstdc++ - - -D MQTT_MAX_PACKET_SIZE=768 - -D MQTT_SOCKET_TIMEOUT=5 - -D PB_FIELD_16BIT=1 - -; *** Fix espressif8266@1.7.0 induced undesired all warnings -build_unflags = -Wall - -monitor_speed = 115200 -upload_speed = 460800 -; *** Upload Serial reset method for Wemos and NodeMCU -upload_resetmethod = nodemcu -;upload_port = COM5 -extra_scripts = scripts/strip-floats.py - scripts/name-firmware.py - -lib_deps = - PubSubClient - Nanopb=https://github.com/nanopb/nanopb.git#nanopb-0.3.9.5 - -[env:dc1] -lib_deps = ${env.lib_deps} - EspSoftwareSerial@5.0.4 +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter, extra scripting +; Upload options: custom port, speed and extra flags +; Library options: dependencies, extra library storages +; +; Please visit documentation for the other options and examples +; http://docs.platformio.org/en/stable/projectconf.html + +[platformio] +default_envs = dc1 + +[env] +framework = arduino +board = esp01_1m +board_build.f_cpu = 80000000L +board_build.flash_mode = dout +board_build.ldscript = eagle.flash.1m.ld + +; *** Esp8266 core for Arduino version 2.6.1 +platform = espressif8266@2.3.3 +build_flags = -D NDEBUG + -mtarget-align + -Wl,-Map,firmware.map +; -Wl,-Teagle.flash.1m.ld + -DBEARSSL_SSL_BASIC +; NONOSDK22x_190703 = 2.2.2-dev(38a443e) + -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703 +; lwIP 2 - Higher Bandwidth no Features + -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH +; VTABLES in Flash + -DVTABLES_IN_FLASH +; No exception code in firmware + -fno-exceptions + -lstdc++ + + -D MQTT_MAX_PACKET_SIZE=768 + -D MQTT_SOCKET_TIMEOUT=5 + -D PB_FIELD_16BIT=1 + +; *** Fix espressif8266@1.7.0 induced undesired all warnings +build_unflags = -Wall + +monitor_speed = 115200 +upload_speed = 460800 +; *** Upload Serial reset method for Wemos and NodeMCU +upload_resetmethod = nodemcu +;upload_port = COM5 +extra_scripts = scripts/strip-floats.py + scripts/name-firmware.py + +lib_deps = + PubSubClient + Nanopb=https://github.com/nanopb/nanopb.git#nanopb-0.3.9.5 + +[env:dc1] +lib_deps = ${env.lib_deps} + EspSoftwareSerial@5.0.4 diff --git a/scripts/name-firmware.py b/scripts/name-firmware.py index fc39df1..e3d397a 100644 --- a/scripts/name-firmware.py +++ b/scripts/name-firmware.py @@ -1,33 +1,33 @@ -Import('env') -import os -import shutil - -OUTPUT_DIR = "output{}".format(os.path.sep) - -def bin_map_copy(source, target, env): - variant = str(target[0]).split(os.path.sep)[2] - #print(variant) - #print(str(target[0])) - - o = str(target[0]).replace("firmware.bin", "") - dirs = os.listdir(o) - for file in dirs: - p = os.path.join(o, file, "esp_framework\\Rtc.cpp.o") - if os.path.isfile(p): - os.remove(p) - - # check if output directories exist and create if necessary - if not os.path.isdir(OUTPUT_DIR): - os.mkdir(OUTPUT_DIR) - - # create string with location and file names based on variant - bin_file = "{}{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant) - - if os.path.isfile(bin_file): - os.remove(bin_file) - - # copy firmware.bin to firmware/.bin - shutil.copy(str(target[0]), bin_file) - - -env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_map_copy]) +Import('env') +import os +import shutil + +OUTPUT_DIR = "output{}".format(os.path.sep) + +def bin_map_copy(source, target, env): + variant = str(target[0]).split(os.path.sep)[2] + #print(variant) + #print(str(target[0])) + + o = str(target[0]).replace("firmware.bin", "") + dirs = os.listdir(o) + for file in dirs: + p = os.path.join(o, file, "esp_framework\\Rtc.cpp.o") + if os.path.isfile(p): + os.remove(p) + + # check if output directories exist and create if necessary + if not os.path.isdir(OUTPUT_DIR): + os.mkdir(OUTPUT_DIR) + + # create string with location and file names based on variant + bin_file = "{}{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant) + + if os.path.isfile(bin_file): + os.remove(bin_file) + + # copy firmware.bin to firmware/.bin + shutil.copy(str(target[0]), bin_file) + + +env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_map_copy]) diff --git a/scripts/strip-floats.py b/scripts/strip-floats.py index da916eb..6f21a74 100644 --- a/scripts/strip-floats.py +++ b/scripts/strip-floats.py @@ -1,15 +1,15 @@ -Import('env') - -# -# Dump build environment (for debug) -#print env.Dump() -# - -flags = " ".join(env['LINKFLAGS']) -flags = flags.replace("-u _printf_float", "") -flags = flags.replace("-u _scanf_float", "") -newflags = flags.split() - -env.Replace( - LINKFLAGS=newflags +Import('env') + +# +# Dump build environment (for debug) +#print env.Dump() +# + +flags = " ".join(env['LINKFLAGS']) +flags = flags.replace("-u _printf_float", "") +flags = flags.replace("-u _scanf_float", "") +newflags = flags.split() + +env.Replace( + LINKFLAGS=newflags ) \ No newline at end of file diff --git a/src/CAT9554.cpp b/src/CAT9554.cpp index ce9c4bf..b612d16 100644 --- a/src/CAT9554.cpp +++ b/src/CAT9554.cpp @@ -1,182 +1,182 @@ -#include -#include "CAT9554.h" - -bool CAT9554::updateGpio = false; - -uint32_t i2c_buffer = 0; -bool I2cValidRead(uint8_t addr, uint8_t reg, uint8_t size) -{ - uint8_t retry = I2C_RETRY_COUNTER; - bool status = false; - - i2c_buffer = 0; - while (!status && retry) - { - if (retry != I2C_RETRY_COUNTER) - { - delayMicroseconds(10 * 1000); - } - Wire.beginTransmission(addr); // start transmission to device - Wire.write(reg); // sends register address to read from - if (0 == Wire.endTransmission(false)) - { // Try to become I2C Master, send data and collect bytes, keep master status for next request... - Wire.requestFrom((int)addr, (int)size); // send data n-bytes read - if (Wire.available() == size) - { - for (uint32_t i = 0; i < size; i++) - { - i2c_buffer = i2c_buffer << 8 | Wire.read(); // receive DATA - } - status = true; - } - } - retry--; - } - return status; -} - -bool I2cWrite(uint8_t addr, uint8_t reg, uint32_t val, uint8_t size) -{ - uint8_t x = I2C_RETRY_COUNTER; - do - { - if (x != I2C_RETRY_COUNTER) - { - delayMicroseconds(10 * 1000); - } - Wire.beginTransmission((uint8_t)addr); // start transmission to device - Wire.write(reg); // sends register address to write to - uint8_t bytes = size; - while (bytes--) - { - Wire.write((val >> (8 * bytes)) & 0xFF); // write data - } - x--; - } while (Wire.endTransmission(true) != 0 && x != 0); // end transmission - return (x); -} - -CAT9554::CAT9554(uint8_t sda, uint8_t scl, uint16_t frequency) -{ - Wire.begin(sda, scl); - Wire.setClock(frequency); -} - -bool CAT9554::readByte(uint8_t reg, uint8_t *data) -{ - bool status = I2cValidRead(CAT9554_ADDRESS, reg, 1); - *data = (uint8_t)i2c_buffer; - return status; -} - -bool CAT9554::writeByte(uint8_t reg, uint16_t value) -{ - return I2cWrite(CAT9554_ADDRESS, reg, value, 1); -} - -bool CAT9554::readGpio() -{ - uint8_t data; - if (!this->readByte(INPUT_REG, &data)) - { - return false; - } - this->inputMask = data; - return true; -} - -bool CAT9554::writeGpio() -{ - if (!this->writeByte(OUTPUT_REG, this->outputMask)) - { - return false; - } - return true; -} - -bool CAT9554::configGpio() -{ - if (!this->writeByte(INPUT_REG, this->configMask)) - { - return false; - } - if (!this->writeByte(CONFIG_REG, this->configMask)) - { - return false; - } - if (!this->writeByte(INPUT_REG, 0x00)) - { - return false; - } - return true; -} - -bool CAT9554::readConfig() -{ - uint8_t data; - if (!this->readByte(CONFIG_REG, &data)) - { - return false; - } - this->configMask = data; - return true; -} - -void ICACHE_RAM_ATTR CAT9554::gpioIntr() -{ - CAT9554::updateGpio = true; -} - -void CAT9554::setup() -{ - if (!this->readGpio()) - { - return; - } - - if (this->enableIrq) - { - attachInterrupt(this->irqPin, gpioIntr, FALLING); - CAT9554::updateGpio = false; - } - this->readGpio(); - this->readConfig(); -} - -bool CAT9554::digitalRead(uint8_t pin) -{ - if (!this->enableIrq || CAT9554::updateGpio) - { - this->readGpio(); - this->updateGpio = false; - } - return this->inputMask & (1 << pin); -} - -bool CAT9554::digitalWrite(uint8_t pin, bool value) -{ - if (value) - { - this->outputMask |= (1 << pin); - } - else - { - this->outputMask &= ~(1 << pin); - } - return this->writeGpio(); -} - -void CAT9554::pinMode(uint8_t pin, uint8_t mode) -{ - if (mode == CAT9554_INPUT) - { - // Clear mode mask bit - this->configMask |= (1 << pin); - } - else if (mode == CAT9554_OUTPUT) - { - // Set mode mask bit - this->configMask &= ~(1 << pin); - } - this->configGpio(); -} +#include +#include "CAT9554.h" + +bool CAT9554::updateGpio = false; + +uint32_t i2c_buffer = 0; +bool I2cValidRead(uint8_t addr, uint8_t reg, uint8_t size) +{ + uint8_t retry = I2C_RETRY_COUNTER; + bool status = false; + + i2c_buffer = 0; + while (!status && retry) + { + if (retry != I2C_RETRY_COUNTER) + { + delayMicroseconds(10 * 1000); + } + Wire.beginTransmission(addr); // start transmission to device + Wire.write(reg); // sends register address to read from + if (0 == Wire.endTransmission(false)) + { // Try to become I2C Master, send data and collect bytes, keep master status for next request... + Wire.requestFrom((int)addr, (int)size); // send data n-bytes read + if (Wire.available() == size) + { + for (uint32_t i = 0; i < size; i++) + { + i2c_buffer = i2c_buffer << 8 | Wire.read(); // receive DATA + } + status = true; + } + } + retry--; + } + return status; +} + +bool I2cWrite(uint8_t addr, uint8_t reg, uint32_t val, uint8_t size) +{ + uint8_t x = I2C_RETRY_COUNTER; + do + { + if (x != I2C_RETRY_COUNTER) + { + delayMicroseconds(10 * 1000); + } + Wire.beginTransmission((uint8_t)addr); // start transmission to device + Wire.write(reg); // sends register address to write to + uint8_t bytes = size; + while (bytes--) + { + Wire.write((val >> (8 * bytes)) & 0xFF); // write data + } + x--; + } while (Wire.endTransmission(true) != 0 && x != 0); // end transmission + return (x); +} + +CAT9554::CAT9554(uint8_t sda, uint8_t scl, uint16_t frequency) +{ + Wire.begin(sda, scl); + Wire.setClock(frequency); +} + +bool CAT9554::readByte(uint8_t reg, uint8_t *data) +{ + bool status = I2cValidRead(CAT9554_ADDRESS, reg, 1); + *data = (uint8_t)i2c_buffer; + return status; +} + +bool CAT9554::writeByte(uint8_t reg, uint16_t value) +{ + return I2cWrite(CAT9554_ADDRESS, reg, value, 1); +} + +bool CAT9554::readGpio() +{ + uint8_t data; + if (!this->readByte(INPUT_REG, &data)) + { + return false; + } + this->inputMask = data; + return true; +} + +bool CAT9554::writeGpio() +{ + if (!this->writeByte(OUTPUT_REG, this->outputMask)) + { + return false; + } + return true; +} + +bool CAT9554::configGpio() +{ + if (!this->writeByte(INPUT_REG, this->configMask)) + { + return false; + } + if (!this->writeByte(CONFIG_REG, this->configMask)) + { + return false; + } + if (!this->writeByte(INPUT_REG, 0x00)) + { + return false; + } + return true; +} + +bool CAT9554::readConfig() +{ + uint8_t data; + if (!this->readByte(CONFIG_REG, &data)) + { + return false; + } + this->configMask = data; + return true; +} + +void ICACHE_RAM_ATTR CAT9554::gpioIntr() +{ + CAT9554::updateGpio = true; +} + +void CAT9554::setup() +{ + if (!this->readGpio()) + { + return; + } + + if (this->enableIrq) + { + attachInterrupt(this->irqPin, gpioIntr, FALLING); + CAT9554::updateGpio = false; + } + this->readGpio(); + this->readConfig(); +} + +bool CAT9554::digitalRead(uint8_t pin) +{ + if (!this->enableIrq || CAT9554::updateGpio) + { + this->readGpio(); + this->updateGpio = false; + } + return this->inputMask & (1 << pin); +} + +bool CAT9554::digitalWrite(uint8_t pin, bool value) +{ + if (value) + { + this->outputMask |= (1 << pin); + } + else + { + this->outputMask &= ~(1 << pin); + } + return this->writeGpio(); +} + +void CAT9554::pinMode(uint8_t pin, uint8_t mode) +{ + if (mode == CAT9554_INPUT) + { + // Clear mode mask bit + this->configMask |= (1 << pin); + } + else if (mode == CAT9554_OUTPUT) + { + // Set mode mask bit + this->configMask &= ~(1 << pin); + } + this->configGpio(); +} diff --git a/src/CSE7766.cpp b/src/CSE7766.cpp index 2ce8449..d395dcf 100644 --- a/src/CSE7766.cpp +++ b/src/CSE7766.cpp @@ -1,243 +1,243 @@ -#include "CSE7766.h" - -CSE7766::CSE7766(uint8_t rxPin, uint16_t baudrate) -{ - this->serial = new SoftwareSerial(rxPin, SW_SERIAL_UNUSED_PIN, false, 32); - this->serial->enableIntTx(false); - this->serial->begin(baudrate); - Cse.power_invalid = CSE_MAX_INVALID_POWER; -} - -void CSE7766::cseReceived() -{ - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 - // F2 5A 02 F7 60 00 03 61 00 40 10 05 72 40 51 A6 58 63 10 1B E1 7F 4D 4E - F2 = Power cycle exceeds range - takes too long - No load - // 55 5A 02 F7 60 00 03 5A 00 40 10 04 8B 9F 51 A6 58 18 72 75 61 AC A1 30 - 55 = Ok, 61 = Power not valid (load below 5W) - // 55 5A 02 F7 60 00 03 AB 00 40 10 02 60 5D 51 A6 58 03 E9 EF 71 0B 7A 36 - 55 = Ok, 71 = Ok - // Hd Id VCal---- Voltage- ICal---- Current- PCal---- Power--- Ad CF--- Ck - - uint8_t header = this->rawData[0]; - if ((header & 0xFC) == 0xFC) - { - //Debug::AddInfo(PSTR("CSE: Abnormal hardware")); - return; - } - - // Get chip calibration data (coefficients) and use as initial defaults - if (HLW_UREF_PULSE == this->voltageCalibration) - { - long voltage_coefficient = 191200; // uSec - if (CSE_NOT_CALIBRATED != header) - { - voltage_coefficient = this->rawData[2] << 16 | this->rawData[3] << 8 | this->rawData[4]; - } - this->voltageCalibration = voltage_coefficient / CSE_UREF; - } - if (HLW_IREF_PULSE == this->currentCalibration) - { - long current_coefficient = 16140; // uSec - if (CSE_NOT_CALIBRATED != header) - { - current_coefficient = this->rawData[8] << 16 | this->rawData[9] << 8 | this->rawData[10]; - } - this->currentCalibration = current_coefficient; - } - if (HLW_PREF_PULSE == this->powerCalibration) - { - long power_coefficient = 5364000; // uSec - if (CSE_NOT_CALIBRATED != header) - { - power_coefficient = this->rawData[14] << 16 | this->rawData[15] << 8 | this->rawData[16]; - } - this->powerCalibration = power_coefficient / CSE_PREF; - } - - uint8_t adjustement = this->rawData[20]; - Cse.voltage_cycle = this->rawData[5] << 16 | this->rawData[6] << 8 | this->rawData[7]; - Cse.current_cycle = this->rawData[11] << 16 | this->rawData[12] << 8 | this->rawData[13]; - Cse.power_cycle = this->rawData[17] << 16 | this->rawData[18] << 8 | this->rawData[19]; - Cse.cf_pulses = this->rawData[21] << 8 | this->rawData[22]; - - if (adjustement & 0x40) // Voltage valid - { - Energy.voltage = (float)(this->voltageCalibration * CSE_UREF) / (float)Cse.voltage_cycle; - } - if (adjustement & 0x10) // Power valid - { - Cse.power_invalid = 0; - if ((header & 0xF2) == 0xF2) // Power cycle exceeds range - { - Energy.active_power = 0; - } - else - { - if (0 == Cse.power_cycle_first) - { - Cse.power_cycle_first = Cse.power_cycle; - } // Skip first incomplete Cse.power_cycle - if (Cse.power_cycle_first != Cse.power_cycle) - { - Cse.power_cycle_first = -1; - Energy.active_power = (float)(this->powerCalibration * CSE_PREF) / (float)Cse.power_cycle; - } - else - { - Energy.active_power = 0; - } - } - } - else - { - if (Cse.power_invalid < CSE_MAX_INVALID_POWER) // Allow measurements down to about 1W - { - Cse.power_invalid++; - } - else - { - Cse.power_cycle_first = 0; - Energy.active_power = 0; // Powered on but no load - } - } - if (adjustement & 0x20) // Current valid - { - if (0 == Energy.active_power) - { - Energy.current = 0; - } - else - { - Energy.current = (float)this->currentCalibration / (float)Cse.current_cycle; - } - } -} - -void CSE7766::loop() -{ - while (this->serial->available()) - { - uint8_t serial_in_byte = this->serial->read(); - - if (Cse.received) - { - this->rawData[this->rawDataIndex++] = serial_in_byte; - if (24 == this->rawDataIndex) - { - //char strHex[100]; - //hex2Str(this->rawData, 23, strHex, true); - //Debug::AddInfo(PSTR("CSE7766 Data: %s"), strHex); - uint8_t checksum = 0; - for (uint8_t i = 2; i < 23; i++) - { - checksum += this->rawData[i]; - } - if (checksum == this->rawData[23]) - { - Energy.data_valid = 0; - cseReceived(); - Cse.received = false; - this->rawDataIndex = 0; - } - else - { - do - { // Sync buffer with data (issue #1907 and #3425) - memmove(this->rawData, this->rawData + 1, 24); - this->rawDataIndex--; - } while ((this->rawDataIndex > 2) && (0x5A != this->rawData[1])); - if (0x5A != this->rawData[1]) - { - Cse.received = false; - this->rawDataIndex = 0; - } - } - } - } - else - { - if ((0x5A == serial_in_byte) && (1 == this->rawDataIndex)) // 0x5A - Packet header 2 - { - Cse.received = true; - } - else - { - this->rawDataIndex = 0; - } - this->rawData[this->rawDataIndex++] = serial_in_byte; - } - } -} - -bool CSE7766::everySecond() -{ - if (Energy.data_valid <= ENERGY_WATCHDOG) - { - Energy.data_valid++; - if (Energy.data_valid > ENERGY_WATCHDOG) - { - // Reset energy registers - Energy.voltage = 0; - Energy.current = 0; - Energy.active_power = 0; - - if (!isnan(Energy.apparent_power)) - { - Energy.apparent_power = NAN; - } - if (!isnan(Energy.reactive_power)) - { - Energy.reactive_power = NAN; - } - if (!isnan(Energy.power_factor)) - { - Energy.power_factor = NAN; - } - } - } - - if (Energy.data_valid > ENERGY_WATCHDOG) - { - Cse.voltage_cycle = 0; - Cse.current_cycle = 0; - Cse.power_cycle = 0; - } - else - { - long cf_frequency = 0; - - if (CSE_PULSES_NOT_INITIALIZED == Cse.cf_pulses_last_time) - { - Cse.cf_pulses_last_time = Cse.cf_pulses; // Init after restart - } - else - { - if (Cse.cf_pulses < Cse.cf_pulses_last_time) - { // Rolled over after 65535 pulses - cf_frequency = (65536 - Cse.cf_pulses_last_time) + Cse.cf_pulses; - } - else - { - cf_frequency = Cse.cf_pulses - Cse.cf_pulses_last_time; - } - if (cf_frequency && Energy.active_power) - { - unsigned long delta = (cf_frequency * this->powerCalibration) / 36; - // prevent invalid load delta steps even checksum is valid (issue #5789): - // if (delta <= (3680*100/36) * 10 ) { // max load for S31/Pow R2: 3.68kW - // prevent invalid load delta steps even checksum is valid but allow up to 4kW (issue #7155): - if (delta <= (4000 * 100 / 36) * 10) - { // max load for S31/Pow R2: 4.00kW - Cse.cf_pulses_last_time = Cse.cf_pulses; - Energy.kWhtoday_delta += delta; - } - else - { - //Debug::AddInfo(PSTR("CSE: Load overflow")); - Cse.cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED; - } - //EnergyUpdateToday(); - return true; - } - } - } - return false; +#include "CSE7766.h" + +CSE7766::CSE7766(uint8_t rxPin, uint16_t baudrate) +{ + this->serial = new SoftwareSerial(rxPin, SW_SERIAL_UNUSED_PIN, false, 32); + this->serial->enableIntTx(false); + this->serial->begin(baudrate); + Cse.power_invalid = CSE_MAX_INVALID_POWER; +} + +void CSE7766::cseReceived() +{ + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 + // F2 5A 02 F7 60 00 03 61 00 40 10 05 72 40 51 A6 58 63 10 1B E1 7F 4D 4E - F2 = Power cycle exceeds range - takes too long - No load + // 55 5A 02 F7 60 00 03 5A 00 40 10 04 8B 9F 51 A6 58 18 72 75 61 AC A1 30 - 55 = Ok, 61 = Power not valid (load below 5W) + // 55 5A 02 F7 60 00 03 AB 00 40 10 02 60 5D 51 A6 58 03 E9 EF 71 0B 7A 36 - 55 = Ok, 71 = Ok + // Hd Id VCal---- Voltage- ICal---- Current- PCal---- Power--- Ad CF--- Ck + + uint8_t header = this->rawData[0]; + if ((header & 0xFC) == 0xFC) + { + //Debug::AddInfo(PSTR("CSE: Abnormal hardware")); + return; + } + + // Get chip calibration data (coefficients) and use as initial defaults + if (HLW_UREF_PULSE == this->voltageCalibration) + { + long voltage_coefficient = 191200; // uSec + if (CSE_NOT_CALIBRATED != header) + { + voltage_coefficient = this->rawData[2] << 16 | this->rawData[3] << 8 | this->rawData[4]; + } + this->voltageCalibration = voltage_coefficient / CSE_UREF; + } + if (HLW_IREF_PULSE == this->currentCalibration) + { + long current_coefficient = 16140; // uSec + if (CSE_NOT_CALIBRATED != header) + { + current_coefficient = this->rawData[8] << 16 | this->rawData[9] << 8 | this->rawData[10]; + } + this->currentCalibration = current_coefficient; + } + if (HLW_PREF_PULSE == this->powerCalibration) + { + long power_coefficient = 5364000; // uSec + if (CSE_NOT_CALIBRATED != header) + { + power_coefficient = this->rawData[14] << 16 | this->rawData[15] << 8 | this->rawData[16]; + } + this->powerCalibration = power_coefficient / CSE_PREF; + } + + uint8_t adjustement = this->rawData[20]; + Cse.voltage_cycle = this->rawData[5] << 16 | this->rawData[6] << 8 | this->rawData[7]; + Cse.current_cycle = this->rawData[11] << 16 | this->rawData[12] << 8 | this->rawData[13]; + Cse.power_cycle = this->rawData[17] << 16 | this->rawData[18] << 8 | this->rawData[19]; + Cse.cf_pulses = this->rawData[21] << 8 | this->rawData[22]; + + if (adjustement & 0x40) // Voltage valid + { + Energy.voltage = (float)(this->voltageCalibration * CSE_UREF) / (float)Cse.voltage_cycle; + } + if (adjustement & 0x10) // Power valid + { + Cse.power_invalid = 0; + if ((header & 0xF2) == 0xF2) // Power cycle exceeds range + { + Energy.active_power = 0; + } + else + { + if (0 == Cse.power_cycle_first) + { + Cse.power_cycle_first = Cse.power_cycle; + } // Skip first incomplete Cse.power_cycle + if (Cse.power_cycle_first != Cse.power_cycle) + { + Cse.power_cycle_first = -1; + Energy.active_power = (float)(this->powerCalibration * CSE_PREF) / (float)Cse.power_cycle; + } + else + { + Energy.active_power = 0; + } + } + } + else + { + if (Cse.power_invalid < CSE_MAX_INVALID_POWER) // Allow measurements down to about 1W + { + Cse.power_invalid++; + } + else + { + Cse.power_cycle_first = 0; + Energy.active_power = 0; // Powered on but no load + } + } + if (adjustement & 0x20) // Current valid + { + if (0 == Energy.active_power) + { + Energy.current = 0; + } + else + { + Energy.current = (float)this->currentCalibration / (float)Cse.current_cycle; + } + } +} + +void CSE7766::loop() +{ + while (this->serial->available()) + { + uint8_t serial_in_byte = this->serial->read(); + + if (Cse.received) + { + this->rawData[this->rawDataIndex++] = serial_in_byte; + if (24 == this->rawDataIndex) + { + //char strHex[100]; + //hex2Str(this->rawData, 23, strHex, true); + //Debug::AddInfo(PSTR("CSE7766 Data: %s"), strHex); + uint8_t checksum = 0; + for (uint8_t i = 2; i < 23; i++) + { + checksum += this->rawData[i]; + } + if (checksum == this->rawData[23]) + { + Energy.data_valid = 0; + cseReceived(); + Cse.received = false; + this->rawDataIndex = 0; + } + else + { + do + { // Sync buffer with data (issue #1907 and #3425) + memmove(this->rawData, this->rawData + 1, 24); + this->rawDataIndex--; + } while ((this->rawDataIndex > 2) && (0x5A != this->rawData[1])); + if (0x5A != this->rawData[1]) + { + Cse.received = false; + this->rawDataIndex = 0; + } + } + } + } + else + { + if ((0x5A == serial_in_byte) && (1 == this->rawDataIndex)) // 0x5A - Packet header 2 + { + Cse.received = true; + } + else + { + this->rawDataIndex = 0; + } + this->rawData[this->rawDataIndex++] = serial_in_byte; + } + } +} + +bool CSE7766::everySecond() +{ + if (Energy.data_valid <= ENERGY_WATCHDOG) + { + Energy.data_valid++; + if (Energy.data_valid > ENERGY_WATCHDOG) + { + // Reset energy registers + Energy.voltage = 0; + Energy.current = 0; + Energy.active_power = 0; + + if (!isnan(Energy.apparent_power)) + { + Energy.apparent_power = NAN; + } + if (!isnan(Energy.reactive_power)) + { + Energy.reactive_power = NAN; + } + if (!isnan(Energy.power_factor)) + { + Energy.power_factor = NAN; + } + } + } + + if (Energy.data_valid > ENERGY_WATCHDOG) + { + Cse.voltage_cycle = 0; + Cse.current_cycle = 0; + Cse.power_cycle = 0; + } + else + { + long cf_frequency = 0; + + if (CSE_PULSES_NOT_INITIALIZED == Cse.cf_pulses_last_time) + { + Cse.cf_pulses_last_time = Cse.cf_pulses; // Init after restart + } + else + { + if (Cse.cf_pulses < Cse.cf_pulses_last_time) + { // Rolled over after 65535 pulses + cf_frequency = (65536 - Cse.cf_pulses_last_time) + Cse.cf_pulses; + } + else + { + cf_frequency = Cse.cf_pulses - Cse.cf_pulses_last_time; + } + if (cf_frequency && Energy.active_power) + { + unsigned long delta = (cf_frequency * this->powerCalibration) / 36; + // prevent invalid load delta steps even checksum is valid (issue #5789): + // if (delta <= (3680*100/36) * 10 ) { // max load for S31/Pow R2: 3.68kW + // prevent invalid load delta steps even checksum is valid but allow up to 4kW (issue #7155): + if (delta <= (4000 * 100 / 36) * 10) + { // max load for S31/Pow R2: 4.00kW + Cse.cf_pulses_last_time = Cse.cf_pulses; + Energy.kWhtoday_delta += delta; + } + else + { + //Debug::AddInfo(PSTR("CSE: Load overflow")); + Cse.cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED; + } + //EnergyUpdateToday(); + return true; + } + } + } + return false; } \ No newline at end of file diff --git a/src/DC1.cpp b/src/DC1.cpp index 9e03acf..ce27a7a 100644 --- a/src/DC1.cpp +++ b/src/DC1.cpp @@ -1,905 +1,905 @@ - -#include "DC1.h" -#include "Rtc.h" -#include "Util.h" - -#pragma region 继承 - -void DC1::init() -{ - Led::init(LED_PIN, LOW); - cat9554 = new CAT9554(CAT9554_SDA_PIN, CAT9554_SCL_PIN); - cat9554->setIrqPin(CAT9554_IRQ_PIN); - cat9554->setup(); - - cse7766 = new CSE7766(CSE7766_RX_PIN, CSE7766_BAUDRATE); - - // 按键 - pinMode(KEY_0_PIN, INPUT_PULLDOWN_16); - cat9554->pinMode(KEY_1_PIN, INPUT); - cat9554->pinMode(KEY_2_PIN, INPUT); - cat9554->pinMode(KEY_3_PIN, INPUT); - - // 继电器 - cat9554->pinMode(REL_0_PIN, OUTPUT); - cat9554->pinMode(REL_1_PIN, OUTPUT); - cat9554->pinMode(REL_2_PIN, OUTPUT); - cat9554->pinMode(REL_3_PIN, OUTPUT); - - pinMode(LOGO_LED_PIN, OUTPUT); - logoLed(); - - channels = 4; - for (uint8_t ch = 0; ch < channels; ch++) - { - // 0:开关通电时断开 1 : 开关通电时闭合 2 : 开关通电时状态与断电前相反 3 : 开关通电时保持断电前状态 - if (config.power_on_state == 2) - { - switchRelay(ch, !bitRead(config.last_state, ch), false); // 开关通电时状态与断电前相反 - } - else if (config.power_on_state == 3) - { - switchRelay(ch, bitRead(config.last_state, ch), false); // 开关通电时保持断电前状态 - } - else - { - switchRelay(ch, config.power_on_state == 1, false); // 开关通电时闭合 - } - // 总开关关时跳过其他 - if (ch == 0 && !bitRead(lastState, 0) && config.sub_kinkage != 0) - { - break; - } - } - energyInit(); - powerTopic = Mqtt::getStatTopic(F("POWER")); -} - -bool DC1::moduleLed() -{ - if (WiFi.status() == WL_CONNECTED && Mqtt::mqttClient.connected()) - { - if (config.wifi_led == 0) - { - Led::on(); - return true; - } - else if (config.wifi_led == 1) - { - Led::off(); - return true; - } - } - return false; -} - -void DC1::loop() -{ - cse7766->loop(); - for (size_t ch = 0; ch < channels; ch++) - { - checkButton(ch); - } - - if (bitRead(operationFlag, 0)) - { - bitClear(operationFlag, 0); - energyUpdate(); - } -} - -void DC1::perSecondDo() -{ - bitSet(operationFlag, 0); -} -#pragma endregion - -#pragma region 配置 - -void DC1::readConfig() -{ - Config::moduleReadConfig(MODULE_CFG_VERSION, sizeof(DC1ConfigMessage), DC1ConfigMessage_fields, &config); -} - -void DC1::resetConfig() -{ - Debug::AddInfo(PSTR("moduleResetConfig . . . OK")); - memset(&config, 0, sizeof(DC1ConfigMessage)); - - config.power_on_state = 3; - config.power_mode = 0; - config.logo_led = 0; - config.wifi_led = 0; - config.sub_kinkage = 2; - config.energy_power_delta = 10; - config.report_interval = 60; - config.energy_max_power = 2300; -} - -void DC1::saveConfig(bool isEverySecond) -{ - if (bitRead(operationFlag, 1) || !isEverySecond) - { - bitClear(operationFlag, 1); - energySync(); - } - Config::moduleSaveConfig(MODULE_CFG_VERSION, DC1ConfigMessage_size, DC1ConfigMessage_fields, &config); -} -#pragma endregion - -#pragma region MQTT - -void DC1::mqttCallback(String topicStr, String str) -{ - if (channels >= 1 && topicStr.endsWith("/POWER") || topicStr.endsWith("/POWER1")) - { - switchRelay(0, (str == "ON" ? true : (str == "OFF" ? false : !bitRead(lastState, 0)))); - } - else if (channels >= 2 && topicStr.endsWith("/POWER2")) - { - switchRelay(1, (str == "ON" ? true : (str == "OFF" ? false : !bitRead(lastState, 1)))); - } - else if (channels >= 3 && topicStr.endsWith("/POWER3")) - { - switchRelay(2, (str == "ON" ? true : (str == "OFF" ? false : !bitRead(lastState, 2)))); - } - else if (channels >= 4 && topicStr.endsWith("/POWER4")) - { - switchRelay(3, (str == "ON" ? true : (str == "OFF" ? false : !bitRead(lastState, 3)))); - } - else if (topicStr.endsWith("/clear")) - { - energyClear(); - } - else if (topicStr.endsWith("/report")) - { - reportPower(); - reportEnergy(); - } -} - -void DC1::mqttConnected() -{ - if (globalConfig.mqtt.discovery) - { - mqttDiscovery(true); - } - - reportPower(); - reportEnergy(); -} - -void DC1::mqttDiscovery(bool isEnable) -{ - char topic[50]; - char message[500]; - - String tmp = Mqtt::getCmndTopic(F("POWER")); - String availability = Mqtt::getTeleTopic(F("availability")); - for (size_t ch = 0; ch < channels; ch++) - { - sprintf(topic, "%s/switch/%s_%d/config", globalConfig.mqtt.discovery_prefix, UID, (ch + 1)); - if (isEnable) - { - sprintf(message, HASS_DISCOVER_DC1_SWICH, UID, (ch + 1), - (tmp + (ch + 1)).c_str(), - (powerTopic + (ch + 1)).c_str(), - availability.c_str()); - Mqtt::publish(topic, message, true); - //Debug::AddInfo(PSTR("discovery: %s - %s"), topic, message); - } - else - { - Mqtt::publish(topic, "", true); - } - } - - String tims[] = {"voltage", "current", "power", "apparent_power", "reactive_power", "factor", "total", "yesterday", "today", "starttime"}; - String tims2[] = {"V", "A", "W", "VA", "VAr", "", "kWh", "kWh", "kWh", ""}; - String energy = Mqtt::getTeleTopic(F("ENERGY")); - for (size_t i = 0; i < 10; i++) - { - sprintf(topic, "%s/sensor/%s_%s/config", globalConfig.mqtt.discovery_prefix, UID, tims[i].c_str()); - if (isEnable) - { - if (tims2[i].length() == 0) - { - sprintf(message, HASS_DISCOVER_DC1_SENSOR_WITHOUT_UNIT, - UID, tims[i].c_str(), - energy.c_str(), - tims[i].c_str()); - } - else - { - sprintf(message, HASS_DISCOVER_DC1_SENSOR, - UID, tims[i].c_str(), - energy.c_str(), - tims[i].c_str(), - tims2[i].c_str()); - } - Mqtt::publish(topic, message, true); - //Debug::AddInfo(PSTR("discovery: %s - %s"), topic, message); - } - else - { - Mqtt::publish(topic, "", true); - } - } - if (isEnable) - { - Mqtt::availability(); - reportPower(); - reportEnergy(); - } -} -#pragma endregion - -#pragma region Http - -void DC1::httpAdd(ESP8266WebServer *server) -{ - server->on(F("/dc1_do"), std::bind(&DC1::httpDo, this, server)); - server->on(F("/dc1_setting"), std::bind(&DC1::httpSetting, this, server)); - server->on(F("/ha"), std::bind(&DC1::httpHa, this, server)); -} - -String DC1::httpGetStatus(ESP8266WebServer *server) -{ - String data; - for (size_t ch = 0; ch < channels; ch++) - { - data += ",\"POWER" + String(ch + 1) + "\":"; - data += bitRead(lastState, ch) ? 1 : 0; - } - energyShow(false); - data += String(tmpData); - return data.substring(1); -} - -void DC1::httpHtml(ESP8266WebServer *server) -{ - String radioJs = F(""); - - server->sendContent(page); - server->sendContent(radioJs); -} - -void DC1::httpDo(ESP8266WebServer *server) -{ - String c = server->arg(F("c")); - if (c != F("1") && c != F("2") && c != F("3") && c != F("4")) - { - server->send(200, F("text/html"), F("{\"code\":0,\"msg\":\"参数错误。\"}")); - return; - } - uint8_t ch = c.toInt() - 1; - if (ch > channels) - { - server->send(200, F("text/html"), F("{\"code\":0,\"msg\":\"继电器数量错误。\"}")); - return; - } - String str = server->arg(F("do")); - switchRelay(ch, (str == "ON" ? true : (str == "OFF" ? false : !bitRead(lastState, ch)))); - - server->send(200, F("text/html"), "{\"code\":1,\"msg\":\"操作成功\",\"data\":{" + httpGetStatus(server) + "}}"); -} - -void DC1::httpSetting(ESP8266WebServer *server) -{ - if (server->hasArg(F("c"))) - { - energyClear(); - server->send(200, F("text/html"), "{\"code\":1,\"msg\":\"重置用电量成功。\",\"data\":{" + httpGetStatus(server) + "}}"); - return; - } - config.power_on_state = server->arg(F("power_on_state")).toInt(); - config.power_mode = server->arg(F("power_mode")).toInt(); - config.logo_led = server->arg(F("logo_led")).toInt(); - config.wifi_led = server->arg(F("wifi_led")).toInt(); - config.sub_kinkage = server->arg(F("sub_kinkage")).toInt(); - - config.report_interval = server->arg(F("report_interval")).toInt(); - config.energy_power_delta = server->arg(F("energy_power_delta")).toInt(); - config.energy_max_power = server->arg(F("energy_max_power")).toInt(); - - logoLed(); - - Config::saveConfig(); - server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"已经设置成功。\"}")); -} - -void DC1::httpHa(ESP8266WebServer *server) -{ - char attachment[100]; - snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=%s.yaml"), UID); - - server->setContentLength(CONTENT_LENGTH_UNKNOWN); - server->sendHeader(F("Content-Disposition"), attachment); - server->send(200, F("Content-Type: application/octet-stream"), ""); - - String availability = Mqtt::getTeleTopic(F("availability")); - String tmp = Mqtt::getCmndTopic(F("POWER")); - server->sendContent(F("switch:\r\n")); - for (size_t ch = 0; ch < channels; ch++) - { - server->sendContent(F(" - platform: mqtt\r\n name: \"")); - server->sendContent(UID); - server->sendContent(F("_")); - server->sendContent(String((ch + 1))); - server->sendContent(F("\"\r\n state_topic: \"")); - server->sendContent((powerTopic + (ch + 1))); - server->sendContent(F("\"\r\n command_topic: \"")); - server->sendContent((tmp + (ch + 1))); - server->sendContent(F("\"\r\n payload_on: \"ON\"\r\n payload_off: \"OFF\"\r\n availability_topic: \"")); - server->sendContent(availability); - server->sendContent(F("\"\r\n payload_available: \"online\"\r\n payload_not_available: \"offline\"\r\n\r\n")); - } - - String energy = Mqtt::getTeleTopic(F("ENERGY")); - String tims[] = {"voltage", "current", "power", "apparent_power", "reactive_power", "factor", "total", "yesterday", "today", "starttime"}; - String tims2[] = {"V", "A", "W", "VA", "VAr", "", "kWh", "kWh", "kWh", ""}; - server->sendContent(F("sensor:\r\n")); - for (size_t i = 0; i < 10; i++) - { - server->sendContent(F(" - platform: mqtt\r\n name: \"")); - server->sendContent(UID); - server->sendContent(F("_")); - server->sendContent(tims[i]); - server->sendContent(F("\"\r\n state_topic: \"")); - server->sendContent(energy); - server->sendContent(F("\"\r\n value_template: \"{{value_json.")); - server->sendContent(tims[i]); - server->sendContent(F("}}")); - if (tims2[i].length() > 0) - { - server->sendContent(F("\"\r\n unit_of_measurement: \"")); - server->sendContent(tims2[i]); - } - server->sendContent(F("\"\r\n\r\n")); - } -} -#pragma endregion - -void DC1::logoLed() -{ - if (config.logo_led == 0) - { - digitalWrite(LOGO_LED_PIN, LOW); - } - else if (config.logo_led == 1) - { - digitalWrite(LOGO_LED_PIN, HIGH); - } - else if (config.logo_led == 2) - { - digitalWrite(LOGO_LED_PIN, bitRead(lastState, 0) ? LOW : HIGH); - } - else if (config.logo_led == 3) - { - digitalWrite(LOGO_LED_PIN, bitRead(lastState, 0) ? HIGH : LOW); - } -} - -void DC1::switchRelay(uint8_t ch, bool isOn, bool isSave) -{ - if (ch > channels) - { - Debug::AddInfo(PSTR("invalid channel: %d"), ch); - return; - } - - if (ch > 0 || (ch == 0 && config.sub_kinkage == 0)) - { - if (!bitRead(lastState, 0) && isOn && config.sub_kinkage != 0) - { - if (config.sub_kinkage == 1 || !isSave) - { - isOn = false; - } - else if (config.sub_kinkage == 2) - { - switchRelay(0, true); - } - } - - if (isOn && config.power_mode == 1) - { - for (size_t ch2 = (config.sub_kinkage == 0 ? 0 : 1); ch2 < channels; ch2++) - { - if (ch2 != ch && bitRead(lastState, ch2)) - { - switchRelay(ch2, false, isSave); - } - } - } - } - Debug::AddInfo(PSTR("Relay %d . . . %s"), ch + 1, isOn ? "ON" : "OFF"); - - if (!cat9554->digitalWrite(relGPIO[ch], isOn ? HIGH : LOW)) - { - Debug::AddError(PSTR("CAT9554 digitalWrite Error")); - if (!cat9554->digitalWrite(relGPIO[ch], isOn ? HIGH : LOW)) - { - Debug::AddError(PSTR("CAT9554 digitalWrite Error2")); - return; - } - } - - bitWrite(lastState, ch, isOn); - - Mqtt::publish((powerTopic + (ch + 1)), isOn ? "ON" : "OFF", globalConfig.mqtt.retain); - - if (isSave && config.power_on_state > 0) - { - bitWrite(config.last_state, ch, isOn); - bitSet(operationFlag, 1); - Config::delaySaveConfig(10); - } - - if (ch == 0) - { - logoLed(); - if (isSave && config.sub_kinkage != 0) - { - for (size_t ch2 = 1; ch2 < channels; ch2++) - { - if (isOn) - { - if (bitRead(config.last_state, ch2)) - { - switchRelay(ch2, true, false); - } - } - else - { - switchRelay(ch2, false, false); - } - } - } - } -} - -void DC1::checkButton(uint8_t ch) -{ - bool buttonState = ch == 0 ? digitalRead(btnGPIO[ch]) : cat9554->digitalRead(btnGPIO[ch]); - - if (buttonState == 0) - { - if (!bitRead(buttonTiming, ch)) - { - bitSet(buttonTiming, ch); - buttonTimingStart[ch] = millis(); - } - else - { // buttonTiming = true - if (millis() >= (buttonTimingStart[ch] + buttonDebounceTime)) - { - buttonAction[ch] = 1; - } - if (millis() >= (buttonTimingStart[ch] + buttonLongPressTime)) - { - buttonAction[ch] = 2; - } - } - } - else - { - bitClear(buttonTiming, ch); - if (buttonAction[ch] != 0) - { - if (buttonAction[ch] == 1) // 执行短按动作 - { - switchRelay(ch, !bitRead(lastState, ch), true); - } - else if (buttonAction[ch] == 2) // 执行长按动作 - { - if (ch == 0) - { - Wifi::setupWifiManager(false); - } - } - buttonAction[ch] = 0; - } - } -} - -void DC1::energyUpdate() -{ - if (Rtc::rtcTime.valid && config.energy_kWhtotal_time == 0) - { - config.energy_kWhtotal_time = Rtc::utcTime; - TIME_T tmpTime; - Rtc::breakTime(config.energy_kWhtotal_time, tmpTime); - snprintf_P(kWhtotalTime, sizeof(kWhtotalTime), PSTR("%04d-%02d-%02d %02d:%02d:%02d"), tmpTime.year, tmpTime.month, tmpTime.day_of_month, tmpTime.hour, tmpTime.minute, tmpTime.second); - } - if (Rtc::rtcTime.valid && config.energy_kWhdoy != Rtc::rtcTime.day_of_year) - { - Debug::AddInfo("day_of_year: %d %d %d", Rtc::rtcTime.day_of_year, Rtc::rtcTime.day_of_month, Rtc::rtcTime.day_of_week); - - energySync(); - config.energy_kWhdoy = Rtc::rtcTime.day_of_year; - config.energy_kWhyesterday = config.energy_kWhtoday; - config.energy_kWhtoday = 0; - Config::saveConfig(); - - cse7766->Energy.daily = (float)(config.energy_kWhtoday + cse7766->Energy.kWhtoday) / 100000; - cse7766->Energy.total = (float)(config.energy_kWhtotal + cse7766->Energy.kWhtoday) / 100000; - } - if (cse7766->everySecond()) - { - energyUpdateToday(); - } - if (perSecond % 301 == 0 && cse7766->Energy.kWhtoday > 0) - { - energySync(); - Config::saveConfig(); - } - energyMarginCheck(); -} - -void DC1::energySync() -{ - if (cse7766->Energy.kWhtoday > 0) - { - config.energy_kWhtoday += cse7766->Energy.kWhtoday; - config.energy_kWhtotal += cse7766->Energy.kWhtoday; - cse7766->Energy.kWhtoday = 0; - } -} - -void DC1::energyInit() -{ - cse7766->Energy.kWhtoday = 0; - cse7766->Energy.kWhtoday_delta = 0; - cse7766->Energy.daily = (float)(config.energy_kWhtoday) / 100000; - cse7766->Energy.total = (float)(config.energy_kWhtotal) / 100000; - - TIME_T tmpTime; - Rtc::breakTime(config.energy_kWhtotal_time, tmpTime); - snprintf_P(kWhtotalTime, sizeof(kWhtotalTime), PSTR("%04d-%02d-%02d %02d:%02d:%02d"), tmpTime.year, tmpTime.month, tmpTime.day_of_month, tmpTime.hour, tmpTime.minute, tmpTime.second); -} - -void DC1::energyClear() -{ - config.energy_kWhtoday = 0; - config.energy_kWhyesterday = 0; - config.energy_kWhtotal = 0; - config.energy_kWhdoy = 0; - config.energy_kWhtotal_time = 0; - - cse7766->Energy.kWhtoday_delta = 0; - - energyInit(); - Config::saveConfig(); -} - -void DC1::energyUpdateToday() -{ - if (cse7766->Energy.kWhtoday_delta > 1000) - { - unsigned long delta = cse7766->Energy.kWhtoday_delta / 1000; - cse7766->Energy.kWhtoday_delta -= (delta * 1000); - cse7766->Energy.kWhtoday += delta; - - cse7766->Energy.daily = (float)(config.energy_kWhtoday + cse7766->Energy.kWhtoday) / 100000; - cse7766->Energy.total = (float)(config.energy_kWhtotal + cse7766->Energy.kWhtoday) / 100000; - } -} - -void DC1::energyMarginCheck() -{ - if (cse7766->Energy.power_steady_counter) - { - cse7766->Energy.power_steady_counter--; - return; - } - - uint16_t energy_power_u = (uint16_t)(cse7766->Energy.active_power); - uint16_t energy_voltage_u = (uint16_t)(cse7766->Energy.voltage); - uint16_t energy_current_u = (uint16_t)(cse7766->Energy.current * 1000); - - //Debug::AddInfo(PSTR("NRG: W %d, U %d, I %d"), energy_power_u, energy_voltage_u, energy_current_u); - if (config.energy_power_delta) - { - uint16_t delta = abs(cse7766->Energy.power_history[0] - energy_power_u); - if (delta > 0) - { - if (config.energy_power_delta < 101) - { // 1..100 = Percentage - uint16_t min_power = (cse7766->Energy.power_history[0] > energy_power_u) ? energy_power_u : cse7766->Energy.power_history[0]; - if (0 == min_power) - { - min_power++; - } // Fix divide by 0 exception (#6741) - if (((delta * 100) / min_power) > config.energy_power_delta) - { - cse7766->Energy.power_delta = true; - } - } - else - { // 101..32000 = Absolute - if (delta > (config.energy_power_delta - 100)) - { - cse7766->Energy.power_delta = true; - } - } - if (cse7766->Energy.power_delta) - { - cse7766->Energy.power_history[1] = cse7766->Energy.active_power; // We only want one report so reset history - cse7766->Energy.power_history[2] = cse7766->Energy.active_power; - } - } - } - - cse7766->Energy.power_history[0] = cse7766->Energy.power_history[1]; // Shift in history every second allowing power changes to settle for up to three seconds - cse7766->Energy.power_history[1] = cse7766->Energy.power_history[2]; - cse7766->Energy.power_history[2] = energy_power_u; - - if (config.report_interval > 0 && (perSecond % config.report_interval) == 0) - { - reportPower(); - cse7766->Energy.power_delta = true; - } - if (cse7766->Energy.power_delta) - { - cse7766->Energy.power_delta = false; - reportEnergy(); - } - if (config.energy_max_power) - { - DC1::energyMaxPower(); - } -} - -void DC1::energyMaxPower() -{ - if (cse7766->Energy.active_power > config.energy_max_power) - { - if (!cse7766->Energy.mplh_counter) - { - cse7766->Energy.mplh_counter = MAX_POWER_HOLD; - } - else - { - cse7766->Energy.mplh_counter--; - if (!cse7766->Energy.mplh_counter) - { - Debug::AddError(PSTR("MaxPowerReached: %d"), (uint16_t)cse7766->Energy.active_power); - lastState2 = lastState; - for (size_t ch = 0; ch < channels; ch++) - { - if (bitRead(lastState, ch)) - { - switchRelay(ch, false, false); - } - } - if (!cse7766->Energy.mplr_counter) - { - cse7766->Energy.mplr_counter = MAX_POWER_RETRY + 1; - } - cse7766->Energy.mplw_counter = MAX_POWER_WINDOW; - cse7766->Energy.mplv_counter = 0; - } - } - } - else if (lastState && (cse7766->Energy.active_power <= config.energy_max_power)) - { - cse7766->Energy.mplh_counter = 0; - cse7766->Energy.mplw_counter = 0; - - if (cse7766->Energy.mplv_counter++ == 60) - { - cse7766->Energy.mplv_counter = 0; - cse7766->Energy.mplr_counter = 0; - } - } - if (!lastState) - { - if (cse7766->Energy.mplw_counter) - { - cse7766->Energy.mplw_counter--; - } - else - { - if (cse7766->Energy.mplr_counter) - { - cse7766->Energy.mplr_counter--; - if (cse7766->Energy.mplr_counter) - { - Debug::AddError(PSTR("PowerMonitor ON")); - if (lastState2) - { - for (size_t ch = 0; ch < channels; ch++) - { - if (bitRead(lastState2, ch)) - { - switchRelay(ch, true, false); - } - } - lastState2 = 0; - } - } - else - { - Debug::AddInfo(PSTR("MaxPowerReachedRetry OFF")); - } - } - } - } -} - -void DC1::energyShow(bool isMqtt) -{ - const uint8_t current_resolution = 3; - const uint8_t voltage_resolution = 0; - const uint8_t wattage_resolution = 0; - const uint8_t energy_resolution = 3; - - float apparent_power = cse7766->Energy.apparent_power; - if (isnan(apparent_power)) - { - apparent_power = cse7766->Energy.voltage * cse7766->Energy.current; - } - if (apparent_power < cse7766->Energy.active_power) - { // Should be impossible - cse7766->Energy.active_power = apparent_power; - } - - float power_factor = cse7766->Energy.power_factor; - if (isnan(power_factor)) - { - power_factor = (cse7766->Energy.active_power && apparent_power) ? cse7766->Energy.active_power / apparent_power : 0; - if (power_factor > 1) - { - power_factor = 1; - } - } - - float reactive_power = cse7766->Energy.reactive_power; - if (isnan(reactive_power)) - { - reactive_power = 0; - uint32_t difference = ((uint32_t)(apparent_power * 100) - (uint32_t)(cse7766->Energy.active_power * 100)) / 10; - if ((cse7766->Energy.current > 0.005) && ((difference > 15) || (difference > (uint32_t)(apparent_power * 100 / 1000)))) - { - // calculating reactive power only if current is greater than 0.005A and - // difference between active and apparent power is greater than 1.5W or 1% - reactive_power = (float)(Util::RoundSqrtInt((uint32_t)(apparent_power * apparent_power * 100) - (uint32_t)(cse7766->Energy.active_power * cse7766->Energy.active_power * 100))) / 10; - } - } - - char apparent_power_chr[16]; - char reactive_power_chr[16]; - char power_factor_chr[16]; - Util::dtostrfd(apparent_power, wattage_resolution, apparent_power_chr); - Util::dtostrfd(reactive_power, wattage_resolution, reactive_power_chr); - Util::dtostrfd(power_factor, 2, power_factor_chr); - - char voltage_chr[16]; - char current_chr[16]; - char active_power_chr[16]; - Util::dtostrfd(cse7766->Energy.voltage, voltage_resolution, voltage_chr); - Util::dtostrfd(cse7766->Energy.current, current_resolution, current_chr); - Util::dtostrfd(cse7766->Energy.active_power, wattage_resolution, active_power_chr); - - char energy_daily_chr[16]; - char energy_yesterday_chr[16]; - char energy_total_chr[16]; - Util::dtostrfd(cse7766->Energy.daily, energy_resolution, energy_daily_chr); - Util::dtostrfd((float)config.energy_kWhyesterday / 100000, energy_resolution, energy_yesterday_chr); - Util::dtostrfd(cse7766->Energy.total, energy_resolution, energy_total_chr); - - sprintf(tmpData, "%s\"starttime\":\"%s\",\"total\":\"%s\",\"yesterday\":\"%s\",\"today\":\"%s\"," - "\"voltage\":\"%s\",\"current\":\"%s\",\"power\":\"%s\"," - "\"apparent_power\":\"%s\",\"reactive_power\":\"%s\",\"factor\":\"%s\"%s", - isMqtt ? "{" : ",", - kWhtotalTime, - energy_total_chr, energy_yesterday_chr, energy_daily_chr, - voltage_chr, current_chr, active_power_chr, - apparent_power_chr, reactive_power_chr, power_factor_chr, - isMqtt ? "}" : ""); -} - -void DC1::reportEnergy() -{ - energyShow(true); - Mqtt::publish(Mqtt::getTeleTopic("ENERGY"), tmpData, globalConfig.mqtt.retain); -} - -void DC1::reportPower() -{ - for (size_t ch = 0; ch < channels; ch++) - { - Mqtt::publish((powerTopic + (ch + 1)), bitRead(lastState, ch) ? "ON" : "OFF", globalConfig.mqtt.retain); - } + +#include "DC1.h" +#include "Rtc.h" +#include "Util.h" + +#pragma region 继承 + +void DC1::init() +{ + Led::init(LED_PIN, LOW); + cat9554 = new CAT9554(CAT9554_SDA_PIN, CAT9554_SCL_PIN); + cat9554->setIrqPin(CAT9554_IRQ_PIN); + cat9554->setup(); + + cse7766 = new CSE7766(CSE7766_RX_PIN, CSE7766_BAUDRATE); + + // 按键 + pinMode(KEY_0_PIN, INPUT_PULLDOWN_16); + cat9554->pinMode(KEY_1_PIN, INPUT); + cat9554->pinMode(KEY_2_PIN, INPUT); + cat9554->pinMode(KEY_3_PIN, INPUT); + + // 继电器 + cat9554->pinMode(REL_0_PIN, OUTPUT); + cat9554->pinMode(REL_1_PIN, OUTPUT); + cat9554->pinMode(REL_2_PIN, OUTPUT); + cat9554->pinMode(REL_3_PIN, OUTPUT); + + pinMode(LOGO_LED_PIN, OUTPUT); + logoLed(); + + channels = 4; + for (uint8_t ch = 0; ch < channels; ch++) + { + // 0:开关通电时断开 1 : 开关通电时闭合 2 : 开关通电时状态与断电前相反 3 : 开关通电时保持断电前状态 + if (config.power_on_state == 2) + { + switchRelay(ch, !bitRead(config.last_state, ch), false); // 开关通电时状态与断电前相反 + } + else if (config.power_on_state == 3) + { + switchRelay(ch, bitRead(config.last_state, ch), false); // 开关通电时保持断电前状态 + } + else + { + switchRelay(ch, config.power_on_state == 1, false); // 开关通电时闭合 + } + // 总开关关时跳过其他 + if (ch == 0 && !bitRead(lastState, 0) && config.sub_kinkage != 0) + { + break; + } + } + energyInit(); + powerTopic = Mqtt::getStatTopic(F("POWER")); +} + +bool DC1::moduleLed() +{ + if (WiFi.status() == WL_CONNECTED && Mqtt::mqttClient.connected()) + { + if (config.wifi_led == 0) + { + Led::on(); + return true; + } + else if (config.wifi_led == 1) + { + Led::off(); + return true; + } + } + return false; +} + +void DC1::loop() +{ + cse7766->loop(); + for (size_t ch = 0; ch < channels; ch++) + { + checkButton(ch); + } + + if (bitRead(operationFlag, 0)) + { + bitClear(operationFlag, 0); + energyUpdate(); + } +} + +void DC1::perSecondDo() +{ + bitSet(operationFlag, 0); +} +#pragma endregion + +#pragma region 配置 + +void DC1::readConfig() +{ + Config::moduleReadConfig(MODULE_CFG_VERSION, sizeof(DC1ConfigMessage), DC1ConfigMessage_fields, &config); +} + +void DC1::resetConfig() +{ + Debug::AddInfo(PSTR("moduleResetConfig . . . OK")); + memset(&config, 0, sizeof(DC1ConfigMessage)); + + config.power_on_state = 3; + config.power_mode = 0; + config.logo_led = 0; + config.wifi_led = 0; + config.sub_kinkage = 2; + config.energy_power_delta = 10; + config.report_interval = 60; + config.energy_max_power = 2300; +} + +void DC1::saveConfig(bool isEverySecond) +{ + if (bitRead(operationFlag, 1) || !isEverySecond) + { + bitClear(operationFlag, 1); + energySync(); + } + Config::moduleSaveConfig(MODULE_CFG_VERSION, DC1ConfigMessage_size, DC1ConfigMessage_fields, &config); +} +#pragma endregion + +#pragma region MQTT + +void DC1::mqttCallback(String topicStr, String str) +{ + if (channels >= 1 && topicStr.endsWith("/POWER") || topicStr.endsWith("/POWER1")) + { + switchRelay(0, (str == "ON" ? true : (str == "OFF" ? false : !bitRead(lastState, 0)))); + } + else if (channels >= 2 && topicStr.endsWith("/POWER2")) + { + switchRelay(1, (str == "ON" ? true : (str == "OFF" ? false : !bitRead(lastState, 1)))); + } + else if (channels >= 3 && topicStr.endsWith("/POWER3")) + { + switchRelay(2, (str == "ON" ? true : (str == "OFF" ? false : !bitRead(lastState, 2)))); + } + else if (channels >= 4 && topicStr.endsWith("/POWER4")) + { + switchRelay(3, (str == "ON" ? true : (str == "OFF" ? false : !bitRead(lastState, 3)))); + } + else if (topicStr.endsWith("/clear")) + { + energyClear(); + } + else if (topicStr.endsWith("/report")) + { + reportPower(); + reportEnergy(); + } +} + +void DC1::mqttConnected() +{ + if (globalConfig.mqtt.discovery) + { + mqttDiscovery(true); + } + + reportPower(); + reportEnergy(); +} + +void DC1::mqttDiscovery(bool isEnable) +{ + char topic[50]; + char message[500]; + + String tmp = Mqtt::getCmndTopic(F("POWER")); + String availability = Mqtt::getTeleTopic(F("availability")); + for (size_t ch = 0; ch < channels; ch++) + { + sprintf(topic, "%s/switch/%s_%d/config", globalConfig.mqtt.discovery_prefix, UID, (ch + 1)); + if (isEnable) + { + sprintf(message, HASS_DISCOVER_DC1_SWICH, UID, (ch + 1), + (tmp + (ch + 1)).c_str(), + (powerTopic + (ch + 1)).c_str(), + availability.c_str()); + Mqtt::publish(topic, message, true); + //Debug::AddInfo(PSTR("discovery: %s - %s"), topic, message); + } + else + { + Mqtt::publish(topic, "", true); + } + } + + String tims[] = {"voltage", "current", "power", "apparent_power", "reactive_power", "factor", "total", "yesterday", "today", "starttime"}; + String tims2[] = {"V", "A", "W", "VA", "VAr", "", "kWh", "kWh", "kWh", ""}; + String energy = Mqtt::getTeleTopic(F("ENERGY")); + for (size_t i = 0; i < 10; i++) + { + sprintf(topic, "%s/sensor/%s_%s/config", globalConfig.mqtt.discovery_prefix, UID, tims[i].c_str()); + if (isEnable) + { + if (tims2[i].length() == 0) + { + sprintf(message, HASS_DISCOVER_DC1_SENSOR_WITHOUT_UNIT, + UID, tims[i].c_str(), + energy.c_str(), + tims[i].c_str()); + } + else + { + sprintf(message, HASS_DISCOVER_DC1_SENSOR, + UID, tims[i].c_str(), + energy.c_str(), + tims[i].c_str(), + tims2[i].c_str()); + } + Mqtt::publish(topic, message, true); + //Debug::AddInfo(PSTR("discovery: %s - %s"), topic, message); + } + else + { + Mqtt::publish(topic, "", true); + } + } + if (isEnable) + { + Mqtt::availability(); + reportPower(); + reportEnergy(); + } +} +#pragma endregion + +#pragma region Http + +void DC1::httpAdd(ESP8266WebServer *server) +{ + server->on(F("/dc1_do"), std::bind(&DC1::httpDo, this, server)); + server->on(F("/dc1_setting"), std::bind(&DC1::httpSetting, this, server)); + server->on(F("/ha"), std::bind(&DC1::httpHa, this, server)); +} + +String DC1::httpGetStatus(ESP8266WebServer *server) +{ + String data; + for (size_t ch = 0; ch < channels; ch++) + { + data += ",\"POWER" + String(ch + 1) + "\":"; + data += bitRead(lastState, ch) ? 1 : 0; + } + energyShow(false); + data += String(tmpData); + return data.substring(1); +} + +void DC1::httpHtml(ESP8266WebServer *server) +{ + String radioJs = F(""); + + server->sendContent(page); + server->sendContent(radioJs); +} + +void DC1::httpDo(ESP8266WebServer *server) +{ + String c = server->arg(F("c")); + if (c != F("1") && c != F("2") && c != F("3") && c != F("4")) + { + server->send(200, F("text/html"), F("{\"code\":0,\"msg\":\"参数错误。\"}")); + return; + } + uint8_t ch = c.toInt() - 1; + if (ch > channels) + { + server->send(200, F("text/html"), F("{\"code\":0,\"msg\":\"继电器数量错误。\"}")); + return; + } + String str = server->arg(F("do")); + switchRelay(ch, (str == "ON" ? true : (str == "OFF" ? false : !bitRead(lastState, ch)))); + + server->send(200, F("text/html"), "{\"code\":1,\"msg\":\"操作成功\",\"data\":{" + httpGetStatus(server) + "}}"); +} + +void DC1::httpSetting(ESP8266WebServer *server) +{ + if (server->hasArg(F("c"))) + { + energyClear(); + server->send(200, F("text/html"), "{\"code\":1,\"msg\":\"重置用电量成功。\",\"data\":{" + httpGetStatus(server) + "}}"); + return; + } + config.power_on_state = server->arg(F("power_on_state")).toInt(); + config.power_mode = server->arg(F("power_mode")).toInt(); + config.logo_led = server->arg(F("logo_led")).toInt(); + config.wifi_led = server->arg(F("wifi_led")).toInt(); + config.sub_kinkage = server->arg(F("sub_kinkage")).toInt(); + + config.report_interval = server->arg(F("report_interval")).toInt(); + config.energy_power_delta = server->arg(F("energy_power_delta")).toInt(); + config.energy_max_power = server->arg(F("energy_max_power")).toInt(); + + logoLed(); + + Config::saveConfig(); + server->send(200, F("text/html"), F("{\"code\":1,\"msg\":\"已经设置成功。\"}")); +} + +void DC1::httpHa(ESP8266WebServer *server) +{ + char attachment[100]; + snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=%s.yaml"), UID); + + server->setContentLength(CONTENT_LENGTH_UNKNOWN); + server->sendHeader(F("Content-Disposition"), attachment); + server->send(200, F("Content-Type: application/octet-stream"), ""); + + String availability = Mqtt::getTeleTopic(F("availability")); + String tmp = Mqtt::getCmndTopic(F("POWER")); + server->sendContent(F("switch:\r\n")); + for (size_t ch = 0; ch < channels; ch++) + { + server->sendContent(F(" - platform: mqtt\r\n name: \"")); + server->sendContent(UID); + server->sendContent(F("_")); + server->sendContent(String((ch + 1))); + server->sendContent(F("\"\r\n state_topic: \"")); + server->sendContent((powerTopic + (ch + 1))); + server->sendContent(F("\"\r\n command_topic: \"")); + server->sendContent((tmp + (ch + 1))); + server->sendContent(F("\"\r\n payload_on: \"ON\"\r\n payload_off: \"OFF\"\r\n availability_topic: \"")); + server->sendContent(availability); + server->sendContent(F("\"\r\n payload_available: \"online\"\r\n payload_not_available: \"offline\"\r\n\r\n")); + } + + String energy = Mqtt::getTeleTopic(F("ENERGY")); + String tims[] = {"voltage", "current", "power", "apparent_power", "reactive_power", "factor", "total", "yesterday", "today", "starttime"}; + String tims2[] = {"V", "A", "W", "VA", "VAr", "", "kWh", "kWh", "kWh", ""}; + server->sendContent(F("sensor:\r\n")); + for (size_t i = 0; i < 10; i++) + { + server->sendContent(F(" - platform: mqtt\r\n name: \"")); + server->sendContent(UID); + server->sendContent(F("_")); + server->sendContent(tims[i]); + server->sendContent(F("\"\r\n state_topic: \"")); + server->sendContent(energy); + server->sendContent(F("\"\r\n value_template: \"{{value_json.")); + server->sendContent(tims[i]); + server->sendContent(F("}}")); + if (tims2[i].length() > 0) + { + server->sendContent(F("\"\r\n unit_of_measurement: \"")); + server->sendContent(tims2[i]); + } + server->sendContent(F("\"\r\n\r\n")); + } +} +#pragma endregion + +void DC1::logoLed() +{ + if (config.logo_led == 0) + { + digitalWrite(LOGO_LED_PIN, LOW); + } + else if (config.logo_led == 1) + { + digitalWrite(LOGO_LED_PIN, HIGH); + } + else if (config.logo_led == 2) + { + digitalWrite(LOGO_LED_PIN, bitRead(lastState, 0) ? LOW : HIGH); + } + else if (config.logo_led == 3) + { + digitalWrite(LOGO_LED_PIN, bitRead(lastState, 0) ? HIGH : LOW); + } +} + +void DC1::switchRelay(uint8_t ch, bool isOn, bool isSave) +{ + if (ch > channels) + { + Debug::AddInfo(PSTR("invalid channel: %d"), ch); + return; + } + + if (ch > 0 || (ch == 0 && config.sub_kinkage == 0)) + { + if (!bitRead(lastState, 0) && isOn && config.sub_kinkage != 0) + { + if (config.sub_kinkage == 1 || !isSave) + { + isOn = false; + } + else if (config.sub_kinkage == 2) + { + switchRelay(0, true); + } + } + + if (isOn && config.power_mode == 1) + { + for (size_t ch2 = (config.sub_kinkage == 0 ? 0 : 1); ch2 < channels; ch2++) + { + if (ch2 != ch && bitRead(lastState, ch2)) + { + switchRelay(ch2, false, isSave); + } + } + } + } + Debug::AddInfo(PSTR("Relay %d . . . %s"), ch + 1, isOn ? "ON" : "OFF"); + + if (!cat9554->digitalWrite(relGPIO[ch], isOn ? HIGH : LOW)) + { + Debug::AddError(PSTR("CAT9554 digitalWrite Error")); + if (!cat9554->digitalWrite(relGPIO[ch], isOn ? HIGH : LOW)) + { + Debug::AddError(PSTR("CAT9554 digitalWrite Error2")); + return; + } + } + + bitWrite(lastState, ch, isOn); + + Mqtt::publish((powerTopic + (ch + 1)), isOn ? "ON" : "OFF", globalConfig.mqtt.retain); + + if (isSave && config.power_on_state > 0) + { + bitWrite(config.last_state, ch, isOn); + bitSet(operationFlag, 1); + Config::delaySaveConfig(10); + } + + if (ch == 0) + { + logoLed(); + if (isSave && config.sub_kinkage != 0) + { + for (size_t ch2 = 1; ch2 < channels; ch2++) + { + if (isOn) + { + if (bitRead(config.last_state, ch2)) + { + switchRelay(ch2, true, false); + } + } + else + { + switchRelay(ch2, false, false); + } + } + } + } +} + +void DC1::checkButton(uint8_t ch) +{ + bool buttonState = ch == 0 ? digitalRead(btnGPIO[ch]) : cat9554->digitalRead(btnGPIO[ch]); + + if (buttonState == 0) + { + if (!bitRead(buttonTiming, ch)) + { + bitSet(buttonTiming, ch); + buttonTimingStart[ch] = millis(); + } + else + { // buttonTiming = true + if (millis() >= (buttonTimingStart[ch] + buttonDebounceTime)) + { + buttonAction[ch] = 1; + } + if (millis() >= (buttonTimingStart[ch] + buttonLongPressTime)) + { + buttonAction[ch] = 2; + } + } + } + else + { + bitClear(buttonTiming, ch); + if (buttonAction[ch] != 0) + { + if (buttonAction[ch] == 1) // 执行短按动作 + { + switchRelay(ch, !bitRead(lastState, ch), true); + } + else if (buttonAction[ch] == 2) // 执行长按动作 + { + if (ch == 0) + { + Wifi::setupWifiManager(false); + } + } + buttonAction[ch] = 0; + } + } +} + +void DC1::energyUpdate() +{ + if (Rtc::rtcTime.valid && config.energy_kWhtotal_time == 0) + { + config.energy_kWhtotal_time = Rtc::utcTime; + TIME_T tmpTime; + Rtc::breakTime(config.energy_kWhtotal_time, tmpTime); + snprintf_P(kWhtotalTime, sizeof(kWhtotalTime), PSTR("%04d-%02d-%02d %02d:%02d:%02d"), tmpTime.year, tmpTime.month, tmpTime.day_of_month, tmpTime.hour, tmpTime.minute, tmpTime.second); + } + if (Rtc::rtcTime.valid && config.energy_kWhdoy != Rtc::rtcTime.day_of_year) + { + Debug::AddInfo("day_of_year: %d %d %d", Rtc::rtcTime.day_of_year, Rtc::rtcTime.day_of_month, Rtc::rtcTime.day_of_week); + + energySync(); + config.energy_kWhdoy = Rtc::rtcTime.day_of_year; + config.energy_kWhyesterday = config.energy_kWhtoday; + config.energy_kWhtoday = 0; + Config::saveConfig(); + + cse7766->Energy.daily = (float)(config.energy_kWhtoday + cse7766->Energy.kWhtoday) / 100000; + cse7766->Energy.total = (float)(config.energy_kWhtotal + cse7766->Energy.kWhtoday) / 100000; + } + if (cse7766->everySecond()) + { + energyUpdateToday(); + } + if (perSecond % 301 == 0 && cse7766->Energy.kWhtoday > 0) + { + energySync(); + Config::saveConfig(); + } + energyMarginCheck(); +} + +void DC1::energySync() +{ + if (cse7766->Energy.kWhtoday > 0) + { + config.energy_kWhtoday += cse7766->Energy.kWhtoday; + config.energy_kWhtotal += cse7766->Energy.kWhtoday; + cse7766->Energy.kWhtoday = 0; + } +} + +void DC1::energyInit() +{ + cse7766->Energy.kWhtoday = 0; + cse7766->Energy.kWhtoday_delta = 0; + cse7766->Energy.daily = (float)(config.energy_kWhtoday) / 100000; + cse7766->Energy.total = (float)(config.energy_kWhtotal) / 100000; + + TIME_T tmpTime; + Rtc::breakTime(config.energy_kWhtotal_time, tmpTime); + snprintf_P(kWhtotalTime, sizeof(kWhtotalTime), PSTR("%04d-%02d-%02d %02d:%02d:%02d"), tmpTime.year, tmpTime.month, tmpTime.day_of_month, tmpTime.hour, tmpTime.minute, tmpTime.second); +} + +void DC1::energyClear() +{ + config.energy_kWhtoday = 0; + config.energy_kWhyesterday = 0; + config.energy_kWhtotal = 0; + config.energy_kWhdoy = 0; + config.energy_kWhtotal_time = 0; + + cse7766->Energy.kWhtoday_delta = 0; + + energyInit(); + Config::saveConfig(); +} + +void DC1::energyUpdateToday() +{ + if (cse7766->Energy.kWhtoday_delta > 1000) + { + unsigned long delta = cse7766->Energy.kWhtoday_delta / 1000; + cse7766->Energy.kWhtoday_delta -= (delta * 1000); + cse7766->Energy.kWhtoday += delta; + + cse7766->Energy.daily = (float)(config.energy_kWhtoday + cse7766->Energy.kWhtoday) / 100000; + cse7766->Energy.total = (float)(config.energy_kWhtotal + cse7766->Energy.kWhtoday) / 100000; + } +} + +void DC1::energyMarginCheck() +{ + if (cse7766->Energy.power_steady_counter) + { + cse7766->Energy.power_steady_counter--; + return; + } + + uint16_t energy_power_u = (uint16_t)(cse7766->Energy.active_power); + uint16_t energy_voltage_u = (uint16_t)(cse7766->Energy.voltage); + uint16_t energy_current_u = (uint16_t)(cse7766->Energy.current * 1000); + + //Debug::AddInfo(PSTR("NRG: W %d, U %d, I %d"), energy_power_u, energy_voltage_u, energy_current_u); + if (config.energy_power_delta) + { + uint16_t delta = abs(cse7766->Energy.power_history[0] - energy_power_u); + if (delta > 0) + { + if (config.energy_power_delta < 101) + { // 1..100 = Percentage + uint16_t min_power = (cse7766->Energy.power_history[0] > energy_power_u) ? energy_power_u : cse7766->Energy.power_history[0]; + if (0 == min_power) + { + min_power++; + } // Fix divide by 0 exception (#6741) + if (((delta * 100) / min_power) > config.energy_power_delta) + { + cse7766->Energy.power_delta = true; + } + } + else + { // 101..32000 = Absolute + if (delta > (config.energy_power_delta - 100)) + { + cse7766->Energy.power_delta = true; + } + } + if (cse7766->Energy.power_delta) + { + cse7766->Energy.power_history[1] = cse7766->Energy.active_power; // We only want one report so reset history + cse7766->Energy.power_history[2] = cse7766->Energy.active_power; + } + } + } + + cse7766->Energy.power_history[0] = cse7766->Energy.power_history[1]; // Shift in history every second allowing power changes to settle for up to three seconds + cse7766->Energy.power_history[1] = cse7766->Energy.power_history[2]; + cse7766->Energy.power_history[2] = energy_power_u; + + if (config.report_interval > 0 && (perSecond % config.report_interval) == 0) + { + reportPower(); + cse7766->Energy.power_delta = true; + } + if (cse7766->Energy.power_delta) + { + cse7766->Energy.power_delta = false; + reportEnergy(); + } + if (config.energy_max_power) + { + DC1::energyMaxPower(); + } +} + +void DC1::energyMaxPower() +{ + if (cse7766->Energy.active_power > config.energy_max_power) + { + if (!cse7766->Energy.mplh_counter) + { + cse7766->Energy.mplh_counter = MAX_POWER_HOLD; + } + else + { + cse7766->Energy.mplh_counter--; + if (!cse7766->Energy.mplh_counter) + { + Debug::AddError(PSTR("MaxPowerReached: %d"), (uint16_t)cse7766->Energy.active_power); + lastState2 = lastState; + for (size_t ch = 0; ch < channels; ch++) + { + if (bitRead(lastState, ch)) + { + switchRelay(ch, false, false); + } + } + if (!cse7766->Energy.mplr_counter) + { + cse7766->Energy.mplr_counter = MAX_POWER_RETRY + 1; + } + cse7766->Energy.mplw_counter = MAX_POWER_WINDOW; + cse7766->Energy.mplv_counter = 0; + } + } + } + else if (lastState && (cse7766->Energy.active_power <= config.energy_max_power)) + { + cse7766->Energy.mplh_counter = 0; + cse7766->Energy.mplw_counter = 0; + + if (cse7766->Energy.mplv_counter++ == 60) + { + cse7766->Energy.mplv_counter = 0; + cse7766->Energy.mplr_counter = 0; + } + } + if (!lastState) + { + if (cse7766->Energy.mplw_counter) + { + cse7766->Energy.mplw_counter--; + } + else + { + if (cse7766->Energy.mplr_counter) + { + cse7766->Energy.mplr_counter--; + if (cse7766->Energy.mplr_counter) + { + Debug::AddError(PSTR("PowerMonitor ON")); + if (lastState2) + { + for (size_t ch = 0; ch < channels; ch++) + { + if (bitRead(lastState2, ch)) + { + switchRelay(ch, true, false); + } + } + lastState2 = 0; + } + } + else + { + Debug::AddInfo(PSTR("MaxPowerReachedRetry OFF")); + } + } + } + } +} + +void DC1::energyShow(bool isMqtt) +{ + const uint8_t current_resolution = 3; + const uint8_t voltage_resolution = 0; + const uint8_t wattage_resolution = 0; + const uint8_t energy_resolution = 3; + + float apparent_power = cse7766->Energy.apparent_power; + if (isnan(apparent_power)) + { + apparent_power = cse7766->Energy.voltage * cse7766->Energy.current; + } + if (apparent_power < cse7766->Energy.active_power) + { // Should be impossible + cse7766->Energy.active_power = apparent_power; + } + + float power_factor = cse7766->Energy.power_factor; + if (isnan(power_factor)) + { + power_factor = (cse7766->Energy.active_power && apparent_power) ? cse7766->Energy.active_power / apparent_power : 0; + if (power_factor > 1) + { + power_factor = 1; + } + } + + float reactive_power = cse7766->Energy.reactive_power; + if (isnan(reactive_power)) + { + reactive_power = 0; + uint32_t difference = ((uint32_t)(apparent_power * 100) - (uint32_t)(cse7766->Energy.active_power * 100)) / 10; + if ((cse7766->Energy.current > 0.005) && ((difference > 15) || (difference > (uint32_t)(apparent_power * 100 / 1000)))) + { + // calculating reactive power only if current is greater than 0.005A and + // difference between active and apparent power is greater than 1.5W or 1% + reactive_power = (float)(Util::RoundSqrtInt((uint32_t)(apparent_power * apparent_power * 100) - (uint32_t)(cse7766->Energy.active_power * cse7766->Energy.active_power * 100))) / 10; + } + } + + char apparent_power_chr[16]; + char reactive_power_chr[16]; + char power_factor_chr[16]; + Util::dtostrfd(apparent_power, wattage_resolution, apparent_power_chr); + Util::dtostrfd(reactive_power, wattage_resolution, reactive_power_chr); + Util::dtostrfd(power_factor, 2, power_factor_chr); + + char voltage_chr[16]; + char current_chr[16]; + char active_power_chr[16]; + Util::dtostrfd(cse7766->Energy.voltage, voltage_resolution, voltage_chr); + Util::dtostrfd(cse7766->Energy.current, current_resolution, current_chr); + Util::dtostrfd(cse7766->Energy.active_power, wattage_resolution, active_power_chr); + + char energy_daily_chr[16]; + char energy_yesterday_chr[16]; + char energy_total_chr[16]; + Util::dtostrfd(cse7766->Energy.daily, energy_resolution, energy_daily_chr); + Util::dtostrfd((float)config.energy_kWhyesterday / 100000, energy_resolution, energy_yesterday_chr); + Util::dtostrfd(cse7766->Energy.total, energy_resolution, energy_total_chr); + + sprintf(tmpData, "%s\"starttime\":\"%s\",\"total\":\"%s\",\"yesterday\":\"%s\",\"today\":\"%s\"," + "\"voltage\":\"%s\",\"current\":\"%s\",\"power\":\"%s\"," + "\"apparent_power\":\"%s\",\"reactive_power\":\"%s\",\"factor\":\"%s\"%s", + isMqtt ? "{" : ",", + kWhtotalTime, + energy_total_chr, energy_yesterday_chr, energy_daily_chr, + voltage_chr, current_chr, active_power_chr, + apparent_power_chr, reactive_power_chr, power_factor_chr, + isMqtt ? "}" : ""); +} + +void DC1::reportEnergy() +{ + energyShow(true); + Mqtt::publish(Mqtt::getTeleTopic("ENERGY"), tmpData, globalConfig.mqtt.retain); +} + +void DC1::reportPower() +{ + for (size_t ch = 0; ch < channels; ch++) + { + Mqtt::publish((powerTopic + (ch + 1)), bitRead(lastState, ch) ? "ON" : "OFF", globalConfig.mqtt.retain); + } } \ No newline at end of file diff --git a/src/DC1Config.pb.c b/src/DC1Config.pb.c index a852829..8c68b0c 100644 --- a/src/DC1Config.pb.c +++ b/src/DC1Config.pb.c @@ -1,35 +1,35 @@ -/* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.3.9.4 at Wed Feb 19 10:49:10 2020. */ - -#include "DC1Config.pb.h" - -/* @@protoc_insertion_point(includes) */ -#if PB_PROTO_HEADER_VERSION != 30 -#error Regenerate this file with the current version of nanopb generator. -#endif - - - -const pb_field_t DC1ConfigMessage_fields[18] = { - PB_FIELD( 1, UINT32 , SINGULAR, STATIC , FIRST, DC1ConfigMessage, last_state, last_state, 0), - PB_FIELD( 2, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, power_on_state, last_state, 0), - PB_FIELD( 3, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, power_mode, power_on_state, 0), - PB_FIELD( 4, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, logo_led, power_mode, 0), - PB_FIELD( 5, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, wifi_led, logo_led, 0), - PB_FIELD( 6, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, sub_kinkage, wifi_led, 0), - PB_FIELD( 20, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, report_interval, sub_kinkage, 0), - PB_FIELD( 21, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, energy_power_delta, report_interval, 0), - PB_FIELD( 22, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, energy_power_calibration, energy_power_delta, 0), - PB_FIELD( 23, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, energy_voltage_calibration, energy_power_calibration, 0), - PB_FIELD( 24, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, energy_current_calibration, energy_voltage_calibration, 0), - PB_FIELD( 25, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, energy_kWhtoday, energy_current_calibration, 0), - PB_FIELD( 26, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, energy_kWhyesterday, energy_kWhtoday, 0), - PB_FIELD( 27, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, energy_kWhtotal, energy_kWhyesterday, 0), - PB_FIELD( 28, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, energy_kWhdoy, energy_kWhtotal, 0), - PB_FIELD( 29, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, energy_kWhtotal_time, energy_kWhdoy, 0), - PB_FIELD( 30, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, energy_max_power, energy_kWhtotal_time, 0), - PB_LAST_FIELD -}; - - -/* @@protoc_insertion_point(eof) */ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.9.4 at Wed Feb 19 10:49:10 2020. */ + +#include "DC1Config.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t DC1ConfigMessage_fields[18] = { + PB_FIELD( 1, UINT32 , SINGULAR, STATIC , FIRST, DC1ConfigMessage, last_state, last_state, 0), + PB_FIELD( 2, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, power_on_state, last_state, 0), + PB_FIELD( 3, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, power_mode, power_on_state, 0), + PB_FIELD( 4, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, logo_led, power_mode, 0), + PB_FIELD( 5, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, wifi_led, logo_led, 0), + PB_FIELD( 6, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, sub_kinkage, wifi_led, 0), + PB_FIELD( 20, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, report_interval, sub_kinkage, 0), + PB_FIELD( 21, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, energy_power_delta, report_interval, 0), + PB_FIELD( 22, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, energy_power_calibration, energy_power_delta, 0), + PB_FIELD( 23, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, energy_voltage_calibration, energy_power_calibration, 0), + PB_FIELD( 24, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, energy_current_calibration, energy_voltage_calibration, 0), + PB_FIELD( 25, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, energy_kWhtoday, energy_current_calibration, 0), + PB_FIELD( 26, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, energy_kWhyesterday, energy_kWhtoday, 0), + PB_FIELD( 27, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, energy_kWhtotal, energy_kWhyesterday, 0), + PB_FIELD( 28, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, energy_kWhdoy, energy_kWhtotal, 0), + PB_FIELD( 29, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, energy_kWhtotal_time, energy_kWhdoy, 0), + PB_FIELD( 30, UINT32 , SINGULAR, STATIC , OTHER, DC1ConfigMessage, energy_max_power, energy_kWhtotal_time, 0), + PB_LAST_FIELD +}; + + +/* @@protoc_insertion_point(eof) */ diff --git a/src/main.cpp b/src/main.cpp index a2fe2bc..7a6376e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,23 +1,23 @@ -#include -#include -#include -#include -#include -#include - -#include "Framework.h" -#include "DC1.h" - -void setup() -{ - Framework::one(115200); - - module = new DC1(); - - Framework::setup(); -} - -void loop() -{ - Framework::loop(); +#include +#include +#include +#include +#include +#include + +#include "Framework.h" +#include "DC1.h" + +void setup() +{ + Framework::one(115200); + + module = new DC1(); + + Framework::setup(); +} + +void loop() +{ + Framework::loop(); } \ No newline at end of file diff --git "a/\345\233\272\344\273\266\347\203\247\345\275\225.md" "b/\345\233\272\344\273\266\347\203\247\345\275\225.md" index c7a9456..ffab417 100644 --- "a/\345\233\272\344\273\266\347\203\247\345\275\225.md" +++ "b/\345\233\272\344\273\266\347\203\247\345\275\225.md" @@ -1,94 +1,94 @@ - - -固件烧录需要拆解DC1,通过ttl模块与pc相连,运行烧录软件进行刷机. - - - -## 目录 - -[拆机与TTL接线方法](#拆机与TTL接线方法) - -​ [1、拆主控板](#1、拆主控板) - -​ [2、主控板接线](#2、主控板接线) - -[软件刷机](#软件刷机) - -​ [工具/固件下载](#工具/固件下载) - -​ [开始烧录](#开始烧录) - - - -## 拆机与TTL接线方法 - -> 本章节(拆机与TTL接线方法)中所有内容(包含图片)均来自[三木大神的基于esphome的DC1插座自制固件](https://github.com/Samuel-0-0/phicomm_dc1-esphome/tree/master/cookbook),感谢授权使用 - -![image](https://github.com/Samuel-0-0/phicomm_dc1-esphome/blob/master/cookbook/image/warning_50px.png?raw=true) 请勿带市电操作!!!危险自负!!! - -#### 1、拆主控板 - -##### 用到的工具 - -![image](file/flash/disassembly.jpg) - -##### 拆板步骤 - -**如果插座刚通过电,拆的时候千万不要去摸电路板,电容带电!!!** - -![image](file/flash/1.jpg) -![image](file/flash/2.jpg) -![image](file/flash/3.jpg) -![image](file/flash/4.jpg) -![image](file/flash/5.jpg) -![image](file/flash/6.jpg) - -#### 2、主控板接线 - -##### 用到的TTL工具 - -任选一个即可 -![image](file/flash/ttl.jpg) - - - -##### 接线方法 - -![image](file/flash/wiring_diagram.jpg) -![image](file/flash/wiring_diagram1.jpg) - - - -## 软件刷机 - -### 工具/固件下载 - -确认硬件连接正常后,下载以下软件: - -烧录软件: flash_download_tools_vX.zip [点这里下载](https://www.espressif.com/zh-hans/support/download/other-tools) - -完整固件: dc1.bin [点这里下载](https://github.com/qlwz/esp_dc1/releases) - -### 开始烧录 - -将flash_download_tools_vX.zip解压,打开目录下的flash_download_tools_vX.exe,选择ESP8266 DownloadTool,根据以下截图做配置, - -![image](file/flash/flash1.png) - -将与主控板连接的usbTTL连接上电脑(确保主控io0必需短接gnd后再上电,以进入刷机模式),根据自己的实际串口号设置.,点击START按钮即可开始烧录. - - - -稍等片刻,出现![FINISH_S](file/flash/FINISH_S.bmp)即为烧录超过 - - - -注意:部分发现烧录完成后可能出现问题无法使用.可以尝试用以上烧录软件ERASE擦除一次后重新烧录. - -进入烧录模式后点ERASE,显示完成即为擦除超过.再将主控板重新上电并再次进入刷机模式,重新点START烧录即可 - -## 从ESPhome升级到ESP DC1固件 - -有人测试直接从esphomeota到dc1固件成功,本人未做测试,需要的可以试试看.可以将结果反馈给我,谢谢 - + + +固件烧录需要拆解DC1,通过ttl模块与pc相连,运行烧录软件进行刷机. + + + +## 目录 + +[拆机与TTL接线方法](#拆机与TTL接线方法) + +​ [1、拆主控板](#1、拆主控板) + +​ [2、主控板接线](#2、主控板接线) + +[软件刷机](#软件刷机) + +​ [工具/固件下载](#工具/固件下载) + +​ [开始烧录](#开始烧录) + + + +## 拆机与TTL接线方法 + +> 本章节(拆机与TTL接线方法)中所有内容(包含图片)均来自[三木大神的基于esphome的DC1插座自制固件](https://github.com/Samuel-0-0/phicomm_dc1-esphome/tree/master/cookbook),感谢授权使用 + +![image](https://github.com/Samuel-0-0/phicomm_dc1-esphome/blob/master/cookbook/image/warning_50px.png?raw=true) 请勿带市电操作!!!危险自负!!! + +#### 1、拆主控板 + +##### 用到的工具 + +![image](file/flash/disassembly.jpg) + +##### 拆板步骤 + +**如果插座刚通过电,拆的时候千万不要去摸电路板,电容带电!!!** + +![image](file/flash/1.jpg) +![image](file/flash/2.jpg) +![image](file/flash/3.jpg) +![image](file/flash/4.jpg) +![image](file/flash/5.jpg) +![image](file/flash/6.jpg) + +#### 2、主控板接线 + +##### 用到的TTL工具 + +任选一个即可 +![image](file/flash/ttl.jpg) + + + +##### 接线方法 + +![image](file/flash/wiring_diagram.jpg) +![image](file/flash/wiring_diagram1.jpg) + + + +## 软件刷机 + +### 工具/固件下载 + +确认硬件连接正常后,下载以下软件: + +烧录软件: flash_download_tools_vX.zip [点这里下载](https://www.espressif.com/zh-hans/support/download/other-tools) + +完整固件: dc1.bin [点这里下载](https://github.com/qlwz/esp_dc1/releases) + +### 开始烧录 + +将flash_download_tools_vX.zip解压,打开目录下的flash_download_tools_vX.exe,选择ESP8266 DownloadTool,根据以下截图做配置, + +![image](file/flash/flash1.png) + +将与主控板连接的usbTTL连接上电脑(确保主控io0必需短接gnd后再上电,以进入刷机模式),根据自己的实际串口号设置.,点击START按钮即可开始烧录. + + + +稍等片刻,出现![FINISH_S](file/flash/FINISH_S.bmp)即为烧录超过 + + + +注意:部分发现烧录完成后可能出现问题无法使用.可以尝试用以上烧录软件ERASE擦除一次后重新烧录. + +进入烧录模式后点ERASE,显示完成即为擦除超过.再将主控板重新上电并再次进入刷机模式,重新点START烧录即可 + +## 从ESPhome升级到ESP DC1固件 + +有人测试直接从esphomeota到dc1固件成功,本人未做测试,需要的可以试试看.可以将结果反馈给我,谢谢 + 在esphome中ota选择dc1.bin固件升级即可 \ No newline at end of file