├── .clang-format ├── .github ├── FUNDING.yml └── workflows │ ├── ci.yaml │ └── matchers │ ├── ci-custom.json │ ├── clang-tidy.json │ ├── gcc.json │ ├── lint-python.json │ └── python.json ├── .gitignore ├── .yamllint ├── LICENSE ├── README.md ├── docs └── HS_MS_MSX RS232 Protocol.pdf ├── esp32-example.yaml ├── esp8266-example.yaml ├── images ├── 001.jpg ├── 002.jpg ├── 003.jpg ├── 004.jpg ├── 005.jpg ├── rj45-colors-t568a-vs-t568.png └── rj45-colors-t568a-vs-t568.svg └── tests ├── esp8266-test-ping.yaml ├── esp8266-test-pong.yaml ├── esp8266-test-protocols.yaml └── esp8266-uart-sniffer.yaml /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | AccessModifierOffset: -1 3 | AlignAfterOpenBracket: Align 4 | AlignConsecutiveAssignments: false 5 | AlignConsecutiveDeclarations: false 6 | AlignEscapedNewlines: DontAlign 7 | AlignOperands: true 8 | AlignTrailingComments: true 9 | AllowAllParametersOfDeclarationOnNextLine: true 10 | AllowShortBlocksOnASingleLine: false 11 | AllowShortCaseLabelsOnASingleLine: false 12 | AllowShortFunctionsOnASingleLine: All 13 | AllowShortIfStatementsOnASingleLine: false 14 | AllowShortLoopsOnASingleLine: false 15 | AlwaysBreakAfterReturnType: None 16 | AlwaysBreakBeforeMultilineStrings: false 17 | AlwaysBreakTemplateDeclarations: MultiLine 18 | BinPackArguments: true 19 | BinPackParameters: true 20 | BraceWrapping: 21 | AfterClass: false 22 | AfterControlStatement: false 23 | AfterEnum: false 24 | AfterFunction: false 25 | AfterNamespace: false 26 | AfterObjCDeclaration: false 27 | AfterStruct: false 28 | AfterUnion: false 29 | AfterExternBlock: false 30 | BeforeCatch: false 31 | BeforeElse: false 32 | IndentBraces: false 33 | SplitEmptyFunction: true 34 | SplitEmptyRecord: true 35 | SplitEmptyNamespace: true 36 | BreakBeforeBinaryOperators: None 37 | BreakBeforeBraces: Attach 38 | BreakBeforeInheritanceComma: false 39 | BreakInheritanceList: BeforeColon 40 | BreakBeforeTernaryOperators: true 41 | BreakConstructorInitializersBeforeComma: false 42 | BreakConstructorInitializers: BeforeColon 43 | BreakAfterJavaFieldAnnotations: false 44 | BreakStringLiterals: true 45 | ColumnLimit: 120 46 | CommentPragmas: "^ IWYU pragma:" 47 | CompactNamespaces: false 48 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 49 | ConstructorInitializerIndentWidth: 4 50 | ContinuationIndentWidth: 4 51 | Cpp11BracedListStyle: true 52 | DerivePointerAlignment: true 53 | DisableFormat: false 54 | ExperimentalAutoDetectBinPacking: false 55 | FixNamespaceComments: true 56 | ForEachMacros: 57 | - foreach 58 | - Q_FOREACH 59 | - BOOST_FOREACH 60 | IncludeBlocks: Preserve 61 | IncludeCategories: 62 | - Regex: '^' 63 | Priority: 2 64 | - Regex: '^<.*\.h>' 65 | Priority: 1 66 | - Regex: "^<.*" 67 | Priority: 2 68 | - Regex: ".*" 69 | Priority: 3 70 | IncludeIsMainRegex: "([-_](test|unittest))?$" 71 | IndentCaseLabels: true 72 | IndentPPDirectives: None 73 | IndentWidth: 2 74 | IndentWrappedFunctionNames: false 75 | KeepEmptyLinesAtTheStartOfBlocks: false 76 | MacroBlockBegin: "" 77 | MacroBlockEnd: "" 78 | MaxEmptyLinesToKeep: 1 79 | NamespaceIndentation: None 80 | PenaltyBreakAssignment: 2 81 | PenaltyBreakBeforeFirstCallParameter: 1 82 | PenaltyBreakComment: 300 83 | PenaltyBreakFirstLessLess: 120 84 | PenaltyBreakString: 1000 85 | PenaltyBreakTemplateDeclaration: 10 86 | PenaltyExcessCharacter: 1000000 87 | PenaltyReturnTypeOnItsOwnLine: 2000 88 | PointerAlignment: Right 89 | RawStringFormats: 90 | - Language: Cpp 91 | Delimiters: 92 | - cc 93 | - CC 94 | - cpp 95 | - Cpp 96 | - CPP 97 | - "c++" 98 | - "C++" 99 | CanonicalDelimiter: "" 100 | BasedOnStyle: google 101 | - Language: TextProto 102 | Delimiters: 103 | - pb 104 | - PB 105 | - proto 106 | - PROTO 107 | EnclosingFunctions: 108 | - EqualsProto 109 | - EquivToProto 110 | - PARSE_PARTIAL_TEXT_PROTO 111 | - PARSE_TEST_PROTO 112 | - PARSE_TEXT_PROTO 113 | - ParseTextOrDie 114 | - ParseTextProtoOrDie 115 | CanonicalDelimiter: "" 116 | BasedOnStyle: google 117 | ReflowComments: true 118 | SortIncludes: false 119 | SortUsingDeclarations: false 120 | SpaceAfterCStyleCast: true 121 | SpaceAfterTemplateKeyword: false 122 | SpaceBeforeAssignmentOperators: true 123 | SpaceBeforeCpp11BracedList: false 124 | SpaceBeforeCtorInitializerColon: true 125 | SpaceBeforeInheritanceColon: true 126 | SpaceBeforeParens: ControlStatements 127 | SpaceBeforeRangeBasedForLoopColon: true 128 | SpaceInEmptyParentheses: false 129 | SpacesBeforeTrailingComments: 2 130 | SpacesInAngles: false 131 | SpacesInContainerLiterals: false 132 | SpacesInCStyleCastParentheses: false 133 | SpacesInParentheses: false 134 | SpacesInSquareBrackets: false 135 | Standard: Auto 136 | TabWidth: 2 137 | UseTab: Never 138 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | buy_me_a_coffee: syssi 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: # yamllint disable-line rule:truthy 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | schedule: 9 | - cron: 0 12 * * * 10 | 11 | env: 12 | FORCE_COLOR: 1 13 | 14 | jobs: 15 | yamllint: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v1 19 | - name: yaml-lint 20 | uses: ibiqlik/action-yamllint@v3 21 | with: 22 | config_file: .yamllint 23 | 24 | esphome-config: 25 | runs-on: ubuntu-latest 26 | steps: 27 | - name: ⤵️ Check out configuration from GitHub 28 | uses: actions/checkout@v2 29 | - name: Setup Python 3.12 30 | uses: actions/setup-python@v1 31 | with: 32 | python-version: 3.12 33 | - name: Install dependencies 34 | run: | 35 | python -m pip install --upgrade pip setuptools wheel 36 | pip install esphome 37 | pip list 38 | esphome version 39 | - name: Write secrets.yaml 40 | shell: bash 41 | run: 'echo -e "wifi_ssid: ssid\nwifi_password: password\nmqtt_host: host\nmqtt_username: username\nmqtt_password: password" > secrets.yaml' 42 | - name: Write tests/secrets.yaml 43 | shell: bash 44 | run: 'echo -e "wifi_ssid: ssid\nwifi_password: password\nmqtt_host: host\nmqtt_username: username\nmqtt_password: password" > tests/secrets.yaml' 45 | - run: | 46 | for YAML in esp*.yaml; do 47 | esphome config $YAML >/dev/null 48 | done 49 | - run: | 50 | for YAML in tests/esp*.yaml; do 51 | esphome config $YAML >/dev/null 52 | done 53 | 54 | esphome-compile: 55 | runs-on: ubuntu-latest 56 | needs: [esphome-config] 57 | steps: 58 | - name: ⤵️ Check out configuration from GitHub 59 | uses: actions/checkout@v2 60 | - name: Cache .esphome 61 | uses: actions/cache@v4 62 | with: 63 | path: .esphome 64 | key: esphome-compile-esphome-${{ hashFiles('*.yaml') }} 65 | restore-keys: esphome-compile-esphome- 66 | - name: Cache .pioenvs 67 | uses: actions/cache@v4 68 | with: 69 | path: .pioenvs 70 | key: esphome-compile-pioenvs-${{ hashFiles('*.yaml') }} 71 | restore-keys: esphome-compile-pioenvs- 72 | - name: Set up Python 3.12 73 | uses: actions/setup-python@v1 74 | with: 75 | python-version: 3.12 76 | - name: Install dependencies 77 | run: | 78 | python -m pip install --upgrade pip setuptools wheel 79 | pip install esphome 80 | pip list 81 | esphome version 82 | - name: Register problem matchers 83 | run: | 84 | echo "::add-matcher::.github/workflows/matchers/gcc.json" 85 | echo "::add-matcher::.github/workflows/matchers/python.json" 86 | - name: Write secrets.yaml 87 | shell: bash 88 | run: 'echo -e "wifi_ssid: ssid\nwifi_password: password\nmqtt_host: host\nmqtt_username: username\nmqtt_password: password" > secrets.yaml' 89 | - run: | 90 | for YAML in esp*.yaml; do 91 | esphome compile $YAML 92 | done 93 | -------------------------------------------------------------------------------- /.github/workflows/matchers/ci-custom.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "ci-custom", 5 | "pattern": [ 6 | { 7 | "regexp": "^ERROR (.*):(\\d+):(\\d+) - (.*)$", 8 | "file": 1, 9 | "line": 2, 10 | "column": 3, 11 | "message": 4 12 | } 13 | ] 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.github/workflows/matchers/clang-tidy.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "clang-tidy", 5 | "pattern": [ 6 | { 7 | "regexp": "^(.*):(\\d+):(\\d+):\\s+(error):\\s+(.*) \\[([a-z0-9,\\-]+)\\]\\s*$", 8 | "file": 1, 9 | "line": 2, 10 | "column": 3, 11 | "severity": 4, 12 | "message": 5 13 | } 14 | ] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.github/workflows/matchers/gcc.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "gcc", 5 | "severity": "error", 6 | "pattern": [ 7 | { 8 | "regexp": "^(.*):(\\d+):(\\d+):\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$", 9 | "file": 1, 10 | "line": 2, 11 | "column": 3, 12 | "severity": 4, 13 | "message": 5 14 | } 15 | ] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.github/workflows/matchers/lint-python.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "flake8", 5 | "severity": "error", 6 | "pattern": [ 7 | { 8 | "regexp": "^(.*):(\\d+) - ([EFCDNW]\\d{3}.*)$", 9 | "file": 1, 10 | "line": 2, 11 | "message": 3 12 | } 13 | ] 14 | }, 15 | { 16 | "owner": "pylint", 17 | "severity": "error", 18 | "pattern": [ 19 | { 20 | "regexp": "^(.*):(\\d+) - (\\[[EFCRW]\\d{4}\\(.*\\),.*\\].*)$", 21 | "file": 1, 22 | "line": 2, 23 | "message": 3 24 | } 25 | ] 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /.github/workflows/matchers/python.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "python", 5 | "pattern": [ 6 | { 7 | "regexp": "^\\s*File\\s\\\"(.*)\\\",\\sline\\s(\\d+),\\sin\\s(.*)$", 8 | "file": 1, 9 | "line": 2 10 | }, 11 | { 12 | "regexp": "^\\s*raise\\s(.*)\\(\\'(.*)\\'\\)$", 13 | "message": 2 14 | } 15 | ] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | secrets.yaml 3 | .esphome/ 4 | **/.pioenvs/ 5 | **/.piolibdeps/ 6 | **/lib/ 7 | **/src/ 8 | **/partitions.csv 9 | 10 | __pycache__/ 11 | *.py[cod] 12 | *$py.class 13 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | extends: default 2 | 3 | yaml-files: 4 | - '*.yaml' 5 | - '*.yml' 6 | - '.yamllint' 7 | 8 | ignore: | 9 | .clang-format 10 | .esphome/ 11 | tests/.esphome/ 12 | 13 | rules: 14 | braces: 15 | level: error 16 | min-spaces-inside: 0 17 | max-spaces-inside: 1 18 | min-spaces-inside-empty: -1 19 | max-spaces-inside-empty: -1 20 | brackets: 21 | level: error 22 | min-spaces-inside: 0 23 | max-spaces-inside: 0 24 | min-spaces-inside-empty: -1 25 | max-spaces-inside-empty: -1 26 | colons: 27 | level: error 28 | max-spaces-before: 0 29 | max-spaces-after: 1 30 | commas: 31 | level: error 32 | max-spaces-before: 0 33 | min-spaces-after: 1 34 | max-spaces-after: 1 35 | comments: 36 | level: error 37 | require-starting-space: true 38 | min-spaces-from-content: 2 39 | comments-indentation: disable 40 | document-end: 41 | level: error 42 | present: false 43 | document-start: 44 | level: error 45 | present: false 46 | empty-lines: 47 | level: error 48 | max: 2 49 | max-start: 0 50 | max-end: 1 51 | hyphens: 52 | level: error 53 | max-spaces-after: 1 54 | indentation: 55 | level: error 56 | spaces: 2 57 | indent-sequences: true 58 | check-multi-line-strings: false 59 | key-duplicates: 60 | level: error 61 | line-length: disable 62 | new-line-at-end-of-file: 63 | level: error 64 | new-lines: 65 | level: error 66 | type: unix 67 | trailing-spaces: 68 | level: error 69 | truthy: 70 | level: error 71 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esphome-pipsolar 2 | 3 | ![GitHub actions](https://github.com/syssi/esphome-pipsolar/actions/workflows/ci.yaml/badge.svg) 4 | ![GitHub stars](https://img.shields.io/github/stars/syssi/esphome-pipsolar) 5 | ![GitHub forks](https://img.shields.io/github/forks/syssi/esphome-pipsolar) 6 | ![GitHub watchers](https://img.shields.io/github/watchers/syssi/esphome-pipsolar) 7 | [!["Buy Me A Coffee"](https://img.shields.io/badge/buy%20me%20a%20coffee-donate-yellow.svg)](https://www.buymeacoffee.com/syssi) 8 | 9 | ESPHome example configuration to monitor and control a pipsolar inverter via RS232 10 | 11 | Kudos to [@andreashergert1984](https://github.com/andreashergert1984) for the great work! 12 | 13 | ## Supported devices 14 | 15 | * PIP4048 compatible PV Inverter 16 | 17 | 18 | ## Requirements 19 | 20 | * [ESPHome 2024.6.0 or higher](https://github.com/esphome/esphome/releases). 21 | * One half of an ethernet cable with RJ45 connector 22 | * RS232-to-TTL module (`MAX3232CSE` f.e.) 23 | * Generic ESP32 or ESP8266 board 24 | 25 | ## Schematics 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | ``` 44 | RS232 UART-TTL 45 | ┌──────────┐ ┌──────────┐ ┌─────────┐ 46 | │ │ │ │<----- RX ----->│ │ 47 | │ │<---- TX ---->│ RS232 │<----- TX ----->│ ESP32/ │ 48 | │ PIP │<---- RX ---->│ to TTL │<----- GND ---->│ ESP8266 │ 49 | │ │<---- GND --->│ module │<-- 3.3V VCC -->│ │<--- VCC 50 | │ │ │ │ │ │<--- GND 51 | └──────────┘ └──────────┘ └─────────┘ 52 | ``` 53 | 54 | ### RJ45 connector 55 | 56 | | Pin | Purpose | MAX3232 pin | Color T-568B | 57 | | :-----: | :----------- | :---------------- | :------------| 58 | | 1 | TX | P13 (RIN1) | White-Orange | 59 | | 2 | RX | P14 (DOUT1) | Orange | 60 | | 3 | | | | 61 | | 4 | VCC 12V | - | Blue | 62 | | 5 | | | | 63 | | 6 | | | | 64 | | 7 | | | | 65 | | 8 | GND | P15 (GND) | Brown | 66 | 67 | Please be aware of the different RJ45 pinout colors ([T-568A vs. T-568B](images/rj45-colors-t568a-vs-t568.png)). 68 | 69 | The inverter provides +12V on pin 4 or 7 depending on the model. You can use a cheap DC-DC converter to power the ESP with 3.3V. 70 | 71 | The [source for the pinout is here](docs/HS_MS_MSX%20RS232%20Protocol.pdf). 72 | 73 | ### MAX3232 74 | 75 | | Pin | Label | ESPHome | ESP8266 example | ESP32 example | 76 | | :----------- | :----------- | :---------- | :--------------- | :------------ | 77 | | P11 (DIN1) | TXD | `tx_pin` | `GPIO4` | `GPIO16` | 78 | | P12 (ROUT1) | RXD | `rx_pin` | `GPIO5` | `GPIO17` | 79 | | P16 (VCC) | VCC | | | | 80 | | P15 (GND) | GND | | | | 81 | 82 | ## Installation 83 | 84 | Use the `esp32-example.yaml` / `esp8266-example.yaml` as proof of concept: 85 | 86 | ```bash 87 | # Install esphome 88 | pip3 install esphome 89 | 90 | # Clone this external component 91 | git clone https://github.com/syssi/esphome-pipsolar.git 92 | cd esphome-pipsolar 93 | 94 | # Create a secret.yaml containing some setup specific secrets 95 | cat > secrets.yaml <>>`) your inverter responds (`<<<`). `NAK` is a negative response f.e. if the command isn't supported. If you have trouble to interpret the log please create an issue and provide your ESPHome log. 141 | 142 | ## References 143 | 144 | * https://github.com/esphome/esphome/pull/1664 145 | * https://github.com/esphome/esphome-docs/pull/1084/files 146 | * https://github.com/andreashergert1984/esphome/tree/feature_pipsolar_anh 147 | * https://github.com/jblance/mpp-solar/tree/master/docs/protocols 148 | -------------------------------------------------------------------------------- /docs/HS_MS_MSX RS232 Protocol.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syssi/esphome-pipsolar/da7781ccedd0a22ee2c133e5715026a2d64d6b28/docs/HS_MS_MSX RS232 Protocol.pdf -------------------------------------------------------------------------------- /esp32-example.yaml: -------------------------------------------------------------------------------- 1 | # Warning: 2 | # 3 | # If you configure a lot of the possible sensors etc. it could be that you run 4 | # out of memory (on esp8266). If you configure nearly all sensors etc. you run 5 | # in a stack-size issue. In this case you have to increase stack size! 6 | # 7 | # https://github.com/esphome/issues/issues/855 8 | 9 | substitutions: 10 | name: pipsolar 11 | tx_pin: GPIO16 12 | rx_pin: GPIO17 13 | 14 | esphome: 15 | name: ${name} 16 | min_version: 2024.6.0 17 | project: 18 | name: "syssi.esphome-pipsolar@main" 19 | version: 1.0.0 20 | 21 | esp32: 22 | board: wemos_d1_mini32 23 | framework: 24 | type: esp-idf 25 | 26 | wifi: 27 | ssid: !secret wifi_ssid 28 | password: !secret wifi_password 29 | 30 | ota: 31 | platform: esphome 32 | 33 | logger: 34 | baud_rate: 0 35 | level: DEBUG 36 | 37 | # If you use Home Assistant please remove this `mqtt` section and uncomment the `api` component! 38 | # The native API has many advantages over MQTT: https://esphome.io/components/api.html#advantages-over-mqtt 39 | mqtt: 40 | broker: !secret mqtt_host 41 | username: !secret mqtt_username 42 | password: !secret mqtt_password 43 | id: mqtt_client 44 | 45 | # api: 46 | 47 | uart: 48 | - id: uart_0 49 | baud_rate: 2400 50 | tx_pin: ${tx_pin} 51 | rx_pin: ${rx_pin} 52 | # debug: 53 | # direction: BOTH 54 | # dummy_receiver: false 55 | # after: 56 | # delimiter: "\r" 57 | # sequence: 58 | # - lambda: UARTDebug::log_string(direction, bytes); 59 | 60 | pipsolar: 61 | uart_id: uart_0 62 | id: inverter0 63 | 64 | sensor: 65 | - platform: pipsolar 66 | pipsolar_id: inverter0 67 | # QPIRI 68 | # grid_rating_voltage: 69 | # name: "${name} grid_rating_voltage" 70 | # grid_rating_current: 71 | # name: "${name} grid_rating_current" 72 | # ac_output_rating_voltage: 73 | # name: "${name} ac_output_rating_voltage" 74 | # ac_output_rating_frequency: 75 | # name: "${name} ac_output_rating_frequency" 76 | # ac_output_rating_current: 77 | # name: "${name} ac_output_rating_current" 78 | # ac_output_rating_apparent_power: 79 | # name: "${name} ac_output_rating_apparent_power" 80 | # ac_output_rating_active_power: 81 | # name: "${name} ac_output_rating_active_power" 82 | # battery_rating_voltage: 83 | # name: "${name} battery_rating_voltage" 84 | # battery_recharge_voltage: 85 | # name: "${name} battery_recharge_voltage" 86 | # battery_under_voltage: 87 | # name: "${name} battery_under_voltage" 88 | # battery_bulk_voltage: 89 | # name: "${name} battery_bulk_voltage" 90 | # battery_float_voltage: 91 | # name: "${name} battery_float_voltage" 92 | # battery_type: 93 | # name: "${name} battery_type" 94 | # current_max_ac_charging_current: 95 | # name: "${name} current_max_ac_charging_current" 96 | # current_max_charging_current: 97 | # name: "${name} current_max_charging_current" 98 | # input_voltage_range: 99 | # name: "${name} input_voltage_range" 100 | # output_source_priority: 101 | # name: "${name} output_source_priority" 102 | # charger_source_priority: 103 | # name: "${name} charger_source_priority" 104 | # parallel_max_num: 105 | # name: "${name} parallel_max_num" 106 | # machine_type: 107 | # name: "${name} machine_type" 108 | # topology: 109 | # name: "${name} topology" 110 | # output_mode: 111 | # name: "${name} output_mode" 112 | # battery_redischarge_voltage: 113 | # name: "${name} battery_redischarge_voltage" 114 | # pv_ok_condition_for_parallel: 115 | # name: "${name} pv_ok_condition_for_parallel" 116 | # pv_power_balance: 117 | # name: "${name} pv_power_balance" 118 | 119 | # QPIGS 120 | grid_voltage: 121 | name: "${name} grid_voltage" 122 | grid_frequency: 123 | name: "${name} grid_frequency" 124 | ac_output_voltage: 125 | name: "${name} ac_output_voltage" 126 | ac_output_frequency: 127 | name: "${name} ac_output_frequency" 128 | ac_output_apparent_power: 129 | name: "${name} ac_output_apparent_power" 130 | ac_output_active_power: 131 | name: "${name} ac_output_active_power" 132 | output_load_percent: 133 | name: "${name} output_load_percent" 134 | bus_voltage: 135 | name: "${name} bus_voltage" 136 | battery_voltage: 137 | name: "${name} battery_voltage" 138 | battery_charging_current: 139 | name: "${name} battery_charging_current" 140 | battery_capacity_percent: 141 | name: "${name} battery_capacity_percent" 142 | inverter_heat_sink_temperature: 143 | name: "${name} inverter_heat_sink_temperature" 144 | pv_input_current_for_battery: 145 | name: "${name} pv_input_current_for_battery" 146 | pv_input_voltage: 147 | name: "${name} pv_input_voltage" 148 | battery_voltage_scc: 149 | name: "${name} battery_voltage_scc" 150 | battery_discharge_current: 151 | name: "${name} battery_discharge_current" 152 | battery_voltage_offset_for_fans_on: 153 | name: "${name} battery_voltage_offset_for_fans_on" 154 | # eeprom_version: 155 | # name: "${name} eeprom_version" 156 | pv_charging_power: 157 | name: "${name} pv_charging_power" 158 | 159 | text_sensor: 160 | - platform: pipsolar 161 | pipsolar_id: inverter0 162 | device_mode: 163 | name: "${name} device_mode" 164 | filters: 165 | map: 166 | - P -> Power on mode 167 | - S -> Standby mode 168 | - L -> Line mode 169 | - B -> Battery mode 170 | - F -> Fault mode 171 | - D -> Shutdown mode 172 | # last_qpigs: 173 | # name: "${name} last_qpigs" 174 | # last_qpiri: 175 | # name: "${name} last_qpiri" 176 | # last_qmod: 177 | # name: "${name} last_qmod" 178 | # last_qflag: 179 | # name: "${name} last_qflag" 180 | 181 | binary_sensor: 182 | - platform: pipsolar 183 | pipsolar_id: inverter0 184 | add_sbu_priority_version: 185 | name: "${name} add_sbu_priority_version" 186 | configuration_status: 187 | name: "${name} configuration_status" 188 | # scc_firmware_version: 189 | # name: "${name} scc_firmware_version" 190 | load_status: 191 | name: "${name} load_status" 192 | battery_voltage_to_steady_while_charging: 193 | name: "${name} battery_voltage_to_steady_while_charging" 194 | charging_status: 195 | name: "${name} charging_status" 196 | scc_charging_status: 197 | name: "${name} scc_charging_status" 198 | ac_charging_status: 199 | name: "${name} ac_charging_status" 200 | charging_to_floating_mode: 201 | name: "${name} charging_to_floating_mode" 202 | switch_on: 203 | name: "${name} switch_on" 204 | # dustproof_installed: 205 | # name: "${name} dustproof_installed" 206 | silence_buzzer_open_buzzer: 207 | name: "${name} silence_buzzer_open_buzzer" 208 | overload_bypass_function: 209 | name: "${name} overload_bypass_function" 210 | lcd_escape_to_default: 211 | name: "${name} lcd_escape_to_default" 212 | overload_restart_function: 213 | name: "${name} overload_restart_function" 214 | over_temperature_restart_function: 215 | name: "${name} over_temperature_restart_function" 216 | # backlight_on: 217 | # name: "${name} backlight_on" 218 | 219 | switch: 220 | - platform: pipsolar 221 | pipsolar_id: inverter0 222 | output_source_priority_utility: 223 | name: "${name} output_source_priority_utility" 224 | output_source_priority_solar: 225 | name: "${name} output_source_priority_solar" 226 | output_source_priority_battery: 227 | name: "${name} output_source_priority_battery" 228 | input_voltage_range: 229 | name: "${name} input_voltage_range" 230 | pv_ok_condition_for_parallel: 231 | name: "${name} pv_ok_condition_for_parallel" 232 | pv_power_balance: 233 | name: "${name} pv_power_balance" 234 | 235 | output: 236 | - platform: pipsolar 237 | pipsolar_id: inverter0 238 | battery_recharge_voltage: 239 | id: inverter0_battery_recharge_voltage_out 240 | -------------------------------------------------------------------------------- /esp8266-example.yaml: -------------------------------------------------------------------------------- 1 | # Warning: 2 | # 3 | # If you configure a lot of the possible sensors etc. it could be that you run 4 | # out of memory (on esp8266). If you configure nearly all sensors etc. you run 5 | # in a stack-size issue. In this case you have to increase stack size! 6 | # 7 | # https://github.com/esphome/issues/issues/855 8 | 9 | substitutions: 10 | name: pipsolar 11 | tx_pin: GPIO4 12 | rx_pin: GPIO5 13 | 14 | esphome: 15 | name: ${name} 16 | min_version: 2024.6.0 17 | project: 18 | name: "syssi.esphome-pipsolar@main" 19 | version: 1.0.0 20 | 21 | esp8266: 22 | board: d1_mini 23 | 24 | wifi: 25 | ssid: !secret wifi_ssid 26 | password: !secret wifi_password 27 | 28 | ota: 29 | platform: esphome 30 | 31 | logger: 32 | baud_rate: 0 33 | level: DEBUG 34 | 35 | # If you use Home Assistant please remove this `mqtt` section and uncomment the `api` component! 36 | # The native API has many advantages over MQTT: https://esphome.io/components/api.html#advantages-over-mqtt 37 | mqtt: 38 | broker: !secret mqtt_host 39 | username: !secret mqtt_username 40 | password: !secret mqtt_password 41 | id: mqtt_client 42 | 43 | # api: 44 | 45 | uart: 46 | id: uart_0 47 | baud_rate: 2400 48 | tx_pin: ${tx_pin} 49 | rx_pin: ${rx_pin} 50 | # debug: 51 | # direction: BOTH 52 | # dummy_receiver: false 53 | # after: 54 | # delimiter: "\r" 55 | # sequence: 56 | # - lambda: UARTDebug::log_string(direction, bytes); 57 | 58 | pipsolar: 59 | uart_id: uart_0 60 | id: inverter0 61 | 62 | sensor: 63 | - platform: pipsolar 64 | pipsolar_id: inverter0 65 | # QPIRI 66 | # grid_rating_voltage: 67 | # name: "${name} grid_rating_voltage" 68 | # grid_rating_current: 69 | # name: "${name} grid_rating_current" 70 | # ac_output_rating_voltage: 71 | # name: "${name} ac_output_rating_voltage" 72 | # ac_output_rating_frequency: 73 | # name: "${name} ac_output_rating_frequency" 74 | # ac_output_rating_current: 75 | # name: "${name} ac_output_rating_current" 76 | # ac_output_rating_apparent_power: 77 | # name: "${name} ac_output_rating_apparent_power" 78 | # ac_output_rating_active_power: 79 | # name: "${name} ac_output_rating_active_power" 80 | # battery_rating_voltage: 81 | # name: "${name} battery_rating_voltage" 82 | # battery_recharge_voltage: 83 | # name: "${name} battery_recharge_voltage" 84 | # battery_under_voltage: 85 | # name: "${name} battery_under_voltage" 86 | # battery_bulk_voltage: 87 | # name: "${name} battery_bulk_voltage" 88 | # battery_float_voltage: 89 | # name: "${name} battery_float_voltage" 90 | # battery_type: 91 | # name: "${name} battery_type" 92 | # current_max_ac_charging_current: 93 | # name: "${name} current_max_ac_charging_current" 94 | # current_max_charging_current: 95 | # name: "${name} current_max_charging_current" 96 | # input_voltage_range: 97 | # name: "${name} input_voltage_range" 98 | # output_source_priority: 99 | # name: "${name} output_source_priority" 100 | # charger_source_priority: 101 | # name: "${name} charger_source_priority" 102 | # parallel_max_num: 103 | # name: "${name} parallel_max_num" 104 | # machine_type: 105 | # name: "${name} machine_type" 106 | # topology: 107 | # name: "${name} topology" 108 | # output_mode: 109 | # name: "${name} output_mode" 110 | # battery_redischarge_voltage: 111 | # name: "${name} battery_redischarge_voltage" 112 | # pv_ok_condition_for_parallel: 113 | # name: "${name} pv_ok_condition_for_parallel" 114 | # pv_power_balance: 115 | # name: "${name} pv_power_balance" 116 | 117 | # QPIGS 118 | grid_voltage: 119 | name: "${name} grid_voltage" 120 | grid_frequency: 121 | name: "${name} grid_frequency" 122 | ac_output_voltage: 123 | name: "${name} ac_output_voltage" 124 | ac_output_frequency: 125 | name: "${name} ac_output_frequency" 126 | ac_output_apparent_power: 127 | name: "${name} ac_output_apparent_power" 128 | ac_output_active_power: 129 | name: "${name} ac_output_active_power" 130 | output_load_percent: 131 | name: "${name} output_load_percent" 132 | bus_voltage: 133 | name: "${name} bus_voltage" 134 | battery_voltage: 135 | name: "${name} battery_voltage" 136 | battery_charging_current: 137 | name: "${name} battery_charging_current" 138 | battery_capacity_percent: 139 | name: "${name} battery_capacity_percent" 140 | inverter_heat_sink_temperature: 141 | name: "${name} inverter_heat_sink_temperature" 142 | pv_input_current_for_battery: 143 | name: "${name} pv_input_current_for_battery" 144 | pv_input_voltage: 145 | name: "${name} pv_input_voltage" 146 | battery_voltage_scc: 147 | name: "${name} battery_voltage_scc" 148 | battery_discharge_current: 149 | name: "${name} battery_discharge_current" 150 | battery_voltage_offset_for_fans_on: 151 | name: "${name} battery_voltage_offset_for_fans_on" 152 | # eeprom_version: 153 | # name: "${name} eeprom_version" 154 | pv_charging_power: 155 | name: "${name} pv_charging_power" 156 | 157 | text_sensor: 158 | - platform: pipsolar 159 | pipsolar_id: inverter0 160 | device_mode: 161 | name: "${name} device_mode" 162 | filters: 163 | map: 164 | - P -> Power on mode 165 | - S -> Standby mode 166 | - L -> Line mode 167 | - B -> Battery mode 168 | - F -> Fault mode 169 | - D -> Shutdown mode 170 | # last_qpigs: 171 | # name: "${name} last_qpigs" 172 | # last_qpiri: 173 | # name: "${name} last_qpiri" 174 | # last_qmod: 175 | # name: "${name} last_qmod" 176 | # last_qflag: 177 | # name: "${name} last_qflag" 178 | 179 | binary_sensor: 180 | - platform: pipsolar 181 | pipsolar_id: inverter0 182 | add_sbu_priority_version: 183 | name: "${name} add_sbu_priority_version" 184 | configuration_status: 185 | name: "${name} configuration_status" 186 | # scc_firmware_version: 187 | # name: "${name} scc_firmware_version" 188 | load_status: 189 | name: "${name} load_status" 190 | battery_voltage_to_steady_while_charging: 191 | name: "${name} battery_voltage_to_steady_while_charging" 192 | charging_status: 193 | name: "${name} charging_status" 194 | scc_charging_status: 195 | name: "${name} scc_charging_status" 196 | ac_charging_status: 197 | name: "${name} ac_charging_status" 198 | charging_to_floating_mode: 199 | name: "${name} charging_to_floating_mode" 200 | switch_on: 201 | name: "${name} switch_on" 202 | # dustproof_installed: 203 | # name: "${name} dustproof_installed" 204 | silence_buzzer_open_buzzer: 205 | name: "${name} silence_buzzer_open_buzzer" 206 | overload_bypass_function: 207 | name: "${name} overload_bypass_function" 208 | lcd_escape_to_default: 209 | name: "${name} lcd_escape_to_default" 210 | overload_restart_function: 211 | name: "${name} overload_restart_function" 212 | over_temperature_restart_function: 213 | name: "${name} over_temperature_restart_function" 214 | # backlight_on: 215 | # name: "${name} backlight_on" 216 | 217 | switch: 218 | - platform: pipsolar 219 | pipsolar_id: inverter0 220 | output_source_priority_utility: 221 | name: "${name} output_source_priority_utility" 222 | output_source_priority_solar: 223 | name: "${name} output_source_priority_solar" 224 | output_source_priority_battery: 225 | name: "${name} output_source_priority_battery" 226 | input_voltage_range: 227 | name: "${name} input_voltage_range" 228 | pv_ok_condition_for_parallel: 229 | name: "${name} pv_ok_condition_for_parallel" 230 | pv_power_balance: 231 | name: "${name} pv_power_balance" 232 | 233 | output: 234 | - platform: pipsolar 235 | pipsolar_id: inverter0 236 | battery_recharge_voltage: 237 | id: inverter0_battery_recharge_voltage_out 238 | -------------------------------------------------------------------------------- /images/001.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syssi/esphome-pipsolar/da7781ccedd0a22ee2c133e5715026a2d64d6b28/images/001.jpg -------------------------------------------------------------------------------- /images/002.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syssi/esphome-pipsolar/da7781ccedd0a22ee2c133e5715026a2d64d6b28/images/002.jpg -------------------------------------------------------------------------------- /images/003.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syssi/esphome-pipsolar/da7781ccedd0a22ee2c133e5715026a2d64d6b28/images/003.jpg -------------------------------------------------------------------------------- /images/004.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syssi/esphome-pipsolar/da7781ccedd0a22ee2c133e5715026a2d64d6b28/images/004.jpg -------------------------------------------------------------------------------- /images/005.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syssi/esphome-pipsolar/da7781ccedd0a22ee2c133e5715026a2d64d6b28/images/005.jpg -------------------------------------------------------------------------------- /images/rj45-colors-t568a-vs-t568.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syssi/esphome-pipsolar/da7781ccedd0a22ee2c133e5715026a2d64d6b28/images/rj45-colors-t568a-vs-t568.png -------------------------------------------------------------------------------- /tests/esp8266-test-ping.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: pipsolar 3 | tx_pin: GPIO4 4 | rx_pin: GPIO5 5 | 6 | esphome: 7 | name: ${name} 8 | min_version: 2024.6.0 9 | 10 | esp8266: 11 | board: d1_mini 12 | 13 | wifi: 14 | ssid: !secret wifi_ssid 15 | password: !secret wifi_password 16 | 17 | ota: 18 | platform: esphome 19 | 20 | logger: 21 | level: DEBUG 22 | 23 | api: 24 | reboot_timeout: 0s 25 | 26 | uart: 27 | id: uart_0 28 | baud_rate: 2400 29 | tx_pin: ${tx_pin} 30 | rx_pin: ${rx_pin} 31 | debug: 32 | direction: BOTH 33 | dummy_receiver: true 34 | after: 35 | delimiter: "\r" 36 | sequence: 37 | - lambda: UARTDebug::log_string(direction, bytes); 38 | 39 | interval: 40 | - interval: 2s 41 | then: 42 | - uart.write: "ping\r" 43 | -------------------------------------------------------------------------------- /tests/esp8266-test-pong.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: pipsolar 3 | tx_pin: GPIO4 4 | rx_pin: GPIO5 5 | 6 | esphome: 7 | name: ${name} 8 | min_version: 2024.6.0 9 | 10 | esp8266: 11 | board: d1_mini 12 | 13 | wifi: 14 | ssid: !secret wifi_ssid 15 | password: !secret wifi_password 16 | 17 | ota: 18 | platform: esphome 19 | 20 | logger: 21 | level: DEBUG 22 | 23 | api: 24 | reboot_timeout: 0s 25 | 26 | uart: 27 | id: uart_0 28 | baud_rate: 2400 29 | tx_pin: ${tx_pin} 30 | rx_pin: ${rx_pin} 31 | debug: 32 | direction: BOTH 33 | dummy_receiver: true 34 | after: 35 | delimiter: "\r" 36 | sequence: 37 | - lambda: UARTDebug::log_string(direction, bytes); 38 | 39 | interval: 40 | - interval: 2s 41 | then: 42 | - uart.write: "pong\r" 43 | -------------------------------------------------------------------------------- /tests/esp8266-test-protocols.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: pipsolar 3 | tx_pin: GPIO4 4 | rx_pin: GPIO5 5 | 6 | esphome: 7 | name: ${name} 8 | min_version: 2024.6.0 9 | 10 | esp8266: 11 | board: d1_mini 12 | 13 | wifi: 14 | ssid: !secret wifi_ssid 15 | password: !secret wifi_password 16 | 17 | ota: 18 | platform: esphome 19 | 20 | logger: 21 | level: DEBUG 22 | 23 | api: 24 | reboot_timeout: 0s 25 | 26 | uart: 27 | id: uart_0 28 | baud_rate: 2400 29 | tx_pin: ${tx_pin} 30 | rx_pin: ${rx_pin} 31 | debug: 32 | direction: BOTH 33 | dummy_receiver: true 34 | after: 35 | delimiter: "\r" 36 | sequence: 37 | - lambda: UARTDebug::log_string(direction, bytes); 38 | 39 | interval: 40 | - interval: 80s 41 | then: 42 | # PI30/PI30MAX/PI30REVO/PI41 43 | - logger.log: 44 | level: INFO 45 | format: "Testing PI30/PI30MAX/PI30REVO/PI41 commands..." 46 | - logger.log: 47 | level: INFO 48 | format: "This is the set of commands supported by the pipsolar component!" 49 | - uart.write: [0x51, 0x50, 0x49, 0xBE, 0xAC, 0x0D] # QPI\xbe\xac\r 50 | - delay: 1s 51 | - uart.write: [0x51, 0x44, 0x49, 0x71, 0x1B, 0x0D] # QDIq\x1b\r 52 | - delay: 1s 53 | - uart.write: [0x51, 0x46, 0x4C, 0x41, 0x47, 0x98, 0x74, 0x0D] # QFLAG\x98t\r 54 | - delay: 1s 55 | - uart.write: [0x51, 0x4D, 0x4E, 0xBB, 0x64, 0x0D] # QMN\xbbd\r 56 | - delay: 1s 57 | - uart.write: [0x51, 0x4D, 0x4F, 0x44, 0x49, 0xC1, 0x0D] # QMODI\xc1\r 58 | - delay: 1s 59 | - uart.write: [0x51, 0x50, 0x49, 0x47, 0x53, 0xB7, 0xA9, 0x0D] # QPIGS\xB7\xA9\r 60 | - delay: 1s 61 | - uart.write: [0x51, 0x50, 0x49, 0x52, 0x49, 0xF8, 0x54, 0x0D] # QPIRI\xF8T\r 62 | - delay: 1s 63 | - uart.write: [0x51, 0x50, 0x49, 0x57, 0x53, 0xB4, 0xDA, 0x0D] # QPIWS\xb4\xda\r 64 | - delay: 1s 65 | - uart.write: [0x51, 0x54, 0x27, 0xFF, 0x0D] # QT'\xff\r 66 | - delay: 1s 67 | # Test some PI30 commands from Phocos Any-Grid PSW-H inverters 68 | - uart.write: [0x51, 0x50, 0x47, 0x53, 0x30, 0x3F, 0xDA, 0x0D] # QPGS0\r 69 | - delay: 1s 70 | - uart.write: [0x51, 0x50, 0x47, 0x53, 0x31, 0x2F, 0xFB, 0x0D] # QPGS1\r 71 | - delay: 1s 72 | - uart.write: [0x51, 0x50, 0x47, 0x53, 0x32, 0x1F, 0x98, 0x0D] # QPGS2\r 73 | - delay: 1s 74 | 75 | # PI41 split phase 76 | - logger.log: 77 | level: INFO 78 | format: "Testing PI41 split phase / multiple strings commands..." 79 | - uart.write: [0x51, 0x50, 0x49, 0x47, 0x53, 0x32, 0x68, 0x2D, 0x0D] # QPIGS2h-\r 80 | - delay: 1s 81 | - uart.write: [0x51, 0x50, 0x32, 0x47, 0x53, 0x30, 0x14, 0x05, 0x0D] # QP2GS0\x14\x05\r 82 | - delay: 1s 83 | - uart.write: [0x51, 0x50, 0x32, 0x47, 0x53, 0x31, 0x04, 0x24, 0x0D] # QP2GS1\x04$\r 84 | - delay: 1s 85 | 86 | # PI18 87 | - logger.log: 88 | level: INFO 89 | format: "Testing unsupported PI18 commands..." 90 | - uart.write: [0x5E, 0x50, 0x30, 0x30, 0x35, 0x50, 0x49, 0x71, 0x8B, 0x0D] # ^P005PIq\x8b\r 91 | - delay: 1s 92 | - uart.write: [0x5E, 0x50, 0x30, 0x30, 0x35, 0x47, 0x53, 0x58, 0x14, 0x0D] # ^P005GSX\x14\r 93 | - delay: 1s 94 | - uart.write: [0x5E, 0x50, 0x30, 0x30, 0x36, 0x4D, 0x4F, 0x44, 0xDD, 0xBE, 0x0D] # ^P006MOD\xdd\xbe\r 95 | - delay: 1s 96 | 97 | # PI17 98 | - logger.log: 99 | level: INFO 100 | format: "Testing unsupported PI17 commands..." 101 | - uart.write: [0x5E, 0x50, 0x30, 0x30, 0x33, 0x50, 0x49, 0x0D] # ^P003PI\r 102 | - delay: 1s 103 | - uart.write: [0x5E, 0x50, 0x30, 0x30, 0x34, 0x4D, 0x4F, 0x44, 0x0D] # ^P004MOD\r 104 | - delay: 1s 105 | - uart.write: [0x5E, 0x50, 0x30, 0x30, 0x35, 0x46, 0x4C, 0x41, 0x47, 0x0D] # ^P005FLAG\r 106 | - delay: 1s 107 | 108 | # PI16 109 | - logger.log: 110 | level: INFO 111 | format: "Testing unsupported PI16 commands..." 112 | - uart.write: [0x51, 0x50, 0x49, 0x0D] # QPI\r 113 | - delay: 1s 114 | - uart.write: [0x51, 0x4D, 0x4F, 0x44, 0x0D] # QMOD\r 115 | - delay: 1s 116 | - uart.write: [0x51, 0x50, 0x49, 0x47, 0x53, 0x0D] # QPIGS\r 117 | - delay: 1s 118 | - uart.write: [0x51, 0x50, 0x49, 0x52, 0x49, 0x0D] # QPIRI\r 119 | - delay: 1s 120 | - uart.write: [0x51, 0x4D, 0x4F, 0x44, 0x0D] # QMOD\r 121 | 122 | # Qx 123 | - logger.log: 124 | level: INFO 125 | format: "Testing unsupported Qx commands..." 126 | - uart.write: [0x51, 0x31, 0x0D] # Q1\r 127 | - delay: 1s 128 | - uart.write: [0x51, 0x32, 0x0D] # Q2\r 129 | - delay: 1s 130 | - uart.write: [0x51, 0x34, 0x0D] # Q4\r 131 | - delay: 1s 132 | - uart.write: [0x51, 0x36, 0x0D] # Q6\r 133 | - delay: 1s 134 | 135 | - logger.log: 136 | level: INFO 137 | format: "Done. Repeating..." 138 | -------------------------------------------------------------------------------- /tests/esp8266-uart-sniffer.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: uart-sniffer 3 | rx_pin: GPIO5 4 | rx_pin2: GPIO4 5 | 6 | esphome: 7 | name: ${name} 8 | min_version: 2024.6.0 9 | 10 | esp8266: 11 | board: d1_mini 12 | 13 | wifi: 14 | ssid: !secret wifi_ssid 15 | password: !secret wifi_password 16 | 17 | ota: 18 | platform: esphome 19 | 20 | logger: 21 | level: DEBUG 22 | 23 | api: 24 | reboot_timeout: 0s 25 | 26 | uart: 27 | - id: uart_0 28 | baud_rate: 2400 29 | rx_pin: ${rx_pin} 30 | debug: 31 | direction: RX 32 | dummy_receiver: true 33 | sequence: 34 | - lambda: UARTDebug::log_hex(esphome::uart::UARTDirection::UART_DIRECTION_RX, bytes, ':'); 35 | 36 | - id: uart_1 37 | baud_rate: 2400 38 | rx_pin: ${rx_pin2} 39 | debug: 40 | direction: RX 41 | dummy_receiver: true 42 | sequence: 43 | - lambda: UARTDebug::log_hex(esphome::uart::UARTDirection::UART_DIRECTION_TX, bytes, ':'); 44 | --------------------------------------------------------------------------------