├── .github
├── FUNDING copy.yml
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.yaml
│ ├── config.yml
│ ├── feature_request.yaml
│ ├── help.yaml
│ └── working_device.yaml
└── workflows
│ ├── ISSUE_WORKFLOW.yaml
│ └── update_central_repo.yml
├── .gitignore
├── .vscode
└── extensions.json
├── LICENSE
├── Protocol
├── BlueSolar-HEX-protocol.pdf
├── VE.Direct-Protocol-3.33.pdf
├── vicASCII.log
├── vicHEX.log
└── viread.log
├── README.md
├── include
└── readme.md
├── notes
├── platformio.ini
├── src
├── RTCMemory.h
├── Settings.h
├── VeDirectDataList.h
├── VeDirectDeviceCodes.h
├── VeDirectDeviceList.h
├── VeDirectFrameHandler.cpp
├── VeDirectFrameHandler.h
├── htmlProzessor.h
├── main.cpp
├── main.h
├── status-LED.h
└── webpages
│ ├── HTML_CONFIRM_RESET.html
│ ├── HTML_FOOT.html
│ ├── HTML_HEAD.html
│ ├── HTML_MAIN.html
│ ├── HTML_REBOOT.html
│ ├── HTML_SETTINGS.html
│ └── HTML_SETTINGS_EDIT.html
└── tools
├── mini_html.py
├── post_compile.py
└── pre_compile.py
/.github/FUNDING copy.yml:
--------------------------------------------------------------------------------
1 | custom: ['https://donate.softwarecrash.de']
2 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | custom: ['https://donate.softwarecrash.de']
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yaml:
--------------------------------------------------------------------------------
1 | name: 🐞 Bug Report
2 | description: If something isn't working as expected.
3 | title: "[Bug]: "
4 | labels: ["bug", "triage"]
5 | body:
6 | - type: checkboxes
7 | attributes:
8 | label: Is there an existing issue for this?
9 | description: Please search to see if an issue already exists for the bug you encountered.
10 | options:
11 | - label: I have searched the existing issues
12 | required: true
13 | - type: dropdown
14 | attributes:
15 | label: Used Hardware?
16 | description: Please let us know what hardware you are using.
17 | multiple: true
18 | options:
19 | - Wemos D1 Mini
20 | - ESP-01 (512KB Flash)
21 | - ESP-01S (1MB Flash)
22 | - Other (please write your hardware in 'What happened?')
23 | validations:
24 | required: true
25 | - type: textarea
26 | id: what-happened
27 | attributes:
28 | label: What happened?
29 | description: Also tell us, what did you expect to happen?
30 | placeholder: Tell us what you see!
31 | validations:
32 | required: true
33 | - type: textarea
34 | id: screenhots
35 | attributes:
36 | label: Screenshots / Fotos
37 | description: Add screenshots and fotos of your wiring to help explain your problem.
38 | placeholder: Drag&Drop screenshots and Fotos here
39 | validations:
40 | required: true
41 | - type: textarea
42 | attributes:
43 | label: Steps To Reproduce
44 | description: If applicable, steps to reproduce the behavior.
45 | placeholder: |
46 | 1. Go to...
47 | 2. Click on...
48 | 3. See error...
49 | validations:
50 | required: false
51 | - type: dropdown
52 | id: version
53 | attributes:
54 | label: Version
55 | description: What version of our software are you running?
56 | multiple: true
57 | options:
58 | - 1.x.x and above
59 | - 0.2.4
60 | - 0.2.3
61 | - 0.2.2
62 | - 0.2.1
63 | - 0.2.0
64 | - 0.1.x (Depreciated)
65 | validations:
66 | required: true
67 | - type: textarea
68 | id: output
69 | attributes:
70 | label: Relevant livejson output
71 | description: Please copy and paste your livejson-output (http://IP_of_your_ESP/livejson). This will be automatically formatted into code, so no need for backticks.
72 | render: json
73 | validations:
74 | required: true
75 |
76 | - type: dropdown
77 | id: browsers
78 | attributes:
79 | label: What browsers are you seeing the problem on?
80 | multiple: true
81 | options:
82 | - Firefox
83 | - Chrome
84 | - Safari
85 | - Microsoft Edge
86 | - no Issue with the Browser or WebUI
87 | validations:
88 | required: true
89 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.yaml:
--------------------------------------------------------------------------------
1 | name: 🚀 Feature Request
2 | description: I have a suggestion (and may want to implement it 🙂)!
3 | title: "[FEATURE]
"
4 | body:
5 | - type: textarea
6 | attributes:
7 | label: Is your feature request related to a problem? Please describe
8 | description: A clear and concise description of what the problem is.
9 | validations:
10 | required: true
11 | - type: textarea
12 | attributes:
13 | label: Describe the solution you'd like
14 | description: A clear and concise description of what you want to happen.
15 | validations:
16 | required: true
17 | - type: textarea
18 | attributes:
19 | label: Describe alternatives you've considered
20 | description: A clear and concise description of any alternative solutions or features you've considered.
21 | validations:
22 | required: true
23 | - type: textarea
24 | attributes:
25 | label: Additional context
26 | description: Add any other context or screenshots about the feature request here.
27 | validations:
28 | required: false
29 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/help.yaml:
--------------------------------------------------------------------------------
1 | name: 😭 Help!
2 | description: Ask for help
3 | title: "[HELP] "
4 | labels: help wanted
5 | body:
6 | - type: textarea
7 | attributes:
8 | label: A clear and concise description of what the problem is.
9 | description: A clear and concise description of what the problem is.
10 | validations:
11 | required: true
12 | - type: textarea
13 | attributes:
14 | label: Additional context
15 | description: Add any other context or screenshots about the feature request here.
16 | validations:
17 | required: false
18 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/working_device.yaml:
--------------------------------------------------------------------------------
1 | name: Working device
2 | description: Report a new working device
3 | title: New working device
4 | labels: ["working device"]
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | Thanks for taking the time to fill out this report!
10 | - type: textarea
11 | id: device-type
12 | attributes:
13 | label: What device are you using?
14 | description: Also tell us, wich firmware version you are using?
15 | placeholder: Tell us what you are using!
16 | value: "A new working device!"
17 | validations:
18 | required: true
19 |
--------------------------------------------------------------------------------
/.github/workflows/ISSUE_WORKFLOW.yaml:
--------------------------------------------------------------------------------
1 | name: Close inactive issues
2 | on:
3 | schedule:
4 | - cron: "30 1 * * *"
5 |
6 | jobs:
7 | close-issues:
8 | runs-on: ubuntu-latest
9 | permissions:
10 | issues: write
11 | pull-requests: write
12 | steps:
13 | - uses: actions/stale@v5
14 | with:
15 | days-before-issue-stale: 30
16 | days-before-issue-close: 14
17 | stale-issue-label: "stale"
18 | stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
19 | close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
20 | days-before-pr-stale: -1
21 | days-before-pr-close: -1
22 | repo-token: ${{ secrets.GITHUB_TOKEN }}
23 |
--------------------------------------------------------------------------------
/.github/workflows/update_central_repo.yml:
--------------------------------------------------------------------------------
1 | name: Update Central Firmware Repository
2 |
3 | on:
4 | release:
5 | types: [published, edited]
6 | workflow_dispatch:
7 |
8 | jobs:
9 | update-central-repo:
10 | if: ${{ github.event.release.prerelease == false || github.event_name == 'workflow_dispatch' }}
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Checkout Repository
14 | uses: actions/checkout@v3
15 |
16 | - name: Set up Git
17 | run: |
18 | git config --global user.name 'GitHub Actions'
19 | git config --global user.email 'actions@github.com'
20 |
21 | - name: Clone Central Repository
22 | run: |
23 | git clone https://all-solutions:${{ secrets.CENTRAL_REPO_TOKEN }}@github.com/all-solutions/Flash2MQTT.git Flash2MQTT
24 |
25 | - name: Download Firmware Assets
26 | id: fetch-release
27 | uses: actions/github-script@v6
28 | with:
29 | github-token: ${{ secrets.GITHUB_TOKEN }}
30 | script: |
31 | const fs = require('fs');
32 | const owner = context.repo.owner;
33 | const repo = context.repo.repo;
34 |
35 | let release;
36 | let releaseTag;
37 |
38 | if (context.eventName === 'release') {
39 | releaseTag = context.payload.release.tag_name;
40 | console.log(`Using release tag from event: ${releaseTag}`);
41 | release = await github.rest.repos.getReleaseByTag({ owner, repo, tag: releaseTag });
42 | } else {
43 | release = await github.rest.repos.getLatestRelease({ owner, repo });
44 | releaseTag = release.data.tag_name;
45 | console.log(`Using latest release tag: ${releaseTag}`);
46 | }
47 |
48 | const assets = release.data.assets.filter(asset => asset.name.endsWith('.bin'));
49 |
50 | if (assets.length === 0) {
51 | core.setFailed('No .bin assets found in the release.');
52 | return;
53 | }
54 |
55 | for (const asset of assets) {
56 | const download = await github.rest.repos.getReleaseAsset({
57 | owner,
58 | repo,
59 | asset_id: asset.id,
60 | headers: {
61 | Accept: 'application/octet-stream',
62 | },
63 | });
64 | fs.writeFileSync(asset.name, Buffer.from(download.data));
65 | console.log(`Downloaded ${asset.name}`);
66 | }
67 |
68 | core.setOutput('tag', releaseTag);
69 |
70 | - name: List Downloaded Files
71 | run: ls -la
72 |
73 | - name: Copy Firmware Files
74 | run: |
75 | mkdir -p Flash2MQTT/firmware/${{ github.event.repository.name }}
76 | rm -f Flash2MQTT/firmware/${{ github.event.repository.name }}/*.bin
77 | cp *.bin Flash2MQTT/firmware/${{ github.event.repository.name }}/
78 | echo "Updated at $(date -u)" > Flash2MQTT/firmware/${{ github.event.repository.name }}/last_update.txt
79 |
80 | - name: Install jq
81 | run: sudo apt-get update && sudo apt-get install -y jq
82 |
83 | - name: Update variants.json and firmware_list.json
84 | env:
85 | FIRMWARE_NAME: ${{ github.event.repository.name }}
86 | RELEASE_VERSION: ${{ steps.fetch-release.outputs.tag }}
87 | run: |
88 | cd Flash2MQTT/firmware/${FIRMWARE_NAME}
89 | ls *.bin > bin_files.txt
90 | total=0
91 | count=0
92 | version="${RELEASE_VERSION#v}"
93 | version="${version#V}"
94 | echo "Firmware Name: $FIRMWARE_NAME"
95 | echo "Release Version: $version"
96 |
97 | echo "Determining total number of desired variants..."
98 | while read file; do
99 | if [[ "$file" == *"_${version}.bin" ]]; then
100 | variant_part=$(echo "$file" | sed -E 's/^'"$FIRMWARE_NAME"'_//; s/_'"${version}"'\.bin$//')
101 | variant_name="${variant_part}"
102 | if [[ "$variant_name" == "d1_mini" || "$variant_name" == "esp01_1m" ]]; then
103 | total=$((total + 1))
104 | fi
105 | fi
106 | done < bin_files.txt
107 |
108 | echo '[' > variants.json
109 | while read file; do
110 | if [[ "$file" == *"_${version}.bin" ]]; then
111 | variant_part=$(echo "$file" | sed -E 's/^'"$FIRMWARE_NAME"'_//; s/_'"${version}"'\.bin$//')
112 | variant_name="${variant_part}"
113 | case "$variant_name" in
114 | "d1_mini") display_name="D1 Mini" ;;
115 | "esp01_1m") display_name="ESP-01" ;;
116 | *) continue ;;
117 | esac
118 | count=$((count + 1))
119 | echo ' {' >> variants.json
120 | echo ' "displayName": "'"$display_name"'",' >> variants.json
121 | echo ' "file": "https://all-solutions.github.io/Flash2MQTT/firmware/'"$FIRMWARE_NAME"'/'"$file"'"' >> variants.json
122 | if [ $count -lt $total ]; then
123 | echo ' },' >> variants.json
124 | else
125 | echo ' }' >> variants.json
126 | fi
127 | fi
128 | done < bin_files.txt
129 | echo ']' >> variants.json
130 | rm bin_files.txt
131 |
132 | cd ..
133 | if [ ! -f firmware_list.json ]; then
134 | echo '[]' > firmware_list.json
135 | fi
136 | tmpfile=$(mktemp)
137 | jq --arg name "$FIRMWARE_NAME" --arg version "$version" \
138 | 'if any(.[]; .name == $name) then map(if .name == $name then .version = $version else . end) else . + [{"name": $name, "version": $version}] end' \
139 | firmware_list.json > "$tmpfile" && mv "$tmpfile" firmware_list.json
140 |
141 | - name: Commit and Push Changes
142 | env:
143 | RELEASE_VERSION: ${{ steps.fetch-release.outputs.tag }}
144 | run: |
145 | cd Flash2MQTT
146 | git add firmware/${{ github.event.repository.name }}
147 | git add firmware/firmware_list.json
148 | git commit -m "Update firmware for ${{ github.event.repository.name }} to version $RELEASE_VERSION" || \
149 | git commit --allow-empty -m "Force update for ${{ github.event.repository.name }} to version $RELEASE_VERSION (no file changes)"
150 | git pull --rebase origin main
151 | git push https://all-solutions:${{ secrets.CENTRAL_REPO_TOKEN }}@github.com/all-solutions/Flash2MQTT.git HEAD:main
152 |
153 |
154 |
155 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .pio
2 | .vscode/.browse.c_cpp.db*
3 | .vscode/c_cpp_properties.json
4 | .vscode/launch.json
5 | .vscode/ipch
6 | .vscode
7 | .firmware
8 | src/html.h
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // See http://go.microsoft.com/fwlink/?LinkId=827846
3 | // for the documentation about the extensions.json format
4 | "recommendations": [
5 | "platformio.platformio-ide"
6 | ],
7 | "unwantedRecommendations": [
8 | "ms-vscode.cpptools-extension-pack"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Attribution-NonCommercial-NoDerivatives 4.0 International
2 |
3 | =======================================================================
4 |
5 | Creative Commons Corporation ("Creative Commons") is not a law firm and
6 | does not provide legal services or legal advice. Distribution of
7 | Creative Commons public licenses does not create a lawyer-client or
8 | other relationship. Creative Commons makes its licenses and related
9 | information available on an "as-is" basis. Creative Commons gives no
10 | warranties regarding its licenses, any material licensed under their
11 | terms and conditions, or any related information. Creative Commons
12 | disclaims all liability for damages resulting from their use to the
13 | fullest extent possible.
14 |
15 | Using Creative Commons Public Licenses
16 |
17 | Creative Commons public licenses provide a standard set of terms and
18 | conditions that creators and other rights holders may use to share
19 | original works of authorship and other material subject to copyright
20 | and certain other rights specified in the public license below. The
21 | following considerations are for informational purposes only, are not
22 | exhaustive, and do not form part of our licenses.
23 |
24 | Considerations for licensors: Our public licenses are
25 | intended for use by those authorized to give the public
26 | permission to use material in ways otherwise restricted by
27 | copyright and certain other rights. Our licenses are
28 | irrevocable. Licensors should read and understand the terms
29 | and conditions of the license they choose before applying it.
30 | Licensors should also secure all rights necessary before
31 | applying our licenses so that the public can reuse the
32 | material as expected. Licensors should clearly mark any
33 | material not subject to the license. This includes other CC-
34 | licensed material, or material used under an exception or
35 | limitation to copyright. More considerations for licensors:
36 | wiki.creativecommons.org/Considerations_for_licensors
37 |
38 | Considerations for the public: By using one of our public
39 | licenses, a licensor grants the public permission to use the
40 | licensed material under specified terms and conditions. If
41 | the licensor's permission is not necessary for any reason--for
42 | example, because of any applicable exception or limitation to
43 | copyright--then that use is not regulated by the license. Our
44 | licenses grant only permissions under copyright and certain
45 | other rights that a licensor has authority to grant. Use of
46 | the licensed material may still be restricted for other
47 | reasons, including because others have copyright or other
48 | rights in the material. A licensor may make special requests,
49 | such as asking that all changes be marked or described.
50 | Although not required by our licenses, you are encouraged to
51 | respect those requests where reasonable. More considerations
52 | for the public:
53 | wiki.creativecommons.org/Considerations_for_licensees
54 |
55 | =======================================================================
56 |
57 | Creative Commons Attribution-NonCommercial-NoDerivatives 4.0
58 | International Public License
59 |
60 | By exercising the Licensed Rights (defined below), You accept and agree
61 | to be bound by the terms and conditions of this Creative Commons
62 | Attribution-NonCommercial-NoDerivatives 4.0 International Public
63 | License ("Public License"). To the extent this Public License may be
64 | interpreted as a contract, You are granted the Licensed Rights in
65 | consideration of Your acceptance of these terms and conditions, and the
66 | Licensor grants You such rights in consideration of benefits the
67 | Licensor receives from making the Licensed Material available under
68 | these terms and conditions.
69 |
70 |
71 | Section 1 -- Definitions.
72 |
73 | a. Adapted Material means material subject to Copyright and Similar
74 | Rights that is derived from or based upon the Licensed Material
75 | and in which the Licensed Material is translated, altered,
76 | arranged, transformed, or otherwise modified in a manner requiring
77 | permission under the Copyright and Similar Rights held by the
78 | Licensor. For purposes of this Public License, where the Licensed
79 | Material is a musical work, performance, or sound recording,
80 | Adapted Material is always produced where the Licensed Material is
81 | synched in timed relation with a moving image.
82 |
83 | b. Copyright and Similar Rights means copyright and/or similar rights
84 | closely related to copyright including, without limitation,
85 | performance, broadcast, sound recording, and Sui Generis Database
86 | Rights, without regard to how the rights are labeled or
87 | categorized. For purposes of this Public License, the rights
88 | specified in Section 2(b)(1)-(2) are not Copyright and Similar
89 | Rights.
90 |
91 | c. Effective Technological Measures means those measures that, in the
92 | absence of proper authority, may not be circumvented under laws
93 | fulfilling obligations under Article 11 of the WIPO Copyright
94 | Treaty adopted on December 20, 1996, and/or similar international
95 | agreements.
96 |
97 | d. Exceptions and Limitations means fair use, fair dealing, and/or
98 | any other exception or limitation to Copyright and Similar Rights
99 | that applies to Your use of the Licensed Material.
100 |
101 | e. Licensed Material means the artistic or literary work, database,
102 | or other material to which the Licensor applied this Public
103 | License.
104 |
105 | f. Licensed Rights means the rights granted to You subject to the
106 | terms and conditions of this Public License, which are limited to
107 | all Copyright and Similar Rights that apply to Your use of the
108 | Licensed Material and that the Licensor has authority to license.
109 |
110 | g. Licensor means the individual(s) or entity(ies) granting rights
111 | under this Public License.
112 |
113 | h. NonCommercial means not primarily intended for or directed towards
114 | commercial advantage or monetary compensation. For purposes of
115 | this Public License, the exchange of the Licensed Material for
116 | other material subject to Copyright and Similar Rights by digital
117 | file-sharing or similar means is NonCommercial provided there is
118 | no payment of monetary compensation in connection with the
119 | exchange.
120 |
121 | i. Share means to provide material to the public by any means or
122 | process that requires permission under the Licensed Rights, such
123 | as reproduction, public display, public performance, distribution,
124 | dissemination, communication, or importation, and to make material
125 | available to the public including in ways that members of the
126 | public may access the material from a place and at a time
127 | individually chosen by them.
128 |
129 | j. Sui Generis Database Rights means rights other than copyright
130 | resulting from Directive 96/9/EC of the European Parliament and of
131 | the Council of 11 March 1996 on the legal protection of databases,
132 | as amended and/or succeeded, as well as other essentially
133 | equivalent rights anywhere in the world.
134 |
135 | k. You means the individual or entity exercising the Licensed Rights
136 | under this Public License. Your has a corresponding meaning.
137 |
138 |
139 | Section 2 -- Scope.
140 |
141 | a. License grant.
142 |
143 | 1. Subject to the terms and conditions of this Public License,
144 | the Licensor hereby grants You a worldwide, royalty-free,
145 | non-sublicensable, non-exclusive, irrevocable license to
146 | exercise the Licensed Rights in the Licensed Material to:
147 |
148 | a. reproduce and Share the Licensed Material, in whole or
149 | in part, for NonCommercial purposes only; and
150 |
151 | b. produce and reproduce, but not Share, Adapted Material
152 | for NonCommercial purposes only.
153 |
154 | 2. Exceptions and Limitations. For the avoidance of doubt, where
155 | Exceptions and Limitations apply to Your use, this Public
156 | License does not apply, and You do not need to comply with
157 | its terms and conditions.
158 |
159 | 3. Term. The term of this Public License is specified in Section
160 | 6(a).
161 |
162 | 4. Media and formats; technical modifications allowed. The
163 | Licensor authorizes You to exercise the Licensed Rights in
164 | all media and formats whether now known or hereafter created,
165 | and to make technical modifications necessary to do so. The
166 | Licensor waives and/or agrees not to assert any right or
167 | authority to forbid You from making technical modifications
168 | necessary to exercise the Licensed Rights, including
169 | technical modifications necessary to circumvent Effective
170 | Technological Measures. For purposes of this Public License,
171 | simply making modifications authorized by this Section 2(a)
172 | (4) never produces Adapted Material.
173 |
174 | 5. Downstream recipients.
175 |
176 | a. Offer from the Licensor -- Licensed Material. Every
177 | recipient of the Licensed Material automatically
178 | receives an offer from the Licensor to exercise the
179 | Licensed Rights under the terms and conditions of this
180 | Public License.
181 |
182 | b. No downstream restrictions. You may not offer or impose
183 | any additional or different terms or conditions on, or
184 | apply any Effective Technological Measures to, the
185 | Licensed Material if doing so restricts exercise of the
186 | Licensed Rights by any recipient of the Licensed
187 | Material.
188 |
189 | 6. No endorsement. Nothing in this Public License constitutes or
190 | may be construed as permission to assert or imply that You
191 | are, or that Your use of the Licensed Material is, connected
192 | with, or sponsored, endorsed, or granted official status by,
193 | the Licensor or others designated to receive attribution as
194 | provided in Section 3(a)(1)(A)(i).
195 |
196 | b. Other rights.
197 |
198 | 1. Moral rights, such as the right of integrity, are not
199 | licensed under this Public License, nor are publicity,
200 | privacy, and/or other similar personality rights; however, to
201 | the extent possible, the Licensor waives and/or agrees not to
202 | assert any such rights held by the Licensor to the limited
203 | extent necessary to allow You to exercise the Licensed
204 | Rights, but not otherwise.
205 |
206 | 2. Patent and trademark rights are not licensed under this
207 | Public License.
208 |
209 | 3. To the extent possible, the Licensor waives any right to
210 | collect royalties from You for the exercise of the Licensed
211 | Rights, whether directly or through a collecting society
212 | under any voluntary or waivable statutory or compulsory
213 | licensing scheme. In all other cases the Licensor expressly
214 | reserves any right to collect such royalties, including when
215 | the Licensed Material is used other than for NonCommercial
216 | purposes.
217 |
218 |
219 | Section 3 -- License Conditions.
220 |
221 | Your exercise of the Licensed Rights is expressly made subject to the
222 | following conditions.
223 |
224 | a. Attribution.
225 |
226 | 1. If You Share the Licensed Material, You must:
227 |
228 | a. retain the following if it is supplied by the Licensor
229 | with the Licensed Material:
230 |
231 | i. identification of the creator(s) of the Licensed
232 | Material and any others designated to receive
233 | attribution, in any reasonable manner requested by
234 | the Licensor (including by pseudonym if
235 | designated);
236 |
237 | ii. a copyright notice;
238 |
239 | iii. a notice that refers to this Public License;
240 |
241 | iv. a notice that refers to the disclaimer of
242 | warranties;
243 |
244 | v. a URI or hyperlink to the Licensed Material to the
245 | extent reasonably practicable;
246 |
247 | b. indicate if You modified the Licensed Material and
248 | retain an indication of any previous modifications; and
249 |
250 | c. indicate the Licensed Material is licensed under this
251 | Public License, and include the text of, or the URI or
252 | hyperlink to, this Public License.
253 |
254 | For the avoidance of doubt, You do not have permission under
255 | this Public License to Share Adapted Material.
256 |
257 | 2. You may satisfy the conditions in Section 3(a)(1) in any
258 | reasonable manner based on the medium, means, and context in
259 | which You Share the Licensed Material. For example, it may be
260 | reasonable to satisfy the conditions by providing a URI or
261 | hyperlink to a resource that includes the required
262 | information.
263 |
264 | 3. If requested by the Licensor, You must remove any of the
265 | information required by Section 3(a)(1)(A) to the extent
266 | reasonably practicable.
267 |
268 |
269 | Section 4 -- Sui Generis Database Rights.
270 |
271 | Where the Licensed Rights include Sui Generis Database Rights that
272 | apply to Your use of the Licensed Material:
273 |
274 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right
275 | to extract, reuse, reproduce, and Share all or a substantial
276 | portion of the contents of the database for NonCommercial purposes
277 | only and provided You do not Share Adapted Material;
278 |
279 | b. if You include all or a substantial portion of the database
280 | contents in a database in which You have Sui Generis Database
281 | Rights, then the database in which You have Sui Generis Database
282 | Rights (but not its individual contents) is Adapted Material; and
283 |
284 | c. You must comply with the conditions in Section 3(a) if You Share
285 | all or a substantial portion of the contents of the database.
286 |
287 | For the avoidance of doubt, this Section 4 supplements and does not
288 | replace Your obligations under this Public License where the Licensed
289 | Rights include other Copyright and Similar Rights.
290 |
291 |
292 | Section 5 -- Disclaimer of Warranties and Limitation of Liability.
293 |
294 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
295 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
296 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
297 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
298 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
299 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
300 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
301 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
302 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
303 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
304 |
305 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
306 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
307 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
308 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
309 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
310 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
311 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
312 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
313 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
314 |
315 | c. The disclaimer of warranties and limitation of liability provided
316 | above shall be interpreted in a manner that, to the extent
317 | possible, most closely approximates an absolute disclaimer and
318 | waiver of all liability.
319 |
320 |
321 | Section 6 -- Term and Termination.
322 |
323 | a. This Public License applies for the term of the Copyright and
324 | Similar Rights licensed here. However, if You fail to comply with
325 | this Public License, then Your rights under this Public License
326 | terminate automatically.
327 |
328 | b. Where Your right to use the Licensed Material has terminated under
329 | Section 6(a), it reinstates:
330 |
331 | 1. automatically as of the date the violation is cured, provided
332 | it is cured within 30 days of Your discovery of the
333 | violation; or
334 |
335 | 2. upon express reinstatement by the Licensor.
336 |
337 | For the avoidance of doubt, this Section 6(b) does not affect any
338 | right the Licensor may have to seek remedies for Your violations
339 | of this Public License.
340 |
341 | c. For the avoidance of doubt, the Licensor may also offer the
342 | Licensed Material under separate terms or conditions or stop
343 | distributing the Licensed Material at any time; however, doing so
344 | will not terminate this Public License.
345 |
346 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
347 | License.
348 |
349 |
350 | Section 7 -- Other Terms and Conditions.
351 |
352 | a. The Licensor shall not be bound by any additional or different
353 | terms or conditions communicated by You unless expressly agreed.
354 |
355 | b. Any arrangements, understandings, or agreements regarding the
356 | Licensed Material not stated herein are separate from and
357 | independent of the terms and conditions of this Public License.
358 |
359 |
360 | Section 8 -- Interpretation.
361 |
362 | a. For the avoidance of doubt, this Public License does not, and
363 | shall not be interpreted to, reduce, limit, restrict, or impose
364 | conditions on any use of the Licensed Material that could lawfully
365 | be made without permission under this Public License.
366 |
367 | b. To the extent possible, if any provision of this Public License is
368 | deemed unenforceable, it shall be automatically reformed to the
369 | minimum extent necessary to make it enforceable. If the provision
370 | cannot be reformed, it shall be severed from this Public License
371 | without affecting the enforceability of the remaining terms and
372 | conditions.
373 |
374 | c. No term or condition of this Public License will be waived and no
375 | failure to comply consented to unless expressly agreed to by the
376 | Licensor.
377 |
378 | d. Nothing in this Public License constitutes or may be interpreted
379 | as a limitation upon, or waiver of, any privileges and immunities
380 | that apply to the Licensor or You, including from the legal
381 | processes of any jurisdiction or authority.
382 |
383 | =======================================================================
384 |
385 | Creative Commons is not a party to its public
386 | licenses. Notwithstanding, Creative Commons may elect to apply one of
387 | its public licenses to material it publishes and in those instances
388 | will be considered the “Licensor.” The text of the Creative Commons
389 | public licenses is dedicated to the public domain under the CC0 Public
390 | Domain Dedication. Except for the limited purpose of indicating that
391 | material is shared under a Creative Commons public license or as
392 | otherwise permitted by the Creative Commons policies published at
393 | creativecommons.org/policies, Creative Commons does not authorize the
394 | use of the trademark "Creative Commons" or any other trademark or logo
395 | of Creative Commons without its prior written consent including,
396 | without limitation, in connection with any unauthorized modifications
397 | to any of its public licenses or any other arrangements,
398 | understandings, or agreements concerning use of licensed material. For
399 | the avoidance of doubt, this paragraph does not form part of the
400 | public licenses.
401 |
402 | Creative Commons may be contacted at creativecommons.org.
403 |
404 |
--------------------------------------------------------------------------------
/Protocol/BlueSolar-HEX-protocol.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softwarecrash/Victron2MQTT/028466b9445ea66dff76e8afe19bc6bf8815a03c/Protocol/BlueSolar-HEX-protocol.pdf
--------------------------------------------------------------------------------
/Protocol/VE.Direct-Protocol-3.33.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softwarecrash/Victron2MQTT/028466b9445ea66dff76e8afe19bc6bf8815a03c/Protocol/VE.Direct-Protocol-3.33.pdf
--------------------------------------------------------------------------------
/Protocol/vicASCII.log:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softwarecrash/Victron2MQTT/028466b9445ea66dff76e8afe19bc6bf8815a03c/Protocol/vicASCII.log
--------------------------------------------------------------------------------
/Protocol/vicHEX.log:
--------------------------------------------------------------------------------
1 |
2 | [2023-08-09 21:47:32.220]# RECV HEX>
3 | 0D 0A 50 49 44 09 30 78 41 30 35 33 0D 0A 46 57 09 31 36 31 0D 0A 53 45 52 23 09 48 51 32 31 30 34 56 4C 41 37 41 0D 0A 56 09 31 32 39 33 30 0D 0A 49 09 2D 39 30 0D 0A 56 50 56 09 31 38 30 0D 0A 50 50 56 09 30 0D 0A 43 53 09 30 0D 0A 4D 50 50 54 09 30 0D 0A 4F 52 09 30 78 30 30 30 30 30 30 30 31 0D 0A 45 52 52 09 30 0D 0A 4C 4F 41 44 09 4F 4E 0D 0A 49 4C 09 30 0D 0A 48 31 39 09 34 34 38 33 0D 0A 48 32 30 09 31 33 0D 0A 48 32 31 09 37 30 0D 0A 48 32 32 09 34 0D 0A 48 32 33 09 34 34 0D 0A 48 53 44 53 09 31 31 34 0D 0A 43 68 65 63 6B 73 75 6D 09 05
4 |
5 | [2023-08-09 21:47:33.201]# RECV HEX>
6 | 0D 0A 50 49 44 09 30 78 41 30 35 33 0D 0A 46 57 09 31 36 31 0D 0A 53 45 52 23 09 48 51 32 31 30 34 56 4C 41 37 41 0D 0A 56 09 31 32 39 33 30 0D 0A 49 09 2D 31 30 30 0D 0A 56 50 56 09 31 38 30 0D 0A 50 50 56 09 30 0D 0A 43 53 09 30 0D 0A 4D 50 50 54 09 30 0D 0A 4F 52 09 30 78 30 30 30 30 30 30 30 31 0D 0A 45 52 52 09 30 0D 0A 4C 4F 41 44 09 4F 4E 0D 0A 49 4C 09 31 30 30 0D 0A 48 31 39 09 34 34 38 33 0D 0A 48 32 30 09 31 33 0D 0A 48 32 31 09 37 30 0D 0A 48 32 32 09 34 0D 0A 48 32 33 09 34 34 0D 0A 48 53 44 53 09 31 31 34 0D 0A 43 68 65 63 6B 73 75 6D 09 7C
7 |
8 | [2023-08-09 21:47:34.201]# RECV HEX>
9 | 0D 0A 50 49 44 09 30 78 41 30 35 33 0D 0A 46 57 09 31 36 31 0D 0A 53 45 52 23 09 48 51 32 31 30 34 56 4C 41 37 41 0D 0A 56 09 31 32 39 32 30 0D 0A 49 09 2D 31 30 30 0D 0A 56 50 56 09 31 38 30 0D 0A 50 50 56 09 30 0D 0A 43 53 09 30 0D 0A 4D 50 50 54 09 30 0D 0A 4F 52 09 30 78 30 30 30 30 30 30 30 31 0D 0A 45 52 52 09 30 0D 0A 4C 4F 41 44 09 4F 4E 0D 0A 49 4C 09 31 30 30 0D 0A 48 31 39 09 34 34 38 33 0D 0A 48 32 30 09 31 33 0D 0A 48 32 31 09 37 30 0D 0A 48 32 32 09 34 0D 0A 48 32 33 09 34 34 0D 0A 48 53 44 53 09 31 31 34 0D 0A 43 68 65 63 6B 73 75 6D 09 7D
10 |
11 | [2023-08-09 21:47:35.196]# RECV HEX>
12 | 0D 0A 50 49 44 09 30 78 41 30 35 33 0D 0A 46 57 09 31 36 31 0D 0A 53 45 52 23 09 48 51 32 31 30 34 56 4C 41 37 41 0D 0A 56 09 31 32 39 33 30 0D 0A 49 09 2D 39 30 0D 0A 56 50 56 09 31 38 30 0D 0A 50 50 56 09 30 0D 0A 43 53 09 30 0D 0A 4D 50 50 54 09 30 0D 0A 4F 52 09 30 78 30 30 30 30 30 30 30 31 0D 0A 45 52 52 09 30 0D 0A 4C 4F 41 44 09 4F 4E 0D 0A 49 4C 09 30 0D 0A 48 31 39 09 34 34 38 33 0D 0A 48 32 30 09 31 33 0D 0A 48 32 31 09 37 30 0D 0A 48 32 32 09 34 0D 0A 48 32 33 09 34 34 0D 0A 48 53 44 53 09 31 31 34 0D 0A 43 68 65 63 6B 73 75 6D 09 05
13 |
14 | [2023-08-09 21:47:36.197]# RECV HEX>
15 | 0D 0A 50 49 44 09 30 78 41 30 35 33 0D 0A 46 57 09 31 36 31 0D 0A 53 45 52 23 09 48 51 32 31 30 34 56 4C 41 37 41 0D 0A 56 09 31 32 39 32 30 0D 0A 49 09 2D 31 30 30 0D 0A 56 50 56 09 31 38 30 0D 0A 50 50 56 09 30 0D 0A 43 53 09 30 0D 0A 4D 50 50 54 09 30 0D 0A 4F 52 09 30 78 30 30 30 30 30 30 30 31 0D 0A 45 52 52 09 30 0D 0A 4C 4F 41 44 09 4F 4E 0D 0A 49 4C 09 31 30 30 0D 0A 48 31 39 09 34 34 38 33 0D 0A 48 32 30 09 31 33 0D 0A 48 32 31 09 37 30 0D 0A 48 32 32 09 34 0D 0A 48 32 33 09 34 34 0D 0A 48 53 44 53 09 31 31 34 0D 0A 43 68 65 63 6B 73 75 6D 09 7D
16 |
17 | [2023-08-09 21:47:37.209]# RECV HEX>
18 | 0D 0A 50 49 44 09 30 78 41 30 35 33 0D 0A 46 57 09 31 36 31 0D 0A 53 45 52 23 09 48 51 32 31 30 34 56 4C 41 37 41 0D 0A 56 09 31 32 39 32 30 0D 0A 49 09 2D 39 30 0D 0A 56 50 56 09 31 38 30 0D 0A 50 50 56 09 30 0D 0A 43 53 09 30 0D 0A 4D 50 50 54 09 30 0D 0A 4F 52 09 30 78 30 30 30 30 30 30 30 31 0D 0A 45 52 52 09 30 0D 0A 4C 4F 41 44 09 4F 4E 0D 0A 49 4C 09 30 0D 0A 48 31 39 09 34 34 38 33 0D 0A 48 32 30 09 31 33 0D 0A 48 32 31 09 37 30 0D 0A 48 32 32 09 34 0D 0A 48 32 33 09 34 34 0D 0A 48 53 44 53 09 31 31 34 0D 0A 43 68 65 63 6B 73 75 6D 09 06
19 |
20 | [2023-08-09 21:47:38.206]# RECV HEX>
21 | 0D 0A 50 49 44 09 30 78 41 30 35 33 0D 0A 46 57 09 31 36 31 0D 0A 53 45 52 23 09 48 51 32 31 30 34 56 4C 41 37 41 0D 0A 56 09 31 32 39 32 30 0D 0A 49 09 2D 39 30 0D 0A 56 50 56 09 31 38 30 0D 0A 50 50 56 09 30 0D 0A 43 53 09 30 0D 0A 4D 50 50 54 09 30 0D 0A 4F 52 09 30 78 30 30 30 30 30 30 30 31 0D 0A 45 52 52 09 30 0D 0A 4C 4F 41 44 09 4F 4E 0D 0A 49 4C 09 30 0D 0A 48 31 39 09 34 34 38 33 0D 0A 48 32 30 09 31 33 0D 0A 48 32 31 09 37 30 0D 0A 48 32 32 09 34 0D 0A 48 32 33 09 34 34 0D 0A 48 53 44 53 09 31 31 34 0D 0A 43 68 65 63 6B 73 75 6D 09 06
22 |
23 | [2023-08-09 21:47:39.204]# RECV HEX>
24 | 0D 0A 50 49 44 09 30 78 41 30 35 33 0D 0A 46 57 09 31 36 31 0D 0A 53 45 52 23 09 48 51 32 31 30 34 56 4C 41 37 41 0D 0A 56 09 31 32 39 32 30 0D 0A 49 09 2D 31 30 30 0D 0A 56 50 56 09 31 38 30 0D 0A 50 50 56 09 30 0D 0A 43 53 09 30 0D 0A 4D 50 50 54 09 30 0D 0A 4F 52 09 30 78 30 30 30 30 30 30 30 31 0D 0A 45 52 52 09 30 0D 0A 4C 4F 41 44 09 4F 4E 0D 0A 49 4C 09 31 30 30 0D 0A 48 31 39 09 34 34 38 33 0D 0A 48 32 30 09 31 33 0D 0A 48 32 31 09 37 30 0D 0A 48 32 32 09 34 0D 0A 48 32 33 09 34 34 0D 0A 48 53 44 53 09 31 31 34 0D 0A 43 68 65 63 6B 73 75 6D 09 7D
25 |
26 | [2023-08-09 21:47:40.187]# RECV HEX>
27 | 0D 0A 50 49 44 09 30 78 41 30 35 33 0D 0A 46 57 09 31 36 31 0D 0A 53 45 52 23 09 48 51 32 31 30 34 56 4C 41 37 41 0D 0A 56 09 31 32 39 33 30 0D 0A 49 09 2D 31 30 30 0D 0A 56 50 56 09 31 38 30 0D 0A 50 50 56 09 30 0D 0A 43 53 09 30 0D 0A 4D 50 50 54 09 30 0D 0A 4F 52 09 30 78 30 30 30 30 30 30 30 31 0D 0A 45 52 52 09 30 0D 0A 4C 4F 41 44 09 4F 4E 0D 0A 49 4C 09 31 30 30 0D 0A 48 31 39 09 34 34 38 33 0D 0A 48 32 30 09 31 33 0D 0A 48 32 31 09 37 30 0D 0A 48 32 32 09 34 0D 0A 48 32 33 09 34 34 0D 0A 48 53 44 53 09 31 31 34 0D 0A 43 68 65 63 6B 73 75 6D 09 7C
28 |
29 | [2023-08-09 21:47:41.186]# RECV HEX>
30 | 0D 0A 50 49 44 09 30 78 41 30 35 33 0D 0A 46 57 09 31 36 31 0D 0A 53 45 52 23 09 48 51 32 31 30 34 56 4C 41 37 41 0D 0A 56 09 31 32 39 32 30 0D 0A 49 09 2D 39 30 0D 0A 56 50 56 09 31 37 30 0D 0A 50 50 56 09 30 0D 0A 43 53 09 30 0D 0A 4D 50 50 54 09 30 0D 0A 4F 52 09 30 78 30 30 30 30 30 30 30 31 0D 0A 45 52 52 09 30 0D 0A 4C 4F 41 44 09 4F 4E 0D 0A 49 4C 09 31 30 30 0D 0A 48 31 39 09 34 34 38 33 0D 0A 48 32 30 09 31 33 0D 0A 48 32 31 09 37 30 0D 0A 48 32 32 09 34 0D 0A 48 32 33 09 34 34 0D 0A 48 53 44 53 09 31 31 34 0D 0A 43 68 65 63 6B 73 75 6D 09 A6
31 |
32 | [2023-08-09 21:47:42.185]# RECV HEX>
33 | 0D 0A 50 49 44 09 30 78 41 30 35 33 0D 0A 46 57 09 31 36 31 0D 0A 53 45 52 23 09 48 51 32 31 30 34 56 4C 41 37 41 0D 0A 56 09 31 32 39 32 30 0D 0A 49 09 2D 31 30 30 0D 0A 56 50 56 09 31 36 30 0D 0A 50 50 56 09 30 0D 0A 43 53 09 30 0D 0A 4D 50 50 54 09 30 0D 0A 4F 52 09 30 78 30 30 30 30 30 30 30 31 0D 0A 45 52 52 09 30 0D 0A 4C 4F 41 44 09 4F 4E 0D 0A 49 4C 09 30 0D 0A 48 31 39 09 34 34 38 33 0D 0A 48 32 30 09 31 33 0D 0A 48 32 31 09 37 30 0D 0A 48 32 32 09 34 0D 0A 48 32 33 09 34 34 0D 0A 48 53 44 53 09 31 31 34 0D 0A 43 68 65 63 6B 73 75 6D 09 E0
34 |
35 | [2023-08-09 21:47:43.183]# RECV HEX>
36 | 0D 0A 50 49 44 09 30 78 41 30 35 33 0D 0A 46 57 09 31 36 31 0D 0A 53 45 52 23 09 48 51 32 31 30 34 56 4C 41 37 41 0D 0A 56 09 31 32 39 33 30 0D 0A 49 09 2D 31 31 30 0D 0A 56 50 56 09 31 36 30 0D 0A 50 50 56 09 30 0D 0A 43 53 09 30 0D 0A 4D 50 50 54 09 30 0D 0A 4F 52 09 30 78 30 30 30 30 30 30 30 31 0D 0A 45 52 52 09 30 0D 0A 4C 4F 41 44 09 4F 4E 0D 0A 49 4C 09 31 30 30 0D 0A 48 31 39 09 34 34 38 33 0D 0A 48 32 30 09 31 33 0D 0A 48 32 31 09 37 30 0D 0A 48 32 32 09 34 0D 0A 48 32 33 09 34 34 0D 0A 48 53 44 53 09 31 31 34 0D 0A 43 68 65 63 6B 73 75 6D 09 7D
37 |
--------------------------------------------------------------------------------
/Protocol/viread.log:
--------------------------------------------------------------------------------
1 | =~=~=~=~=~=~=~=~=~=~=~= PuTTY log 2023.08.09 21:50:22 =~=~=~=~=~=~=~=~=~=~=~=
2 |
3 | PID0xA053
4 | FW161
5 | SER#HQ2104VLA7A
6 | V12930
7 | I-90
8 | VPV120
9 | PPV0
10 | CS0
11 | MPPT0
12 | OR0x00000001
13 | ERR0
14 | LOADON
15 | IL100
16 | H194483
17 | H2013
18 | H2170
19 | H224
20 | H2344
21 | HSDS114
22 | Checksum
23 | PID0xA053
24 | FW161
25 | SER#HQ2104VLA7A
26 | V12930
27 | I-90
28 | VPV120
29 | PPV0
30 | CS0
31 | MPPT0
32 | OR0x00000001
33 | ERR0
34 | LOADON
35 | IL100
36 | H194483
37 | H2013
38 | H2170
39 | H224
40 | H2344
41 | HSDS114
42 | Checksum
43 | PID0xA053
44 | FW161
45 | SER#HQ2104VLA7A
46 | V12920
47 | I-110
48 | VPV120
49 | PPV0
50 | CS0
51 | MPPT0
52 | OR0x00000001
53 | ERR0
54 | LOADON
55 | IL100
56 | H194483
57 | H2013
58 | H2170
59 | H224
60 | H2344
61 | HSDS114
62 | Checksum
63 | PID0xA053
64 | FW161
65 | SER#HQ2104VLA7A
66 | V12920
67 | I-100
68 | VPV140
69 | PPV0
70 | CS0
71 | MPPT0
72 | OR0x00000001
73 | ERR0
74 | LOADON
75 | IL100
76 | H194483
77 | H2013
78 | H2170
79 | H224
80 | H2344
81 | HSDS114
82 | Checksum
83 | PID0xA053
84 | FW161
85 | SER#HQ2104VLA7A
86 | V12930
87 | I-100
88 | VPV180
89 | PPV0
90 | CS0
91 | MPPT0
92 | OR0x00000001
93 | ERR0
94 | LOADON
95 | IL100
96 | H194483
97 | H2013
98 | H2170
99 | H224
100 | H2344
101 | HSDS114
102 | Checksum|
103 | PID0xA053
104 | FW161
105 | SER#HQ2104VLA7A
106 | V12930
107 | I-110
108 | VPV290
109 | PPV0
110 | CS0
111 | MPPT0
112 | OR0x00000001
113 | ERR0
114 | LOADON
115 | IL100
116 | H194483
117 | H2013
118 | H2170
119 | H224
120 | H2344
121 | HSDS114
122 | Checksumy
123 | PID0xA053
124 | FW161
125 | SER#HQ2104VLA7A
126 | V12930
127 | I-100
128 | VPV270
129 | PPV0
130 | CS0
131 | MPPT0
132 | OR0x00000001
133 | ERR0
134 | LOADON
135 | IL100
136 | H194483
137 | H2013
138 | H2170
139 | H224
140 | H2344
141 | HSDS114
142 | Checksum|
143 | PID0xA053
144 | FW161
145 | SER#HQ2104VLA7A
146 | V12930
147 | I-100
148 | VPV250
149 | PPV0
150 | CS0
151 | MPPT0
152 | OR0x00000001
153 | ERR0
154 | LOADON
155 | IL100
156 | H194483
157 | H2013
158 | H2170
159 | H224
160 | H2344
161 | HSDS114
162 | Checksum~
163 | PID0xA053
164 | FW161
165 | SER#HQ2104VLA7A
166 | V12930
167 | I-100
168 | VPV400
169 | PPV0
170 | CS0
171 | MPPT0
172 | OR0x00000001
173 | ERR0
174 | LOADON
175 | IL0
176 | H194483
177 | H2013
178 | H2170
179 | H224
180 | H2344
181 | HSDS114
182 | Checksum
183 | PID0xA053
184 | FW161
185 | SER#HQ2104VLA7A
186 | V12930
187 | I-110
188 | VPV140
189 | PPV0
190 | CS0
191 | MPPT0
192 | OR0x00000001
193 | ERR0
194 | LOADON
195 | IL0
196 | H194483
197 | H2013
198 | H2170
199 | H224
200 | H2344
201 | HSDS114
202 | Checksum
203 | PID0xA053
204 | FW161
205 | SER#HQ2104VLA7A
206 | V12930
207 | I-100
208 | VPV120
209 | PPV0
210 | CS0
211 | MPPT0
212 | OR0x00000001
213 | ERR0
214 | LOADON
215 | IL100
216 | H194483
217 | H2013
218 | H2170
219 | H224
220 | H2344
221 | HSDS114
222 | Checksum
223 | PID0xA053
224 | FW161
225 | SER#HQ2104VLA7A
226 | V12930
227 | I-100
228 | VPV120
229 | PPV0
230 | CS0
231 | MPPT0
232 | OR0x00000001
233 | ERR0
234 | LOADON
235 | IL100
236 | H194483
237 | H2013
238 | H2170
239 | H224
240 | H2344
241 | HSDS114
242 | Checksum
243 | PID0xA053
244 | FW161
245 | SER#HQ2104VLA7A
246 | V12930
247 | I-100
248 | VPV120
249 | PPV0
250 | CS0
251 | MPPT0
252 | OR0x00000001
253 | ERR0
254 | LOADON
255 | IL100
256 | H194483
257 | H2013
258 | H2170
259 | H224
260 | H2344
261 | HSDS114
262 | Checksum
263 | PID0xA053
264 | FW161
265 | SER#HQ2104VLA7A
266 | V12930
267 | I-100
268 | VPV120
269 | PPV0
270 | CS0
271 | MPPT0
272 | OR0x00000001
273 | ERR0
274 | LOADON
275 | IL100
276 | H194483
277 | H2013
278 | H2170
279 | H224
280 | H2344
281 | HSDS114
282 | Checksum
283 | PID0xA053
284 | FW161
285 | SER#HQ2104VLA7A
286 | V12930
287 | I-100
288 | VPV120
289 | PPV0
290 | CS0
291 | MPPT0
292 | OR0x00000001
293 | ERR0
294 | LOADON
295 | IL100
296 | H194483
297 | H2013
298 | H2170
299 | H224
300 | H2344
301 | HSDS114
302 | Checksum
303 | PID0xA053
304 | FW161
305 | SER#HQ2104VLA7A
306 | V12930
307 | I-90
308 | VPV120
309 | PPV0
310 | CS0
311 | MPPT0
312 | OR0x00000001
313 | ERR0
314 | LOADON
315 | IL0
316 | H194483
317 | H2013
318 | H2170
319 | H224
320 | H2344
321 | HSDS114
322 | Checksum
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Victron2MQTT [](https://github.com/softwarecrash/Victron2MQTT/releases/latest) [](https://discord.gg/7gTJk22JDE)
2 |
3 | Little Program for ESP82XX to get the Victron data to web and MQTT
4 |
5 | # Features:
6 | - captive portal for wifi and MQTT config
7 | - config in webinterface
8 | - get essential data over webinterface, get all data like cell voltage and more over MQTT
9 | - get Json over web at /livejson?
10 | - firmware update over webinterface
11 | - [Remote Control](https://github.com/softwarecrash/Victron2MQTT/wiki/Remote-Control)
12 | - [Homeassistant Discovery](https://github.com/softwarecrash/Victron2MQTT/wiki/HomeAssistant-integration)
13 |
14 | **Main screen:**
15 |
16 | 
17 |
18 |
19 | **Settings:**
20 |
21 | 
22 |
23 |
24 | **Config:**
25 |
26 | 
27 |
28 | **MQTT Data**
29 |
30 | 
31 |
32 |
33 | # [Connection to Victron](https://github.com/softwarecrash/Victron2MQTT/wiki/Wiring-Diagram)
34 |
35 |
36 |
37 | # How to use:
38 | - flash your ESP8266 (recommended Wemos D1 Mini) with our [Flash2MQTT-Tool](https://all-solutions.github.io/Flash2MQTT/?get=Victron2MQTT) or with [Tasmotizer](https://github.com/tasmota/tasmotizer/releases)
39 | - connect the ESP like the [wiring diagram](https://github.com/softwarecrash/Victron2MQTT/wiki/Wiring-Diagram)
40 | - search for the wifi ap "Victron2MQTT-AP" and connect to it
41 | - surf to 192.168.4.1 and set up your wifi and optional MQTT
42 | - that's it :)
43 |
44 | ### How-To video by DerKanal
45 |
46 |
47 |
48 |
49 |
50 | ### How-To video by Jarnsen
51 |
52 |
53 |
54 |
55 |
56 | # Completely assembled and tested PCB's
57 |
58 | You are welcome to get fully stocked and tested PCB's. These are then already loaded with the lastest firmware. The earnings from the PCBs are used for the further development of existing and new projects.
59 |
60 | [
](https://all-solutions.store)
61 |
62 | If interested see [here](https://all-solutions.store)
63 |
64 | # Questions?
65 | [Join the Discord Channel (German / English)](https://discord.gg/7gTJk22JDE)
66 |
67 | #
68 | [
](https://donate.softwarecrash.de)
69 |
70 | #
71 | [](https://creativecommons.org/licenses/by-nc-nd/4.0/)
72 |
--------------------------------------------------------------------------------
/include/readme.md:
--------------------------------------------------------------------------------
1 | for pushing tree
--------------------------------------------------------------------------------
/notes:
--------------------------------------------------------------------------------
1 | https://github.com/KinDR007/VictronMPPT-ESPHOME
2 | https://www.akkudoktor.net/forum/panels-wechselrichter-laderegler/victron-ve-direct-mqtt-interface-erste-erfolge-und-fragen/
3 | https://github.com/physee/Victron.Arduino-ESP8266
4 | https://github.com/datjan/esp8266-victron-mppt-solarchargecontroller
5 |
6 | https://www.victronenergy.com/live/vedirect_protocol:faq
7 | https://www.victronenergy.com/upload/documents/Technical-Information-Data-communication-with-Victron-Energy-products_EN.pdf
8 |
9 |
10 | https://www.victronenergy.com/upload/documents/BlueSolar-HEX-protocol.pdf
11 | https://github.com/cterwilliger/VeDirectFrameHandler
--------------------------------------------------------------------------------
/platformio.ini:
--------------------------------------------------------------------------------
1 | ; PlatformIO Project Configuration File
2 | ;
3 | ; Build options: build flags, source filter
4 | ; Upload options: custom upload port, speed and extra flags
5 | ; Library options: dependencies, extra library storages
6 | ; Advanced options: extra scripting
7 | ;
8 | ; Please visit documentation for the other options and examples
9 | ; https://docs.platformio.org/page/projectconf.html
10 |
11 | [env:d1_mini]
12 | platform = espressif8266@4.2.1
13 | board = d1_mini
14 | framework = arduino
15 | monitor_speed = 115200
16 | build_type = debug
17 | monitor_filters = esp8266_exception_decoder, default, time, printable, colorize
18 | board_build.ldscript = eagle.flash.4m.ld ; 4MB (FS:4MB OTA:~3600KB)
19 | upload_speed = 921600
20 |
21 | custom_prog_version = 1.3.0
22 |
23 | build_flags =
24 | -DVERSION=${this.custom_prog_version}
25 | -DPIO_SRC_NAM="Victron2MQTT"
26 | -DESP8266 -DATOMIC_FS_UPDATE
27 |
28 | extra_scripts = pre:tools/mini_html.py
29 | pre:tools/pre_compile.py
30 | post:tools/post_compile.py
31 |
32 | lib_deps =
33 | knolleary/PubSubClient @ ^2.8
34 | bblanchon/ArduinoJson @ ^6.21.3
35 | esphome/ESPAsyncTCP-esphome @ 2.0.0
36 | ESP32Async/ESPAsyncWebServer @ 3.7.6
37 | mathieucarbou/MycilaWebSerial@^8.0.0
38 | alanswx/ESPAsyncWiFiManager@^0.31
39 | plerup/EspSoftwareSerial @ ^8.2.0
40 | https://github.com/dok-net/ghostl
41 | mathieucarbou/OneWire@^2.3.9
42 | gbertaz/NonBlockingDallas@^1.1.0
--------------------------------------------------------------------------------
/src/RTCMemory.h:
--------------------------------------------------------------------------------
1 | /***************************************************************************
2 | * Copyright (C) 2020-2022 Fabiano Riccardi *
3 | * *
4 | * This file is part of RTCMemory. *
5 | * *
6 | * RTCMemory is free software; you can redistribute *
7 | * it and/or modify it under the terms of the GNU Lesser General Public *
8 | * License as published by the Free Software Foundation; either *
9 | * version 2.1 of the License, or (at your option) any later version. *
10 | * *
11 | * RTCMemory is distributed in the hope that it will be useful, *
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
14 | * Lesser General Public License for more details. *
15 | * *
16 | * You should have received a copy of the GNU General Public License *
17 | * along with RTCMemory; if not, see *
18 | ***************************************************************************/
19 | //10.4.204 modifyd by softwarecrash, removed fs and other things for saving lot of space
20 | #ifndef RTCMEMORY_H
21 | #define RTCMEMORY_H
22 |
23 | //#include
24 | //#include
25 |
26 | /**
27 | * T is the user data structure, N is the max number of bytes used by this library. N acts like a
28 | * "soft" limiter.
29 | */
30 | template class RTCMemory {
31 | public:
32 | /**
33 | * Create RTCMemory. Provide a valid filepath to enable the backup of RTC memory on flash memory.
34 | * By default the library uses LittleFS, but you can select an alternative filesystem such as
35 | * SPIFFS.
36 | */
37 | RTCMemory();
38 |
39 | /**
40 | * Initialize the buffer using data in RTC memory. If data are invalid, load data from
41 | * flash. If it fails again, the buffer is cleared. After this call you can safely use this
42 | * instance.
43 | *
44 | * Return true if at least one data source is valid, otherwise false.
45 | */
46 | bool begin();
47 |
48 | /**
49 | * Write data to RTC memory.
50 | *
51 | * Return true if the operation is completed successfully, otherwise false.
52 | */
53 | bool save();
54 |
55 | /**
56 | * Get a pointer to the user buffer, structured accordingly to the typename T.
57 | *
58 | * Return a valid pointer to the data if begin() was called, otherwise nullptr.
59 | */
60 | T *getData();
61 |
62 | private:
63 | const static unsigned int BLOCK_SIZE = 4;
64 |
65 | /**
66 | * Number of bytes to contain the user data structure. This value is ceiled to multiple of 4 to
67 | * respect the memory alignment of RTC memory.
68 | */
69 | const static unsigned int USER_RTC_MEMORY_SIZE = (sizeof(T) - 1) / BLOCK_SIZE * BLOCK_SIZE + BLOCK_SIZE;
70 |
71 | /**
72 | * Max size of user memory.
73 | */
74 | const static unsigned int MAX_USER_RTC_MEMORY_SIZE = 508;
75 |
76 | /**
77 | * Max size available in RTC memory.
78 | */
79 | const static unsigned int MAX_TOTAL_RTC_MEMORY_SIZE = 512;
80 |
81 | struct RTCData {
82 | uint32_t crc32;
83 | // The user buffer
84 | byte data[USER_RTC_MEMORY_SIZE];
85 | };
86 |
87 | /**
88 | * Offset from the first block in RTC memory. This offset is used to shift data toward the
89 | * highest RTC memory addresses.
90 | */
91 | const static unsigned int OFFSET = (MAX_TOTAL_RTC_MEMORY_SIZE - sizeof(RTCData)) / BLOCK_SIZE;
92 |
93 | // The buffer
94 | RTCData rtcData;
95 |
96 | /**
97 | * Tell if this class is ready to be used i.e. if begin() was called.
98 | */
99 | bool ready;
100 |
101 | /**
102 | * Calculate the CRC (32bit) of the given buffer.
103 | *
104 | * Return the CRC code.
105 | */
106 | uint32_t calculateCRC32(const uint8_t *data, size_t length) const;
107 |
108 | /**
109 | * Clear the buffer.
110 | */
111 | void clearBuffer();
112 | };
113 | template
114 | RTCMemory::RTCMemory() : ready(false) {
115 | static_assert(sizeof(T) <= MAX_USER_RTC_MEMORY_SIZE, "The max size of user data is 508 bytes. "
116 | "You must reduce the size of your data "
117 | "structure.");
118 | static_assert(sizeof(RTCData) <= N, "You reach the maximum amount of memory reserved to "
119 | "RTCMemory lib. You may increase this limit beyond 384 bytes "
120 | "and up to 512 bytes, but you will lose RTC data after an "
121 | "OTA update.");
122 | }
123 |
124 | template bool RTCMemory::begin() {
125 | if (ready) {
126 | return true;
127 | }
128 |
129 | if (!ESP.rtcUserMemoryRead(OFFSET, (uint32_t *)&rtcData, sizeof(RTCData))) {
130 | return false;
131 | }
132 |
133 | uint32_t crcOfData = calculateCRC32((uint8_t *)&rtcData.data, USER_RTC_MEMORY_SIZE);
134 |
135 | ready = true;
136 |
137 | if (crcOfData != rtcData.crc32) {
138 | clearBuffer();
139 | return false;
140 | }
141 | return true;
142 | }
143 |
144 | template bool RTCMemory::save() {
145 | if (ready) {
146 | uint32_t crcOfData = calculateCRC32((uint8_t *)&rtcData.data, USER_RTC_MEMORY_SIZE);
147 | rtcData.crc32 = crcOfData;
148 |
149 | if (ESP.rtcUserMemoryWrite(OFFSET, (uint32_t *)&rtcData, sizeof(RTCData))) {
150 | return true;
151 | } else {
152 | return false;
153 | }
154 | } else {
155 | return false;
156 | }
157 | }
158 |
159 | template T *RTCMemory::getData() {
160 | if (ready) { return reinterpret_cast(rtcData.data); }
161 | return nullptr;
162 | };
163 |
164 |
165 | template uint32_t RTCMemory::calculateCRC32(const uint8_t *data, size_t length) const {
166 | uint32_t crc = 0xffffffff;
167 | while (length--) {
168 | uint8_t c = *data++;
169 | for (uint32_t i = 0x80; i > 0; i >>= 1) {
170 | bool bit = crc & 0x80000000;
171 | if (c & i) { bit = !bit; }
172 | crc <<= 1;
173 | if (bit) { crc ^= 0x04c11db7; }
174 | }
175 | }
176 | return crc;
177 | }
178 |
179 | template void RTCMemory::clearBuffer() {
180 | memset((void *)&rtcData.data, 0, USER_RTC_MEMORY_SIZE);
181 | uint32_t result = calculateCRC32((uint8_t *)rtcData.data, USER_RTC_MEMORY_SIZE);
182 | rtcData.crc32 = result;
183 | }
184 |
185 | #endif // END RTCMEMORY_H
186 |
--------------------------------------------------------------------------------
/src/Settings.h:
--------------------------------------------------------------------------------
1 | // Settings: Stores persistant settings, loads and saves to EEPROM
2 |
3 | #include
4 | #include
5 |
6 | #define EEPROM_SIZE 1024
7 |
8 | class Settings
9 | {
10 | // change eeprom config version ONLY when new parameter is added and need reset the parameter
11 | unsigned int configVersion = 11;
12 |
13 | public:
14 | String deviceNameStr;
15 | struct Data
16 | { // do not re-sort this struct
17 | unsigned int coVers; // config version, if changed, previus config will erased
18 | char deviceName[40]; // device name
19 | char mqttServer[40]; // mqtt Server adress
20 | char mqttUser[40]; // mqtt Username
21 | char mqttPassword[40]; // mqtt Password
22 | char mqttTopic[40]; // mqtt publish topic
23 | char mqttTriggerPath[80]; // MQTT Data Trigger Path
24 | unsigned int mqttPort; // mqtt port
25 | unsigned int mqttRefresh; // mqtt refresh time
26 | bool mqttJson; // switch between classic mqtt and json
27 | bool webUIdarkmode; // flag for dark mode
28 | char httpUser[40]; // http basic auth username
29 | char httpPass[40]; // http basic auth password
30 | bool haDiscovery; // HomeAssistant Discovery switch
31 | // bool debugmode; // Debug-Mode
32 | bool keepRcState;
33 | bool rcState;
34 | byte LEDBrightness; // brigthness of led
35 | } data;
36 |
37 | void load()
38 | {
39 | data = {}; // clear bevor load data
40 | EEPROM.begin(EEPROM_SIZE);
41 | EEPROM.get(0, data);
42 | EEPROM.end();
43 | coVersCheck();
44 | sanitycheck();
45 | deviceNameStr = data.deviceName;
46 | }
47 |
48 | void save()
49 | {
50 | sanitycheck();
51 | EEPROM.begin(EEPROM_SIZE);
52 | EEPROM.put(0, data);
53 | EEPROM.commit();
54 | EEPROM.end();
55 | }
56 |
57 | void reset()
58 | {
59 | data = {};
60 | save();
61 | }
62 |
63 | private:
64 | // check the variables from eeprom
65 |
66 | void sanitycheck()
67 | {
68 | if (strlen(data.deviceName) == 0 || strlen(data.deviceName) >= 40)
69 | {
70 | strcpy(data.deviceName, "Victron2MQTT");
71 | }
72 | if (strlen(data.mqttServer) == 0 || strlen(data.mqttServer) >= 40)
73 | {
74 | strcpy(data.mqttServer, "");
75 | }
76 | if (strlen(data.mqttUser) == 0 || strlen(data.mqttUser) >= 40)
77 | {
78 | strcpy(data.mqttUser, "");
79 | }
80 | if (strlen(data.mqttPassword) == 0 || strlen(data.mqttPassword) >= 40)
81 | {
82 | strcpy(data.mqttPassword, "");
83 | }
84 | if (strlen(data.mqttTopic) == 0 || strlen(data.mqttTopic) >= 40)
85 | {
86 | strcpy(data.mqttTopic, "Victron");
87 | }
88 | if (data.mqttPort <= 0 || data.mqttPort >= 65530)
89 | {
90 | data.mqttPort = 0;
91 | }
92 | if (data.mqttRefresh <= 1 || data.mqttRefresh >= 65530)
93 | {
94 | data.mqttRefresh = 0;
95 | }
96 | if (data.mqttJson && !data.mqttJson)
97 | {
98 | data.mqttJson = false;
99 | }
100 | if (strlen(data.mqttTriggerPath) == 0 || strlen(data.mqttTriggerPath) >= 80)
101 | {
102 | strcpy(data.mqttTriggerPath, "");
103 | }
104 | if (data.webUIdarkmode && !data.webUIdarkmode)
105 | {
106 | data.webUIdarkmode = false;
107 | }
108 | if (strlen(data.httpUser) == 0 || strlen(data.httpUser) >= 40)
109 | {
110 | strcpy(data.httpUser, "");
111 | }
112 | if (strlen(data.httpPass) == 0 || strlen(data.httpPass) >= 40)
113 | {
114 | strcpy(data.httpPass, "");
115 | }
116 | if (data.haDiscovery && !data.haDiscovery)
117 | {
118 | data.haDiscovery = false;
119 | }
120 | // if (data.debugmode && !data.debugmode)
121 | //{
122 | // data.debugmode = false;
123 | //}
124 | if (data.keepRcState && !data.keepRcState)
125 | {
126 | data.keepRcState = false;
127 | }
128 | if (data.rcState && !data.rcState)
129 | {
130 | data.rcState = false;
131 | }
132 | if (data.LEDBrightness && !data.LEDBrightness)
133 | {
134 | data.LEDBrightness = 127;
135 | }
136 | }
137 | void coVersCheck()
138 | {
139 | if (data.coVers != configVersion)
140 | {
141 | data.coVers = configVersion;
142 | strcpy(data.deviceName, "Victron2MQTT");
143 | strcpy(data.mqttServer, "");
144 | strcpy(data.mqttUser, "");
145 | strcpy(data.mqttPassword, "");
146 | strcpy(data.mqttTopic, "Victron");
147 | strcpy(data.mqttTriggerPath, "");
148 | data.mqttPort = 0;
149 | data.mqttRefresh = 300;
150 | data.mqttJson = false;
151 | data.webUIdarkmode = false;
152 | strcpy(data.httpUser, "");
153 | strcpy(data.httpPass, "");
154 | data.haDiscovery = false;
155 | // data.debugmode = false;
156 | data.keepRcState = false;
157 | data.rcState = false;
158 | data.LEDBrightness = 127;
159 | save();
160 | load();
161 | }
162 | }
163 | };
164 |
--------------------------------------------------------------------------------
/src/VeDirectDataList.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | struct VePrettyEntry {
6 | const char* key;
7 | const char* name;
8 | const char* op;
9 | };
10 |
11 | // --- PRETTY-DATENSTRINGS ---
12 | const char key_0[] PROGMEM = "V";
13 | const char name_0[] PROGMEM = "Voltage";
14 | const char op_0[] PROGMEM = "1000";
15 | const char key_1[] PROGMEM = "V2";
16 | const char name_1[] PROGMEM = "Voltage_2";
17 | const char op_1[] PROGMEM = "1000";
18 | const char key_2[] PROGMEM = "V3";
19 | const char name_2[] PROGMEM = "Voltage_3";
20 | const char op_2[] PROGMEM = "1000";
21 | const char key_3[] PROGMEM = "VS";
22 | const char name_3[] PROGMEM = "Starter_voltage";
23 | const char op_3[] PROGMEM = "1000";
24 | const char key_4[] PROGMEM = "VM";
25 | const char name_4[] PROGMEM = "Mid_voltage";
26 | const char op_4[] PROGMEM = "1000";
27 | const char key_5[] PROGMEM = "DM";
28 | const char name_5[] PROGMEM = "Mid_deviation";
29 | const char op_5[] PROGMEM = "10";
30 | const char key_6[] PROGMEM = "VPV";
31 | const char name_6[] PROGMEM = "Panel_voltage";
32 | const char op_6[] PROGMEM = "1000";
33 | const char key_7[] PROGMEM = "PPV";
34 | const char name_7[] PROGMEM = "Panel_power";
35 | const char op_7[] PROGMEM = "0";
36 | const char key_8[] PROGMEM = "I";
37 | const char name_8[] PROGMEM = "Battery_current";
38 | const char op_8[] PROGMEM = "1000";
39 | const char key_9[] PROGMEM = "I2";
40 | const char name_9[] PROGMEM = "Battery_current_2";
41 | const char op_9[] PROGMEM = "1000";
42 | const char key_10[] PROGMEM = "I3";
43 | const char name_10[] PROGMEM = "Battery_current_3";
44 | const char op_10[] PROGMEM = "1000";
45 | const char key_11[] PROGMEM = "IL";
46 | const char name_11[] PROGMEM = "Load_current";
47 | const char op_11[] PROGMEM = "1000";
48 | const char key_12[] PROGMEM = "LOAD";
49 | const char name_12[] PROGMEM = "Load_output_state";
50 | const char op_12[] PROGMEM = "";
51 | const char key_13[] PROGMEM = "T";
52 | const char name_13[] PROGMEM = "Battery_temperature";
53 | const char op_13[] PROGMEM = "0";
54 | const char key_14[] PROGMEM = "P";
55 | const char name_14[] PROGMEM = "Instantaneous_power";
56 | const char op_14[] PROGMEM = "0";
57 | const char key_15[] PROGMEM = "CE";
58 | const char name_15[] PROGMEM = "Consumed_Amp_Hours";
59 | const char op_15[] PROGMEM = "1000";
60 | const char key_16[] PROGMEM = "SOC";
61 | const char name_16[] PROGMEM = "SOC";
62 | const char op_16[] PROGMEM = "10";
63 | const char key_17[] PROGMEM = "TTG";
64 | const char name_17[] PROGMEM = "Time_to_go";
65 | const char op_17[] PROGMEM = "0";
66 | const char key_18[] PROGMEM = "ALARM";
67 | const char name_18[] PROGMEM = "Alarm";
68 | const char op_18[] PROGMEM = "";
69 | const char key_19[] PROGMEM = "RELAY";
70 | const char name_19[] PROGMEM = "Relay";
71 | const char op_19[] PROGMEM = "";
72 | const char key_20[] PROGMEM = "AR";
73 | const char name_20[] PROGMEM = "Alarm_code";
74 | const char op_20[] PROGMEM = "0";
75 | const char key_21[] PROGMEM = "OR";
76 | const char name_21[] PROGMEM = "Off_reason";
77 | const char op_21[] PROGMEM = "0";
78 | const char key_22[] PROGMEM = "H1";
79 | const char name_22[] PROGMEM = "Deepest_discharge";
80 | const char op_22[] PROGMEM = "1000";
81 | const char key_23[] PROGMEM = "H2";
82 | const char name_23[] PROGMEM = "Last_discharge";
83 | const char op_23[] PROGMEM = "1000";
84 | const char key_24[] PROGMEM = "H3";
85 | const char name_24[] PROGMEM = "Average_discharge";
86 | const char op_24[] PROGMEM = "1000";
87 | const char key_25[] PROGMEM = "H4";
88 | const char name_25[] PROGMEM = "Charge_cycles";
89 | const char op_25[] PROGMEM = "0";
90 | const char key_26[] PROGMEM = "H5";
91 | const char name_26[] PROGMEM = "Full_discharges";
92 | const char op_26[] PROGMEM = "0";
93 | const char key_27[] PROGMEM = "H6";
94 | const char name_27[] PROGMEM = "Cumulative_Ah_drawn";
95 | const char op_27[] PROGMEM = "1000";
96 | const char key_28[] PROGMEM = "H7";
97 | const char name_28[] PROGMEM = "Minimum_voltage";
98 | const char op_28[] PROGMEM = "1000";
99 | const char key_29[] PROGMEM = "H8";
100 | const char name_29[] PROGMEM = "Maximum_voltage";
101 | const char op_29[] PROGMEM = "1000";
102 | const char key_30[] PROGMEM = "H9";
103 | const char name_30[] PROGMEM = "Last_full_charge";
104 | const char op_30[] PROGMEM = "3600";
105 | const char key_31[] PROGMEM = "H10";
106 | const char name_31[] PROGMEM = "Num_automatic_sync";
107 | const char op_31[] PROGMEM = "0";
108 | const char key_32[] PROGMEM = "H11";
109 | const char name_32[] PROGMEM = "Num_low_volt_alarms";
110 | const char op_32[] PROGMEM = "0";
111 | const char key_33[] PROGMEM = "H12";
112 | const char name_33[] PROGMEM = "Num_high_volt_alarms";
113 | const char op_33[] PROGMEM = "0";
114 | const char key_34[] PROGMEM = "H13";
115 | const char name_34[] PROGMEM = "Num_low_aux_vol_alarms";
116 | const char op_34[] PROGMEM = "0";
117 | const char key_35[] PROGMEM = "H14";
118 | const char name_35[] PROGMEM = "Num_high_aux_vol_alarms";
119 | const char op_35[] PROGMEM = "0";
120 | const char key_36[] PROGMEM = "H15";
121 | const char name_36[] PROGMEM = "Min_aux_volt";
122 | const char op_36[] PROGMEM = "1000";
123 | const char key_37[] PROGMEM = "H16";
124 | const char name_37[] PROGMEM = "Max_aux_volt";
125 | const char op_37[] PROGMEM = "1000";
126 | const char key_38[] PROGMEM = "H17";
127 | const char name_38[] PROGMEM = "Amount_discharged_energy";
128 | const char op_38[] PROGMEM = "100";
129 | const char key_39[] PROGMEM = "H18";
130 | const char name_39[] PROGMEM = "Amount_charged_energy";
131 | const char op_39[] PROGMEM = "100";
132 | const char key_40[] PROGMEM = "H19";
133 | const char name_40[] PROGMEM = "total_kWh";
134 | const char op_40[] PROGMEM = "100";
135 | const char key_41[] PROGMEM = "H20";
136 | const char name_41[] PROGMEM = "today_kWh";
137 | const char op_41[] PROGMEM = "100";
138 | const char key_42[] PROGMEM = "H21";
139 | const char name_42[] PROGMEM = "Max_pow_today";
140 | const char op_42[] PROGMEM = "0";
141 | const char key_43[] PROGMEM = "H22";
142 | const char name_43[] PROGMEM = "Yesterday_kWh";
143 | const char op_43[] PROGMEM = "100";
144 | const char key_44[] PROGMEM = "H23";
145 | const char name_44[] PROGMEM = "Max_pow_yesterday";
146 | const char op_44[] PROGMEM = "0";
147 | const char key_45[] PROGMEM = "ERR";
148 | const char name_45[] PROGMEM = "Current_error";
149 | const char op_45[] PROGMEM = "0";
150 | const char key_46[] PROGMEM = "CS";
151 | const char name_46[] PROGMEM = "Operation_state";
152 | const char op_46[] PROGMEM = "0";
153 | const char key_47[] PROGMEM = "BMV";
154 | const char name_47[] PROGMEM = "Model_description";
155 | const char op_47[] PROGMEM = "";
156 | const char key_48[] PROGMEM = "FW";
157 | const char name_48[] PROGMEM = "Firmware_version_16";
158 | const char op_48[] PROGMEM = "";
159 | const char key_49[] PROGMEM = "FWE";
160 | const char name_49[] PROGMEM = "Firmware_version_24";
161 | const char op_49[] PROGMEM = "";
162 | const char key_50[] PROGMEM = "PID";
163 | const char name_50[] PROGMEM = "Device_model";
164 | const char op_50[] PROGMEM = "";
165 | const char key_51[] PROGMEM = "SER#";
166 | const char name_51[] PROGMEM = "Serial_number";
167 | const char op_51[] PROGMEM = "";
168 | const char key_52[] PROGMEM = "HSDS";
169 | const char name_52[] PROGMEM = "Day";
170 | const char op_52[] PROGMEM = "0";
171 | const char key_53[] PROGMEM = "MODE";
172 | const char name_53[] PROGMEM = "Device_mode";
173 | const char op_53[] PROGMEM = "";
174 | const char key_54[] PROGMEM = "AC_OUT_V";
175 | const char name_54[] PROGMEM = "AC_out_volt";
176 | const char op_54[] PROGMEM = "100";
177 | const char key_55[] PROGMEM = "AC_OUT_I";
178 | const char name_55[] PROGMEM = "AC_out_current";
179 | const char op_55[] PROGMEM = "10";
180 | const char key_56[] PROGMEM = "AC_OUT_S";
181 | const char name_56[] PROGMEM = "AC_out_apparent_pow";
182 | const char op_56[] PROGMEM = "";
183 | const char key_57[] PROGMEM = "WARN";
184 | const char name_57[] PROGMEM = "Warning_reason";
185 | const char op_57[] PROGMEM = "";
186 | const char key_58[] PROGMEM = "MPPT";
187 | const char name_58[] PROGMEM = "Tracker_operation_mode";
188 | const char op_58[] PROGMEM = "";
189 | const char key_59[] PROGMEM = "MON";
190 | const char name_59[] PROGMEM = "DC_monitor_mode";
191 | const char op_59[] PROGMEM = "";
192 | const char key_60[] PROGMEM = "DC_IN_V";
193 | const char name_60[] PROGMEM = "DC_input_voltage";
194 | const char op_60[] PROGMEM = "100";
195 | const char key_61[] PROGMEM = "DC_IN_I";
196 | const char name_61[] PROGMEM = "DC_input_current";
197 | const char op_61[] PROGMEM = "10";
198 | const char key_62[] PROGMEM = "DC_IN_P";
199 | const char name_62[] PROGMEM = "DC_input_power";
200 | const char op_62[] PROGMEM = "0";
201 |
202 | // --- PRETTY-DATENLISTE ---
203 | const VePrettyEntry VePrettyData[] PROGMEM = {
204 | {key_0, name_0, op_0},
205 | {key_1, name_1, op_1},
206 | {key_2, name_2, op_2},
207 | {key_3, name_3, op_3},
208 | {key_4, name_4, op_4},
209 | {key_5, name_5, op_5},
210 | {key_6, name_6, op_6},
211 | {key_7, name_7, op_7},
212 | {key_8, name_8, op_8},
213 | {key_9, name_9, op_9},
214 | {key_10, name_10, op_10},
215 | {key_11, name_11, op_11},
216 | {key_12, name_12, op_12},
217 | {key_13, name_13, op_13},
218 | {key_14, name_14, op_14},
219 | {key_15, name_15, op_15},
220 | {key_16, name_16, op_16},
221 | {key_17, name_17, op_17},
222 | {key_18, name_18, op_18},
223 | {key_19, name_19, op_19},
224 | {key_20, name_20, op_20},
225 | {key_21, name_21, op_21},
226 | {key_22, name_22, op_22},
227 | {key_23, name_23, op_23},
228 | {key_24, name_24, op_24},
229 | {key_25, name_25, op_25},
230 | {key_26, name_26, op_26},
231 | {key_27, name_27, op_27},
232 | {key_28, name_28, op_28},
233 | {key_29, name_29, op_29},
234 | {key_30, name_30, op_30},
235 | {key_31, name_31, op_31},
236 | {key_32, name_32, op_32},
237 | {key_33, name_33, op_33},
238 | {key_34, name_34, op_34},
239 | {key_35, name_35, op_35},
240 | {key_36, name_36, op_36},
241 | {key_37, name_37, op_37},
242 | {key_38, name_38, op_38},
243 | {key_39, name_39, op_39},
244 | {key_40, name_40, op_40},
245 | {key_41, name_41, op_41},
246 | {key_42, name_42, op_42},
247 | {key_43, name_43, op_43},
248 | {key_44, name_44, op_44},
249 | {key_45, name_45, op_45},
250 | {key_46, name_46, op_46},
251 | {key_47, name_47, op_47},
252 | {key_48, name_48, op_48},
253 | {key_49, name_49, op_49},
254 | {key_50, name_50, op_50},
255 | {key_51, name_51, op_51},
256 | {key_52, name_52, op_52},
257 | {key_53, name_53, op_53},
258 | {key_54, name_54, op_54},
259 | {key_55, name_55, op_55},
260 | {key_56, name_56, op_56},
261 | {key_57, name_57, op_57},
262 | {key_58, name_58, op_58},
263 | {key_59, name_59, op_59},
264 | {key_60, name_60, op_60},
265 | {key_61, name_61, op_61},
266 | {key_62, name_62, op_62},
267 | };
268 | const size_t VePrettyDataSize = sizeof(VePrettyData) / sizeof(VePrettyData[0]);
269 |
270 |
271 |
272 | /*
273 |
274 | static const char * VePrettyData[][3] PROGMEM {
275 | // get name, pretify name, value operator
276 | {"V","Voltage", "1000",}, // display in webUI | Dont edit
277 | {"V2","Voltage_2", "1000",},
278 | {"V3","Voltage_3", "1000",},
279 | {"VS","Starter_voltage", "1000",},
280 | {"VM","Mid_voltage", "1000",},
281 | {"DM","Mid_deviation", "10",},
282 | {"VPV","Panel_voltage", "1000",}, // display in webUI | Dont edit
283 | {"PPV","Panel_power", "0",}, // display in webUI | Dont edit
284 | {"I","Battery_current","1000"}, // display in webUI | Dont edit
285 | {"I2","Battery_current_2","1000"},
286 | {"I3","Battery_current_3","1000"},
287 | {"IL","Load_current","1000"}, // display in webUI | Dont edit
288 | {"LOAD","Load_output_state",""}, // display in webUI | Dont edit
289 | {"T","Battery_temperature","0"}, // display in webUI | Dont edit
290 | {"P","Instantaneous_power","0"},
291 | {"CE","Consumed_Amp_Hours","1000"},
292 | {"SOC", "SOC", "10"}, // display in webUI | Dont edit
293 | {"TTG","Time_to_go","0"},
294 | {"ALARM","Alarm",""},
295 | {"RELAY","Relay",""}, // display in webUI | Dont edit
296 | {"AR", "Alarm_code", "0"},
297 | {"OR", "Off_reason", "0"},
298 | {"H1", "Deepest_discharge", "1000"},
299 | {"H2", "Last_discharge", "1000"},
300 | {"H3", "Average_discharge", "1000"},
301 | {"H4", "Charge_cycles", "0"},
302 | {"H5", "Full_discharges", "0"},
303 | {"H6", "Cumulative_Ah_drawn", "1000"},
304 | {"H7", "Minimum_voltage", "1000"},
305 | {"H8", "Maximum_voltage", "1000"},
306 | {"H9", "Last_full_charge", "3600"},
307 | {"H10", "Num_automatic_sync", "0"},
308 | {"H11", "Num_low_volt_alarms", "0"},
309 | {"H12", "Num_high_volt_alarms", "0"},
310 | {"H13", "Num_low_aux_vol_alarms", "0"},
311 | {"H14", "Num_high_aux_vol_alarms", "0"},
312 | {"H15", "Min_aux_volt", "1000"},
313 | {"H16", "Max_aux_volt", "1000"},
314 | {"H17", "Amount_discharged_energy", "100"},
315 | {"H18", "Amount_charged_energy", "100"},
316 | {"H19", "total_kWh", "100"},
317 | {"H20", "today_kWh", "100"},
318 | {"H21", "Max_pow_today", "0"},
319 | {"H22", "Yesterday_kWh", "100"},
320 | {"H23", "Max_pow_yesterday", "0"},
321 | {"ERR", "Current_error", "0"},
322 | {"CS", "Operation_state", "0"},
323 | {"BMV", "Model_description", ""},
324 | {"FW", "Firmware_version_16", ""},
325 | {"FWE", "Firmware_version_24", ""},
326 | {"PID","Device_model",""},
327 | {"SER#","Serial_number",""},
328 | {"HSDS","Day","0"},
329 | {"MODE","Device_mode",""},
330 | {"AC_OUT_V","AC_out_volt","100"},
331 | {"AC_OUT_I","AC_out_current","10"},
332 | {"AC_OUT_S","AC_out_apparent_pow",""},
333 | {"WARN","Warning_reason",""},
334 | {"MPPT","Tracker_operation_mode",""},
335 | {"MON","DC_monitor_mode",""},
336 | {"DC_IN_V","DC_input_voltage","100"},
337 | {"DC_IN_I","DC_input_current","10"},
338 | {"DC_IN_P","DC_input_power","0"},
339 | };*/
340 | static const char * haDescriptor[][4] PROGMEM {
341 | // state_topic, icon, unit_ofmeasurement, class
342 | {"Voltage", "car-battery", "V", "voltage"},
343 | {"Voltage_2", "car-battery", "V", "voltage"},
344 | {"Voltage_3", "car-battery", "V", "voltage"},
345 | {"Starter_voltage", "flash-triangle-outline", "V", "voltage"},
346 | {"Mid_voltage", "battery-outline", "V", "voltage"},
347 | {"Mid_deviation", "battery-outline", "%", "battery"},
348 | {"Panel_voltage", "solar-panel", "V", "voltage"},
349 | {"Panel_power","solar-power","W","power"},
350 | {"Battery_current", "current-dc", "A", "current"},
351 | {"Battery_current_2", "current-dc", "A", "current"},
352 | {"Battery_current_3", "current-dc", "A", "current"},
353 | {"Load_current", "battery-charging", "A", "current"},
354 | {"Load_output_state", "export", "", ""},
355 | {"Battery_temperature", "thermometer", "°C", "temperature"},
356 | {"Instantaneous_power", "solar-power", "W", "power"},
357 | {"Consumed_Amp_Hours", "home-battery-outline", "Ah", ""},
358 | {"SOC", "battery-charging", "%", "battery"},
359 | {"Time_to_go", "camera-timer", "min", "duration"},
360 | {"Alarm", "alarm-light-outline", "", ""},
361 | {"Relay", "electric-switch", "", ""},
362 | {"Alarm_code", "alarm-panel-outline", "", ""},
363 | {"Off_reason", "alarm-panel-outline", "", ""},
364 | {"Deepest_discharge", "battery-outline", "Ah", "energy"},
365 | {"Last_discharge", "battery-outline", "Ah", "energy"},
366 | {"Average_discharge", "battery-outline", "Ah", "energy"},
367 | {"Charge_cycles", "counter", "", ""},
368 | {"Full_discharges", "counter", "", ""},
369 | {"Cumulative_Ah_drawn", "battery-outline", "Ah", "energy"},
370 | {"Minimum_voltage", "battery-charging-outline", "V", "voltage"},
371 | {"Maximum_voltage", "battery-charging-high", "V", "voltage"},
372 | {"Last_full_charge", "clock-time-eight-outline", "h", "duration"},
373 | {"Num_automatic_sync", "counter", "", ""},
374 | {"Num_low_volt_alarms", "counter", "", ""},
375 | {"Num_high_volt_alarms", "counter", "", ""},
376 | {"Num_low_aux_vol_alarms", "counter", "", ""},
377 | {"Num_high_aux_vol_alarms", "counter", "", ""},
378 | {"Min_aux_volt", "battery-low", "V", "voltage"},
379 | {"Max_aux_volt", "battery-high", "V", "voltage"},
380 | {"Amount_discharged_energy", "battery-arrow-down-outline", "kWh", "energy"},
381 | {"Amount_charged_energy", "battery-arrow-up-outline", "kWh", "energy"},
382 | {"total_kWh", "solar-power", "kWh", "energy"},
383 | {"today_kWh", "solar-power", "kWh", "energy"},
384 | {"Max_pow_today", "solar-power-variant-outline", "W", "power"},
385 | {"Yesterday_kWh", "solar-power", "kWh", "energy"},
386 | {"Max_pow_yesterday", "solar-power-variant-outline", "W", "power"},
387 | {"Current_error", "alert-circle-outline", "", ""},
388 | {"Operation_state", "state-machine", "", ""},
389 | {"Model_description", "select-inverse", "", ""},
390 | {"Firmware_version_16", "select-inverse", "", ""},
391 | {"Firmware_version_24", "select-inverse", "", ""},
392 | {"Device_model", "select-inverse", "", ""},
393 | {"Serial_number", "format-list-numbered", "", ""},
394 | {"Day", "calendar-today", "", ""},
395 | {"Device_mode", "tablet-dashboard", "", ""},
396 | {"AC_out_volt", "current-ac", "V", "voltage"},
397 | {"AC_out_current", "current-ac", "A", "current"},
398 | {"AC_out_apparent_pow", "current-ac", "VA", "apparent_power"},
399 | {"Warning_reason", "alert-outline", "", ""},
400 | {"Tracker_operation_mode", "pencil-outline", "", ""},
401 | {"DC_monitor_mode", "multicast", "", ""},
402 | {"DC_input_voltage", "current-dc", "V", "voltage"},
403 | {"DC_input_current", "current-dc", "A", "current"},
404 | {"DC_input_power", "current-dc", "W", "power"},
405 | {"Device_connection", "alert-circle-outline", "", ""}
406 | };
--------------------------------------------------------------------------------
/src/VeDirectDeviceCodes.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | struct VeCodeEntry {
6 | const char* code;
7 | const char* text;
8 | };
9 |
10 | // --- Mappingtabelle: VeDirectDeviceCodeAR ---
11 | static const char VeDirectDeviceCodeAR_code_0[] PROGMEM = "1";
12 | static const char VeDirectDeviceCodeAR_text_0[] PROGMEM = "Low Voltage";
13 | static const char VeDirectDeviceCodeAR_code_1[] PROGMEM = "2";
14 | static const char VeDirectDeviceCodeAR_text_1[] PROGMEM = "High Voltage";
15 | static const char VeDirectDeviceCodeAR_code_2[] PROGMEM = "4";
16 | static const char VeDirectDeviceCodeAR_text_2[] PROGMEM = "Low SOC";
17 | static const char VeDirectDeviceCodeAR_code_3[] PROGMEM = "8";
18 | static const char VeDirectDeviceCodeAR_text_3[] PROGMEM = "Low Starter Voltage";
19 | static const char VeDirectDeviceCodeAR_code_4[] PROGMEM = "16";
20 | static const char VeDirectDeviceCodeAR_text_4[] PROGMEM = "High Starter Voltage";
21 | static const char VeDirectDeviceCodeAR_code_5[] PROGMEM = "32";
22 | static const char VeDirectDeviceCodeAR_text_5[] PROGMEM = "Low Temperature";
23 | static const char VeDirectDeviceCodeAR_code_6[] PROGMEM = "64";
24 | static const char VeDirectDeviceCodeAR_text_6[] PROGMEM = "High Temperature";
25 | static const char VeDirectDeviceCodeAR_code_7[] PROGMEM = "128";
26 | static const char VeDirectDeviceCodeAR_text_7[] PROGMEM = "Mid Voltage";
27 | static const char VeDirectDeviceCodeAR_code_8[] PROGMEM = "256";
28 | static const char VeDirectDeviceCodeAR_text_8[] PROGMEM = "Overload";
29 | static const char VeDirectDeviceCodeAR_code_9[] PROGMEM = "512";
30 | static const char VeDirectDeviceCodeAR_text_9[] PROGMEM = "DC-ripple";
31 | static const char VeDirectDeviceCodeAR_code_10[] PROGMEM = "1024";
32 | static const char VeDirectDeviceCodeAR_text_10[] PROGMEM = "Low V AC out";
33 | static const char VeDirectDeviceCodeAR_code_11[] PROGMEM = "2048";
34 | static const char VeDirectDeviceCodeAR_text_11[] PROGMEM = "LHigh V AC out";
35 | static const char VeDirectDeviceCodeAR_code_12[] PROGMEM = "4096";
36 | static const char VeDirectDeviceCodeAR_text_12[] PROGMEM = "Short Circuit";
37 | static const char VeDirectDeviceCodeAR_code_13[] PROGMEM = "8192";
38 | static const char VeDirectDeviceCodeAR_text_13[] PROGMEM = "BMS Lockout";
39 |
40 | static const VeCodeEntry VeDirectDeviceCodeAR[] PROGMEM = {
41 | {VeDirectDeviceCodeAR_code_0, VeDirectDeviceCodeAR_text_0},
42 | {VeDirectDeviceCodeAR_code_1, VeDirectDeviceCodeAR_text_1},
43 | {VeDirectDeviceCodeAR_code_2, VeDirectDeviceCodeAR_text_2},
44 | {VeDirectDeviceCodeAR_code_3, VeDirectDeviceCodeAR_text_3},
45 | {VeDirectDeviceCodeAR_code_4, VeDirectDeviceCodeAR_text_4},
46 | {VeDirectDeviceCodeAR_code_5, VeDirectDeviceCodeAR_text_5},
47 | {VeDirectDeviceCodeAR_code_6, VeDirectDeviceCodeAR_text_6},
48 | {VeDirectDeviceCodeAR_code_7, VeDirectDeviceCodeAR_text_7},
49 | {VeDirectDeviceCodeAR_code_8, VeDirectDeviceCodeAR_text_8},
50 | {VeDirectDeviceCodeAR_code_9, VeDirectDeviceCodeAR_text_9},
51 | {VeDirectDeviceCodeAR_code_10, VeDirectDeviceCodeAR_text_10},
52 | {VeDirectDeviceCodeAR_code_11, VeDirectDeviceCodeAR_text_11},
53 | {VeDirectDeviceCodeAR_code_12, VeDirectDeviceCodeAR_text_12},
54 | {VeDirectDeviceCodeAR_code_13, VeDirectDeviceCodeAR_text_13},
55 | };
56 | static const size_t VeDirectDeviceCodeARSize = sizeof(VeDirectDeviceCodeAR) / sizeof(VeDirectDeviceCodeAR[0]);
57 |
58 | // --- Mappingtabelle: VeDirectDeviceCodeOR ---
59 | static const char VeDirectDeviceCodeOR_code_0[] PROGMEM = "0X00000000";
60 | static const char VeDirectDeviceCodeOR_text_0[] PROGMEM = "None";
61 | static const char VeDirectDeviceCodeOR_code_1[] PROGMEM = "0X00000001";
62 | static const char VeDirectDeviceCodeOR_text_1[] PROGMEM = "No input power";
63 | static const char VeDirectDeviceCodeOR_code_2[] PROGMEM = "0X00000002";
64 | static const char VeDirectDeviceCodeOR_text_2[] PROGMEM = "Switched off (power switch)";
65 | static const char VeDirectDeviceCodeOR_code_3[] PROGMEM = "0X00000004";
66 | static const char VeDirectDeviceCodeOR_text_3[] PROGMEM = "Switched off (device mode register)";
67 | static const char VeDirectDeviceCodeOR_code_4[] PROGMEM = "0X00000008";
68 | static const char VeDirectDeviceCodeOR_text_4[] PROGMEM = "Remote input";
69 | static const char VeDirectDeviceCodeOR_code_5[] PROGMEM = "0X00000010";
70 | static const char VeDirectDeviceCodeOR_text_5[] PROGMEM = "Protection active";
71 | static const char VeDirectDeviceCodeOR_code_6[] PROGMEM = "0X00000020";
72 | static const char VeDirectDeviceCodeOR_text_6[] PROGMEM = "Paygo";
73 | static const char VeDirectDeviceCodeOR_code_7[] PROGMEM = "0X00000040";
74 | static const char VeDirectDeviceCodeOR_text_7[] PROGMEM = "BMS";
75 | static const char VeDirectDeviceCodeOR_code_8[] PROGMEM = "0X00000080";
76 | static const char VeDirectDeviceCodeOR_text_8[] PROGMEM = "Engine shutdown detection";
77 | static const char VeDirectDeviceCodeOR_code_9[] PROGMEM = "0X00000100";
78 | static const char VeDirectDeviceCodeOR_text_9[] PROGMEM = "Analysing input voltage";
79 |
80 | static const VeCodeEntry VeDirectDeviceCodeOR[] PROGMEM = {
81 | {VeDirectDeviceCodeOR_code_0, VeDirectDeviceCodeOR_text_0},
82 | {VeDirectDeviceCodeOR_code_1, VeDirectDeviceCodeOR_text_1},
83 | {VeDirectDeviceCodeOR_code_2, VeDirectDeviceCodeOR_text_2},
84 | {VeDirectDeviceCodeOR_code_3, VeDirectDeviceCodeOR_text_3},
85 | {VeDirectDeviceCodeOR_code_4, VeDirectDeviceCodeOR_text_4},
86 | {VeDirectDeviceCodeOR_code_5, VeDirectDeviceCodeOR_text_5},
87 | {VeDirectDeviceCodeOR_code_6, VeDirectDeviceCodeOR_text_6},
88 | {VeDirectDeviceCodeOR_code_7, VeDirectDeviceCodeOR_text_7},
89 | {VeDirectDeviceCodeOR_code_8, VeDirectDeviceCodeOR_text_8},
90 | {VeDirectDeviceCodeOR_code_9, VeDirectDeviceCodeOR_text_9},
91 | };
92 | static const size_t VeDirectDeviceCodeORSize = sizeof(VeDirectDeviceCodeOR) / sizeof(VeDirectDeviceCodeOR[0]);
93 |
94 | // --- Mappingtabelle: VeDirectDeviceCodeCS ---
95 | static const char VeDirectDeviceCodeCS_code_0[] PROGMEM = "0";
96 | static const char VeDirectDeviceCodeCS_text_0[] PROGMEM = "Off";
97 | static const char VeDirectDeviceCodeCS_code_1[] PROGMEM = "1";
98 | static const char VeDirectDeviceCodeCS_text_1[] PROGMEM = "Low power";
99 | static const char VeDirectDeviceCodeCS_code_2[] PROGMEM = "2";
100 | static const char VeDirectDeviceCodeCS_text_2[] PROGMEM = "Fault";
101 | static const char VeDirectDeviceCodeCS_code_3[] PROGMEM = "3";
102 | static const char VeDirectDeviceCodeCS_text_3[] PROGMEM = "Bulk";
103 | static const char VeDirectDeviceCodeCS_code_4[] PROGMEM = "4";
104 | static const char VeDirectDeviceCodeCS_text_4[] PROGMEM = "Absorption";
105 | static const char VeDirectDeviceCodeCS_code_5[] PROGMEM = "5";
106 | static const char VeDirectDeviceCodeCS_text_5[] PROGMEM = "Float";
107 | static const char VeDirectDeviceCodeCS_code_6[] PROGMEM = "6";
108 | static const char VeDirectDeviceCodeCS_text_6[] PROGMEM = "Storage";
109 | static const char VeDirectDeviceCodeCS_code_7[] PROGMEM = "7";
110 | static const char VeDirectDeviceCodeCS_text_7[] PROGMEM = "Equalize (manual)";
111 | static const char VeDirectDeviceCodeCS_code_8[] PROGMEM = "9";
112 | static const char VeDirectDeviceCodeCS_text_8[] PROGMEM = "Inverting";
113 | static const char VeDirectDeviceCodeCS_code_9[] PROGMEM = "11";
114 | static const char VeDirectDeviceCodeCS_text_9[] PROGMEM = "Power supply";
115 | static const char VeDirectDeviceCodeCS_code_10[] PROGMEM = "245";
116 | static const char VeDirectDeviceCodeCS_text_10[] PROGMEM = "Starting-up";
117 | static const char VeDirectDeviceCodeCS_code_11[] PROGMEM = "246";
118 | static const char VeDirectDeviceCodeCS_text_11[] PROGMEM = "Repeated absorption";
119 | static const char VeDirectDeviceCodeCS_code_12[] PROGMEM = "247";
120 | static const char VeDirectDeviceCodeCS_text_12[] PROGMEM = "Auto equalize / Recondition";
121 | static const char VeDirectDeviceCodeCS_code_13[] PROGMEM = "248";
122 | static const char VeDirectDeviceCodeCS_text_13[] PROGMEM = "BatterySafe";
123 | static const char VeDirectDeviceCodeCS_code_14[] PROGMEM = "252";
124 | static const char VeDirectDeviceCodeCS_text_14[] PROGMEM = "External Control";
125 |
126 | static const VeCodeEntry VeDirectDeviceCodeCS[] PROGMEM = {
127 | {VeDirectDeviceCodeCS_code_0, VeDirectDeviceCodeCS_text_0},
128 | {VeDirectDeviceCodeCS_code_1, VeDirectDeviceCodeCS_text_1},
129 | {VeDirectDeviceCodeCS_code_2, VeDirectDeviceCodeCS_text_2},
130 | {VeDirectDeviceCodeCS_code_3, VeDirectDeviceCodeCS_text_3},
131 | {VeDirectDeviceCodeCS_code_4, VeDirectDeviceCodeCS_text_4},
132 | {VeDirectDeviceCodeCS_code_5, VeDirectDeviceCodeCS_text_5},
133 | {VeDirectDeviceCodeCS_code_6, VeDirectDeviceCodeCS_text_6},
134 | {VeDirectDeviceCodeCS_code_7, VeDirectDeviceCodeCS_text_7},
135 | {VeDirectDeviceCodeCS_code_8, VeDirectDeviceCodeCS_text_8},
136 | {VeDirectDeviceCodeCS_code_9, VeDirectDeviceCodeCS_text_9},
137 | {VeDirectDeviceCodeCS_code_10, VeDirectDeviceCodeCS_text_10},
138 | {VeDirectDeviceCodeCS_code_11, VeDirectDeviceCodeCS_text_11},
139 | {VeDirectDeviceCodeCS_code_12, VeDirectDeviceCodeCS_text_12},
140 | {VeDirectDeviceCodeCS_code_13, VeDirectDeviceCodeCS_text_13},
141 | {VeDirectDeviceCodeCS_code_14, VeDirectDeviceCodeCS_text_14},
142 | };
143 | static const size_t VeDirectDeviceCodeCSSize = sizeof(VeDirectDeviceCodeCS) / sizeof(VeDirectDeviceCodeCS[0]);
144 |
145 | // --- Mappingtabelle: VeDirectDeviceCodeERR ---
146 | static const char VeDirectDeviceCodeERR_code_0[] PROGMEM = "0";
147 | static const char VeDirectDeviceCodeERR_text_0[] PROGMEM = "No error";
148 | static const char VeDirectDeviceCodeERR_code_1[] PROGMEM = "2";
149 | static const char VeDirectDeviceCodeERR_text_1[] PROGMEM = "Battery voltage too high";
150 | static const char VeDirectDeviceCodeERR_code_2[] PROGMEM = "17";
151 | static const char VeDirectDeviceCodeERR_text_2[] PROGMEM = "Charger temperature too high";
152 | static const char VeDirectDeviceCodeERR_code_3[] PROGMEM = "18";
153 | static const char VeDirectDeviceCodeERR_text_3[] PROGMEM = "Charger over current";
154 | static const char VeDirectDeviceCodeERR_code_4[] PROGMEM = "19";
155 | static const char VeDirectDeviceCodeERR_text_4[] PROGMEM = "Charger current reversed";
156 | static const char VeDirectDeviceCodeERR_code_5[] PROGMEM = "20";
157 | static const char VeDirectDeviceCodeERR_text_5[] PROGMEM = "Bulk time limit exceeded";
158 | static const char VeDirectDeviceCodeERR_code_6[] PROGMEM = "21";
159 | static const char VeDirectDeviceCodeERR_text_6[] PROGMEM = "Current sensor issue (sensor bias/sensor broken)";
160 | static const char VeDirectDeviceCodeERR_code_7[] PROGMEM = "26";
161 | static const char VeDirectDeviceCodeERR_text_7[] PROGMEM = "Terminals overheated";
162 | static const char VeDirectDeviceCodeERR_code_8[] PROGMEM = "28";
163 | static const char VeDirectDeviceCodeERR_text_8[] PROGMEM = "Converter issue";
164 | static const char VeDirectDeviceCodeERR_code_9[] PROGMEM = "33";
165 | static const char VeDirectDeviceCodeERR_text_9[] PROGMEM = "Input voltage too high (solar panel)";
166 | static const char VeDirectDeviceCodeERR_code_10[] PROGMEM = "34";
167 | static const char VeDirectDeviceCodeERR_text_10[] PROGMEM = "Input current too high (solar panel)";
168 | static const char VeDirectDeviceCodeERR_code_11[] PROGMEM = "38";
169 | static const char VeDirectDeviceCodeERR_text_11[] PROGMEM = "Input shutdown (excessive battery voltage)";
170 | static const char VeDirectDeviceCodeERR_code_12[] PROGMEM = "39";
171 | static const char VeDirectDeviceCodeERR_text_12[] PROGMEM = "Input shutdown (current flow during off mode)";
172 | static const char VeDirectDeviceCodeERR_code_13[] PROGMEM = "65";
173 | static const char VeDirectDeviceCodeERR_text_13[] PROGMEM = "Lost communication with one of devices";
174 | static const char VeDirectDeviceCodeERR_code_14[] PROGMEM = "66";
175 | static const char VeDirectDeviceCodeERR_text_14[] PROGMEM = "Synchronised charging device configuration issue";
176 | static const char VeDirectDeviceCodeERR_code_15[] PROGMEM = "67";
177 | static const char VeDirectDeviceCodeERR_text_15[] PROGMEM = "BMS connection lost";
178 | static const char VeDirectDeviceCodeERR_code_16[] PROGMEM = "68";
179 | static const char VeDirectDeviceCodeERR_text_16[] PROGMEM = "Network misconfigured";
180 | static const char VeDirectDeviceCodeERR_code_17[] PROGMEM = "116";
181 | static const char VeDirectDeviceCodeERR_text_17[] PROGMEM = "Factory calibration data lost";
182 | static const char VeDirectDeviceCodeERR_code_18[] PROGMEM = "117";
183 | static const char VeDirectDeviceCodeERR_text_18[] PROGMEM = "Invalid/incompatible firmware";
184 | static const char VeDirectDeviceCodeERR_code_19[] PROGMEM = "119";
185 | static const char VeDirectDeviceCodeERR_text_19[] PROGMEM = "User settings invalid";
186 |
187 | static const VeCodeEntry VeDirectDeviceCodeERR[] PROGMEM = {
188 | {VeDirectDeviceCodeERR_code_0, VeDirectDeviceCodeERR_text_0},
189 | {VeDirectDeviceCodeERR_code_1, VeDirectDeviceCodeERR_text_1},
190 | {VeDirectDeviceCodeERR_code_2, VeDirectDeviceCodeERR_text_2},
191 | {VeDirectDeviceCodeERR_code_3, VeDirectDeviceCodeERR_text_3},
192 | {VeDirectDeviceCodeERR_code_4, VeDirectDeviceCodeERR_text_4},
193 | {VeDirectDeviceCodeERR_code_5, VeDirectDeviceCodeERR_text_5},
194 | {VeDirectDeviceCodeERR_code_6, VeDirectDeviceCodeERR_text_6},
195 | {VeDirectDeviceCodeERR_code_7, VeDirectDeviceCodeERR_text_7},
196 | {VeDirectDeviceCodeERR_code_8, VeDirectDeviceCodeERR_text_8},
197 | {VeDirectDeviceCodeERR_code_9, VeDirectDeviceCodeERR_text_9},
198 | {VeDirectDeviceCodeERR_code_10, VeDirectDeviceCodeERR_text_10},
199 | {VeDirectDeviceCodeERR_code_11, VeDirectDeviceCodeERR_text_11},
200 | {VeDirectDeviceCodeERR_code_12, VeDirectDeviceCodeERR_text_12},
201 | {VeDirectDeviceCodeERR_code_13, VeDirectDeviceCodeERR_text_13},
202 | {VeDirectDeviceCodeERR_code_14, VeDirectDeviceCodeERR_text_14},
203 | {VeDirectDeviceCodeERR_code_15, VeDirectDeviceCodeERR_text_15},
204 | {VeDirectDeviceCodeERR_code_16, VeDirectDeviceCodeERR_text_16},
205 | {VeDirectDeviceCodeERR_code_17, VeDirectDeviceCodeERR_text_17},
206 | {VeDirectDeviceCodeERR_code_18, VeDirectDeviceCodeERR_text_18},
207 | {VeDirectDeviceCodeERR_code_19, VeDirectDeviceCodeERR_text_19},
208 | };
209 | static const size_t VeDirectDeviceCodeERRSize = sizeof(VeDirectDeviceCodeERR) / sizeof(VeDirectDeviceCodeERR[0]);
210 |
211 | // --- Mappingtabelle: VeDirectDeviceCodeMPPT ---
212 | static const char VeDirectDeviceCodeMPPT_code_0[] PROGMEM = "0";
213 | static const char VeDirectDeviceCodeMPPT_text_0[] PROGMEM = "Off";
214 | static const char VeDirectDeviceCodeMPPT_code_1[] PROGMEM = "1";
215 | static const char VeDirectDeviceCodeMPPT_text_1[] PROGMEM = "Voltage or current limited";
216 | static const char VeDirectDeviceCodeMPPT_code_2[] PROGMEM = "2";
217 | static const char VeDirectDeviceCodeMPPT_text_2[] PROGMEM = "MPP Tracker active";
218 |
219 | static const VeCodeEntry VeDirectDeviceCodeMPPT[] PROGMEM = {
220 | {VeDirectDeviceCodeMPPT_code_0, VeDirectDeviceCodeMPPT_text_0},
221 | {VeDirectDeviceCodeMPPT_code_1, VeDirectDeviceCodeMPPT_text_1},
222 | {VeDirectDeviceCodeMPPT_code_2, VeDirectDeviceCodeMPPT_text_2},
223 | };
224 | static const size_t VeDirectDeviceCodeMPPTSize = sizeof(VeDirectDeviceCodeMPPT) / sizeof(VeDirectDeviceCodeMPPT[0]);
225 |
226 |
227 |
228 |
229 |
230 |
231 | /*
232 |
233 |
234 | // AR (Alarm_code)
235 | static const char * VeDirectDeviceCodeAR[][2] PROGMEM{
236 | {"1", "Low Voltage"},
237 | {"2", "High Voltage"},
238 | {"4", "Low SOC"},
239 | {"8", "Low Starter Voltage"},
240 | {"16", "High Starter Voltage"},
241 | {"32", "Low Temperature"},
242 | {"64", "High Temperature"},
243 | {"128", "Mid Voltage"},
244 | {"256", "Overload"},
245 | {"512", "DC-ripple"},
246 | {"1024", "Low V AC out"},
247 | {"2048", "LHigh V AC out"},
248 | {"4096", "Short Circuit"},
249 | {"8192", "BMS Lockout"}};
250 |
251 | // OR (off_reason)
252 | static const char * VeDirectDeviceCodeOR[][2] PROGMEM{
253 | {"0X00000000", "None"},
254 | {"0X00000001", "No input power"},
255 | {"0X00000002", "Switched off (power switch)"},
256 | {"0X00000004", "Switched off (device mode register)"},
257 | {"0X00000008", "Remote input"},
258 | {"0X00000010", "Protection active"},
259 | {"0X00000020", "Paygo"},
260 | {"0X00000040", "BMS"},
261 | {"0X00000080", "Engine shutdown detection"},
262 | {"0X00000100", "Analysing input voltage"},
263 | };
264 | // CS (operation_state)
265 | static const char * VeDirectDeviceCodeCS[][2] PROGMEM{
266 | {"0", "Off"},
267 | {"1", "Low power"},
268 | {"2", "Fault"},
269 | {"3", "Bulk"},
270 | {"4", "Absorption"},
271 | {"5", "Float"},
272 | {"6", "Storage"},
273 | {"7", "Equalize (manual)"},
274 | {"9", "Inverting"},
275 | {"11", "Power supply"},
276 | {"245", "Starting-up"},
277 | {"246", "Repeated absorption"},
278 | {"247", "Auto equalize / Recondition"},
279 | {"248", "BatterySafe"},
280 | {"252", "External Control"}};
281 |
282 | // ERR (Current_error)
283 | static const char * VeDirectDeviceCodeERR[][2] PROGMEM{
284 | {"0", "No error"},
285 | {"2", "Battery voltage too high"},
286 | {"17", "Charger temperature too high"},
287 | {"18", "Charger over current"},
288 | {"19", "Charger current reversed"},
289 | {"20", "Bulk time limit exceeded"},
290 | {"21", "Current sensor issue (sensor bias/sensor broken)"},
291 | {"26", "Terminals overheated"},
292 | {"28", "Converter issue"},
293 | {"33", "Input voltage too high (solar panel)"},
294 | {"34", "Input current too high (solar panel)"},
295 | {"38", "Input shutdown (excessive battery voltage)"},
296 | {"39", "Input shutdown (current flow during off mode)"},
297 | {"65", "Lost communication with one of devices"},
298 | {"66", "Synchronised charging device configuration issue"},
299 | {"67", "BMS connection lost"},
300 | {"68", "Network misconfigured"},
301 | {"116", "Factory calibration data lost"},
302 | {"117", "Invalid/incompatible firmware"},
303 | {"119", "User settings invalid"}};
304 |
305 | // MPPT (Tracker_operation_mode)
306 | static const char * VeDirectDeviceCodeMPPT[][2] PROGMEM{
307 | {"0", "Off"},
308 | {"1", "Voltage or current limited"},
309 | {"2", "MPP Tracker active"}}; */
--------------------------------------------------------------------------------
/src/VeDirectDeviceList.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | struct VeDeviceEntry {
4 | uint16_t id;
5 | const char* name;
6 | };
7 | const char name_203[] PROGMEM = "BMV-700";
8 | const char name_204[] PROGMEM = "BMV-702";
9 | const char name_205[] PROGMEM = "BMV-700H";
10 | const char name_0300[] PROGMEM = "BlueSolar MPPT 70|15";
11 | const char name_A040[] PROGMEM = "BlueSolar MPPT 75|50";
12 | const char name_A041[] PROGMEM = "BlueSolar MPPT 150|35";
13 | const char name_A042[] PROGMEM = "BlueSolar MPPT 75|15";
14 | const char name_A043[] PROGMEM = "BlueSolar MPPT 100|15";
15 | const char name_A044[] PROGMEM = "BlueSolar MPPT 100|30";
16 | const char name_A045[] PROGMEM = "BlueSolar MPPT 100|50";
17 | const char name_A046[] PROGMEM = "BlueSolar MPPT 150|70";
18 | const char name_A047[] PROGMEM = "BlueSolar MPPT 150|100 ";
19 | const char name_A049[] PROGMEM = "BlueSolar MPPT 100|50 rev2";
20 | const char name_A04A[] PROGMEM = "BlueSolar MPPT 100|30 rev2";
21 | const char name_A04B[] PROGMEM = "BlueSolar MPPT 150|35";
22 | const char name_A04C[] PROGMEM = "BlueSolar MPPT 75|10";
23 | const char name_A04D[] PROGMEM = "BlueSolar MPPT 150|45";
24 | const char name_A04E[] PROGMEM = "BlueSolar MPPT 150|60";
25 | const char name_A04F[] PROGMEM = "BlueSolar MPPT 150|85";
26 | const char name_A050[] PROGMEM = "SmartSolar MPPT 250|100";
27 | const char name_A051[] PROGMEM = "SmartSolar MPPT 150|100";
28 | const char name_A052[] PROGMEM = "SmartSolar MPPT 150|85";
29 | const char name_A053[] PROGMEM = "SmartSolar MPPT 75|15";
30 | const char name_A054[] PROGMEM = "SmartSolar MPPT 75|10";
31 | const char name_A055[] PROGMEM = "SmartSolar MPPT 100|15";
32 | const char name_A056[] PROGMEM = "SmartSolar MPPT 100|30";
33 | const char name_A057[] PROGMEM = "SmartSolar MPPT 100|50";
34 | const char name_A058[] PROGMEM = "SmartSolar MPPT 150|35";
35 | const char name_A059[] PROGMEM = "SmartSolar MPPT 150|100 rev2";
36 | const char name_A05A[] PROGMEM = "SmartSolar MPPT 150|85 rev2";
37 | const char name_A05B[] PROGMEM = "SmartSolar MPPT 250|70";
38 | const char name_A05C[] PROGMEM = "SmartSolar MPPT 250|85";
39 | const char name_A05D[] PROGMEM = "SmartSolar MPPT 250|60";
40 | const char name_A05E[] PROGMEM = "SmartSolar MPPT 250|45";
41 | const char name_A05F[] PROGMEM = "SmartSolar MPPT 100|20";
42 | const char name_A060[] PROGMEM = "SmartSolar MPPT 100|20 48V";
43 | const char name_A061[] PROGMEM = "SmartSolar MPPT 150|45";
44 | const char name_A062[] PROGMEM = "SmartSolar MPPT 150|60";
45 | const char name_A063[] PROGMEM = "SmartSolar MPPT 150|70";
46 | const char name_A064[] PROGMEM = "SmartSolar MPPT 250|85 rev2";
47 | const char name_A065[] PROGMEM = "SmartSolar MPPT 250|100 rev2";
48 | const char name_A066[] PROGMEM = "BlueSolar MPPT 100|20";
49 | const char name_A067[] PROGMEM = "BlueSolar MPPT 100|20 48V";
50 | const char name_A068[] PROGMEM = "SmartSolar MPPT 250|60 rev2";
51 | const char name_A069[] PROGMEM = "SmartSolar MPPT 250|70 rev2";
52 | const char name_A06A[] PROGMEM = "SmartSolar MPPT 150|45 rev2";
53 | const char name_A06B[] PROGMEM = "SmartSolar MPPT 150|60 rev2";
54 | const char name_A06C[] PROGMEM = "SmartSolar MPPT 150|70 rev2";
55 | const char name_A06D[] PROGMEM = "SmartSolar MPPT 150|85 rev3";
56 | const char name_A06E[] PROGMEM = "SmartSolar MPPT 150|100 rev3";
57 | const char name_A06F[] PROGMEM = "BlueSolar MPPT 150|45 rev2";
58 | const char name_A070[] PROGMEM = "BlueSolar MPPT 150|60 rev2";
59 | const char name_A071[] PROGMEM = "BlueSolar MPPT 150|70 rev2";
60 | const char name_A072[] PROGMEM = "BlueSolar MPPT 150/45 rev3";
61 | const char name_A073[] PROGMEM = "SmartSolar MPPT 150/45 rev3";
62 | const char name_A074[] PROGMEM = "SmartSolar MPPT 75/10 rev2";
63 | const char name_A075[] PROGMEM = "SmartSolar MPPT 75/15 rev2";
64 | const char name_A076[] PROGMEM = "BlueSolar MPPT 100/30 rev3";
65 | const char name_A077[] PROGMEM = "BlueSolar MPPT 100/50 rev3";
66 | const char name_A078[] PROGMEM = "BlueSolar MPPT 150/35 rev3";
67 | const char name_A079[] PROGMEM = "BlueSolar MPPT 75/10 rev2";
68 | const char name_A07A[] PROGMEM = "BlueSolar MPPT 75/15 rev2";
69 | const char name_A07B[] PROGMEM = "BlueSolar MPPT 100/15 rev2";
70 | const char name_A07C[] PROGMEM = "BlueSolar MPPT 75/10 rev3";
71 | const char name_A07D[] PROGMEM = "BlueSolar MPPT 75/15 rev3";
72 | const char name_A07E[] PROGMEM = "SmartSolar MPPT 100/30 12V";
73 | const char name_A07F[] PROGMEM = "All-In-1 SmartSolar MPPT 75/15 12V";
74 | const char name_A102[] PROGMEM = "SmartSolar MPPT VE.Can 150/70";
75 | const char name_A103[] PROGMEM = "SmartSolar MPPT VE.Can 150/45";
76 | const char name_A104[] PROGMEM = "SmartSolar MPPT VE.Can 150/60";
77 | const char name_A105[] PROGMEM = "SmartSolar MPPT VE.Can 150/85";
78 | const char name_A106[] PROGMEM = "SmartSolar MPPT VE.Can 150/100";
79 | const char name_A107[] PROGMEM = "SmartSolar MPPT VE.Can 250/45";
80 | const char name_A108[] PROGMEM = "SmartSolar MPPT VE.Can 250/60";
81 | const char name_A109[] PROGMEM = "SmartSolar MPPT VE.Can 250/70";
82 | const char name_A10A[] PROGMEM = "SmartSolar MPPT VE.Can 250/85";
83 | const char name_A10B[] PROGMEM = "SmartSolar MPPT VE.Can 250/100";
84 | const char name_A10C[] PROGMEM = "SmartSolar MPPT VE.Can 150/70 rev2";
85 | const char name_A10D[] PROGMEM = "SmartSolar MPPT VE.Can 150/85 rev2";
86 | const char name_A10E[] PROGMEM = "SmartSolar MPPT VE.Can 150/100 rev2";
87 | const char name_A10F[] PROGMEM = "BlueSolar MPPT VE.Can 150/100";
88 | const char name_A112[] PROGMEM = "BlueSolar MPPT VE.Can 250/70";
89 | const char name_A113[] PROGMEM = "BlueSolar MPPT VE.Can 250/100";
90 | const char name_A114[] PROGMEM = "SmartSolar MPPT VE.Can 250/70 rev2";
91 | const char name_A115[] PROGMEM = "SmartSolar MPPT VE.Can 250/100 rev2";
92 | const char name_A116[] PROGMEM = "SmartSolar MPPT VE.Can 250/85 rev2";
93 | const char name_A117[] PROGMEM = "BlueSolar MPPT VE.Can 150/100 rev2";
94 | const char name_A201[] PROGMEM = "Phoenix Inverter 12V 250VA 230V";
95 | const char name_A202[] PROGMEM = "Phoenix Inverter 24V 250VA 230V";
96 | const char name_A204[] PROGMEM = "Phoenix Inverter 48V 250VA 230V";
97 | const char name_A211[] PROGMEM = "Phoenix Inverter 12V 375VA 230V";
98 | const char name_A212[] PROGMEM = "Phoenix Inverter 24V 375VA 230V";
99 | const char name_A214[] PROGMEM = "Phoenix Inverter 48V 375VA 230V";
100 | const char name_A221[] PROGMEM = "Phoenix Inverter 12V 500VA 230V";
101 | const char name_A222[] PROGMEM = "Phoenix Inverter 24V 500VA 230V";
102 | const char name_A224[] PROGMEM = "Phoenix Inverter 48V 500VA 230V";
103 | const char name_A231[] PROGMEM = "Phoenix Inverter 12V 250VA 230V";
104 | const char name_A232[] PROGMEM = "Phoenix Inverter 24V 250VA 230V";
105 | const char name_A234[] PROGMEM = "Phoenix Inverter 48V 250VA 230V";
106 | const char name_A239[] PROGMEM = "Phoenix Inverter 12V 250VA 120V";
107 | const char name_A23A[] PROGMEM = "Phoenix Inverter 24V 250VA 120V";
108 | const char name_A23C[] PROGMEM = "Phoenix Inverter 48V 250VA 120V";
109 | const char name_A241[] PROGMEM = "Phoenix Inverter 12V 375VA 230V";
110 | const char name_A242[] PROGMEM = "Phoenix Inverter 24V 375VA 230V";
111 | const char name_A244[] PROGMEM = "Phoenix Inverter 48V 375VA 230V";
112 | const char name_A249[] PROGMEM = "Phoenix Inverter 12V 375VA 120V";
113 | const char name_A24A[] PROGMEM = "Phoenix Inverter 24V 375VA 120V";
114 | const char name_A24C[] PROGMEM = "Phoenix Inverter 48V 375VA 120V";
115 | const char name_A251[] PROGMEM = "Phoenix Inverter 12V 500VA 230V";
116 | const char name_A252[] PROGMEM = "Phoenix Inverter 24V 500VA 230V";
117 | const char name_A254[] PROGMEM = "Phoenix Inverter 48V 500VA 230V";
118 | const char name_A259[] PROGMEM = "Phoenix Inverter 12V 500VA 120V";
119 | const char name_A25A[] PROGMEM = "Phoenix Inverter 24V 500VA 120V";
120 | const char name_A25C[] PROGMEM = "Phoenix Inverter 48V 500VA 120V";
121 | const char name_A261[] PROGMEM = "Phoenix Inverter 12V 800VA 230V";
122 | const char name_A262[] PROGMEM = "Phoenix Inverter 24V 800VA 230V";
123 | const char name_A264[] PROGMEM = "Phoenix Inverter 48V 800VA 230V";
124 | const char name_A269[] PROGMEM = "Phoenix Inverter 12V 800VA 120V";
125 | const char name_A26A[] PROGMEM = "Phoenix Inverter 24V 800VA 120V";
126 | const char name_A26C[] PROGMEM = "Phoenix Inverter 48V 800VA 120V";
127 | const char name_A271[] PROGMEM = "Phoenix Inverter 12V 1200VA 230V";
128 | const char name_A272[] PROGMEM = "Phoenix Inverter 24V 1200VA 230V";
129 | const char name_A274[] PROGMEM = "Phoenix Inverter 48V 1200VA 230V";
130 | const char name_A279[] PROGMEM = "Phoenix Inverter 12V 1200VA 120V";
131 | const char name_A27A[] PROGMEM = "Phoenix Inverter 24V 1200VA 120V";
132 | const char name_A27C[] PROGMEM = "Phoenix Inverter 48V 1200VA 120V";
133 | const char name_A281[] PROGMEM = "Phoenix Inverter 12V 1600VA 230V";
134 | const char name_A282[] PROGMEM = "Phoenix Inverter 24V 1600VA 230V";
135 | const char name_A284[] PROGMEM = "Phoenix Inverter 48V 1600VA 230V";
136 | const char name_A291[] PROGMEM = "Phoenix Inverter 12V 2000VA 230V";
137 | const char name_A292[] PROGMEM = "Phoenix Inverter 24V 2000VA 230V";
138 | const char name_A294[] PROGMEM = "Phoenix Inverter 48V 2000VA 230V";
139 | const char name_A2A1[] PROGMEM = "Phoenix Inverter 12V 3000VA 230V";
140 | const char name_A2A2[] PROGMEM = "Phoenix Inverter 24V 3000VA 230V";
141 | const char name_A2A4[] PROGMEM = "Phoenix Inverter 48V 3000VA 230V";
142 | const char name_A340[] PROGMEM = "Phoenix Smart IP43 Charger 12|50 (1+1)";
143 | const char name_A341[] PROGMEM = "Phoenix Smart IP43 Charger 12|50 (3)";
144 | const char name_A342[] PROGMEM = "Phoenix Smart IP43 Charger 24|25 (1+1)";
145 | const char name_A343[] PROGMEM = "Phoenix Smart IP43 Charger 24|25 (3)";
146 | const char name_A344[] PROGMEM = "Phoenix Smart IP43 Charger 12|30 (1+1)";
147 | const char name_A345[] PROGMEM = "Phoenix Smart IP43 Charger 12|30 (3)";
148 | const char name_A346[] PROGMEM = "Phoenix Smart IP43 Charger 24|16 (1+1)";
149 | const char name_A347[] PROGMEM = "Phoenix Smart IP43 Charger 24|16 (3)";
150 | const char name_A381[] PROGMEM = "BMV-712 Smart";
151 | const char name_A382[] PROGMEM = "BMV-710H Smart";
152 | const char name_A383[] PROGMEM = "BMV-712 Smart Rev2";
153 | const char name_C038[] PROGMEM = "SmartShunt 300A/50mV";
154 | const char name_A389[] PROGMEM = "SmartShunt 500A/50mV";
155 | const char name_A38A[] PROGMEM = "SmartShunt 1000A/50mV";
156 | const char name_A38B[] PROGMEM = "SmartShunt 2000A/50mV";
157 | const char name_A3F0[] PROGMEM = "Smart BuckBoost 12V/12V-50A";
158 | const char name_C030[] PROGMEM = "SmartShunt 500A/50mV IP65";
159 | const char name_9999[] PROGMEM = "The migthy simluator";
160 |
161 | const VeDeviceEntry VeDeviceList[] PROGMEM = {
162 | {0x0203, name_203},
163 | {0x0204, name_204},
164 | {0x0205, name_205},
165 | {0x0300, name_0300},
166 | {0x9999, name_9999},
167 | {0xA040, name_A040},
168 | {0xA041, name_A041},
169 | {0xA042, name_A042},
170 | {0xA043, name_A043},
171 | {0xA044, name_A044},
172 | {0xA045, name_A045},
173 | {0xA046, name_A046},
174 | {0xA047, name_A047},
175 | {0xA049, name_A049},
176 | {0xA04A, name_A04A},
177 | {0xA04B, name_A04B},
178 | {0xA04C, name_A04C},
179 | {0xA04D, name_A04D},
180 | {0xA04E, name_A04E},
181 | {0xA04F, name_A04F},
182 | {0xA050, name_A050},
183 | {0xA051, name_A051},
184 | {0xA052, name_A052},
185 | {0xA053, name_A053},
186 | {0xA054, name_A054},
187 | {0xA055, name_A055},
188 | {0xA056, name_A056},
189 | {0xA057, name_A057},
190 | {0xA058, name_A058},
191 | {0xA059, name_A059},
192 | {0xA05A, name_A05A},
193 | {0xA05B, name_A05B},
194 | {0xA05C, name_A05C},
195 | {0xA05D, name_A05D},
196 | {0xA05E, name_A05E},
197 | {0xA05F, name_A05F},
198 | {0xA060, name_A060},
199 | {0xA061, name_A061},
200 | {0xA062, name_A062},
201 | {0xA063, name_A063},
202 | {0xA064, name_A064},
203 | {0xA065, name_A065},
204 | {0xA066, name_A066},
205 | {0xA067, name_A067},
206 | {0xA068, name_A068},
207 | {0xA069, name_A069},
208 | {0xA06A, name_A06A},
209 | {0xA06B, name_A06B},
210 | {0xA06C, name_A06C},
211 | {0xA06D, name_A06D},
212 | {0xA06E, name_A06E},
213 | {0xA06F, name_A06F},
214 | {0xA070, name_A070},
215 | {0xA071, name_A071},
216 | {0xA072, name_A072},
217 | {0xA073, name_A073},
218 | {0xA074, name_A074},
219 | {0xA075, name_A075},
220 | {0xA076, name_A076},
221 | {0xA077, name_A077},
222 | {0xA078, name_A078},
223 | {0xA079, name_A079},
224 | {0xA07A, name_A07A},
225 | {0xA07B, name_A07B},
226 | {0xA07C, name_A07C},
227 | {0xA07D, name_A07D},
228 | {0xA07E, name_A07E},
229 | {0xA07F, name_A07F},
230 | {0xA102, name_A102},
231 | {0xA103, name_A103},
232 | {0xA104, name_A104},
233 | {0xA105, name_A105},
234 | {0xA106, name_A106},
235 | {0xA107, name_A107},
236 | {0xA108, name_A108},
237 | {0xA109, name_A109},
238 | {0xA10A, name_A10A},
239 | {0xA10B, name_A10B},
240 | {0xA10C, name_A10C},
241 | {0xA10D, name_A10D},
242 | {0xA10E, name_A10E},
243 | {0xA10F, name_A10F},
244 | {0xA112, name_A112},
245 | {0xA113, name_A113},
246 | {0xA114, name_A114},
247 | {0xA115, name_A115},
248 | {0xA116, name_A116},
249 | {0xA117, name_A117},
250 | {0xA201, name_A201},
251 | {0xA202, name_A202},
252 | {0xA204, name_A204},
253 | {0xA211, name_A211},
254 | {0xA212, name_A212},
255 | {0xA214, name_A214},
256 | {0xA221, name_A221},
257 | {0xA222, name_A222},
258 | {0xA224, name_A224},
259 | {0xA231, name_A231},
260 | {0xA232, name_A232},
261 | {0xA234, name_A234},
262 | {0xA239, name_A239},
263 | {0xA23A, name_A23A},
264 | {0xA23C, name_A23C},
265 | {0xA241, name_A241},
266 | {0xA242, name_A242},
267 | {0xA244, name_A244},
268 | {0xA249, name_A249},
269 | {0xA24A, name_A24A},
270 | {0xA24C, name_A24C},
271 | {0xA251, name_A251},
272 | {0xA252, name_A252},
273 | {0xA254, name_A254},
274 | {0xA259, name_A259},
275 | {0xA25A, name_A25A},
276 | {0xA25C, name_A25C},
277 | {0xA261, name_A261},
278 | {0xA262, name_A262},
279 | {0xA264, name_A264},
280 | {0xA269, name_A269},
281 | {0xA26A, name_A26A},
282 | {0xA26C, name_A26C},
283 | {0xA271, name_A271},
284 | {0xA272, name_A272},
285 | {0xA274, name_A274},
286 | {0xA279, name_A279},
287 | {0xA27A, name_A27A},
288 | {0xA27C, name_A27C},
289 | {0xA281, name_A281},
290 | {0xA282, name_A282},
291 | {0xA284, name_A284},
292 | {0xA291, name_A291},
293 | {0xA292, name_A292},
294 | {0xA294, name_A294},
295 | {0xA2A1, name_A2A1},
296 | {0xA2A2, name_A2A2},
297 | {0xA2A4, name_A2A4},
298 | {0xA340, name_A340},
299 | {0xA341, name_A341},
300 | {0xA342, name_A342},
301 | {0xA343, name_A343},
302 | {0xA344, name_A344},
303 | {0xA345, name_A345},
304 | {0xA346, name_A346},
305 | {0xA347, name_A347},
306 | {0xA381, name_A381},
307 | {0xA382, name_A382},
308 | {0xA383, name_A383},
309 | {0xA389, name_A389},
310 | {0xA38A, name_A38A},
311 | {0xA38B, name_A38B},
312 | {0xA3F0, name_A3F0},
313 | {0xC030, name_C030},
314 | {0xC038, name_C038},
315 | };
316 |
317 | const size_t VeDeviceListSize = sizeof(VeDeviceList) / sizeof(VeDeviceList[0]);
318 |
--------------------------------------------------------------------------------
/src/VeDirectFrameHandler.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "VeDirectFrameHandler.h"
3 |
4 | extern void writeLog(const char* format, ...);
5 | // The name of the record that contains the checksum.
6 | static constexpr char checksumTagName[] = "CHECKSUM";
7 |
8 | /*
9 | * rxData
10 | * This function is called by the application which passes a byte of serial data
11 | * It is unchanged from Victron's example code
12 | */
13 | void VeDirectFrameHandler::rxData(uint8_t inbyte)
14 | {
15 | //Serial.write(inbyte);
16 | if ((inbyte == ':') && (mState != CHECKSUM))
17 | {
18 | mState = RECORD_HEX;
19 | }
20 | if (mState != RECORD_HEX)
21 | {
22 | mChecksum += inbyte;
23 | }
24 | inbyte = toupper(inbyte);
25 |
26 | switch (mState)
27 | {
28 | case IDLE:
29 | /* wait for \n of the start of an record */
30 | switch (inbyte)
31 | {
32 | case '\n':
33 | mState = RECORD_BEGIN;
34 | break;
35 | case '\r': /* Skip */
36 | default:
37 | break;
38 | }
39 | break;
40 | case RECORD_BEGIN:
41 | mTextPointer = mName;
42 | *mTextPointer++ = inbyte;
43 | mState = RECORD_NAME;
44 | break;
45 | case RECORD_NAME:
46 | // The record name is being received, terminated by a \t
47 | switch (inbyte)
48 | {
49 | case '\t':
50 | // the Checksum record indicates a EOR
51 | if (mTextPointer < (mName + sizeof(mName)))
52 | {
53 | *mTextPointer = 0; /* Zero terminate */
54 | //recordNameCallback(true, mName);
55 | if (strcmp(mName, checksumTagName) == 0)
56 | {
57 | mState = CHECKSUM;
58 | break;
59 | }
60 | } else {
61 | //recordNameCallback(false, nullptr);
62 | }
63 | mTextPointer = mValue; /* Reset value pointer */
64 | mState = RECORD_VALUE;
65 | break;
66 |
67 |
68 | default:
69 | // add byte to name, but do no overflow
70 | if (mTextPointer < (mName + sizeof(mName)))
71 | *mTextPointer++ = inbyte;
72 | break;
73 | }
74 | break;
75 | case RECORD_VALUE:
76 | // The record value is being received. The \r indicates a new record.
77 | switch (inbyte)
78 | {
79 | case '\n':
80 | // forward record, only if it could be stored completely
81 | if (mTextPointer < (mValue + sizeof(mValue)))
82 | {
83 | *mTextPointer = 0; // make zero ended
84 | //recordValueCallback(true, mValue);
85 | textRxEvent(mName, mValue);
86 | } else {
87 | //recordValueCallback(false, nullptr);
88 | }
89 | mState = RECORD_BEGIN;
90 | break;
91 | case '\r': /* Skip */
92 | break;
93 | default:
94 | // add byte to value, but do no overflow
95 | if (mTextPointer < (mValue + sizeof(mValue)))
96 | *mTextPointer++ = inbyte;
97 | break;
98 | }
99 | break;
100 | case CHECKSUM:
101 | {
102 | //checksumCallback(mChecksum);
103 | bool valid = mChecksum == 0;
104 | mChecksum = 0;
105 | mState = IDLE;
106 | frameEndEvent(valid);
107 | break;
108 | }
109 | case RECORD_HEX:
110 | if (hexRxEvent(inbyte))
111 | {
112 | //hexFrameCallback();
113 | mChecksum = 0;
114 | mState = IDLE;
115 | }
116 | break;
117 | }
118 | }
119 |
120 | /*
121 | * textRxEvent
122 | * This function is called every time a new name/value is successfully parsed. It writes the values to the temporary buffer.
123 | */
124 | void VeDirectFrameHandler::textRxEvent(char *mName, char *mValue)
125 | {
126 | strcpy(tempName[frameIndex], mName); // copy name to temporary buffer
127 | strcpy(tempValue[frameIndex], mValue); // copy value to temporary buffer
128 | frameIndex++;
129 | }
130 |
131 | /*
132 | * frameEndEvent
133 | * This function is called at the end of the received frame. If the checksum is valid, the temp buffer is read line by line.
134 | * If the name exists in the public buffer, the new value is copied to the public buffer. If not, a new name/value entry
135 | * is created in the public buffer.
136 | */
137 | void VeDirectFrameHandler::frameEndEvent(bool valid)
138 | {
139 | if (valid)
140 | {
141 | for (int i = 0; i < frameIndex; i++)
142 | {
143 | // read each name already in the temp buffer
144 | bool nameExists = false;
145 | for (size_t j = 0; j < veEnd; j++)
146 | {
147 | // compare to existing names in the public buffer
148 | if (strcmp(tempName[i], veName[j]) == 0)
149 | {
150 | strcpy(veValue[j], tempValue[i]); // overwrite tempValue in the public buffer
151 | nameExists = true;
152 | break;
153 | }
154 | }
155 | if (!nameExists)
156 | {
157 | strcpy(veName[veEnd], tempName[i]); // write new Name to public buffer
158 | strcpy(veValue[veEnd], tempValue[i]); // write new Value to public buffer
159 | veEnd++; // increment end of public buffer
160 | if (veEnd >= buffLen)
161 | { // stop any buffer overrun
162 | veEnd = buffLen - 1;
163 | }
164 | }
165 | }
166 | writeLog("\nCRC OK");
167 | veErrorCount = 0;
168 | requestCallback(); // call the callback to do other things with the new data
169 | } else {
170 | veErrorCount++;
171 | writeLog("\nCRC wrong");
172 | }
173 | frameIndex = 0; // reset frame
174 | veError = veErrorCount < veErrorTol ? false : true; //set the crc error flag
175 | }
176 |
177 | bool VeDirectFrameHandler::hexRxEvent(uint8_t inbyte)
178 | {
179 | return inbyte == '\n';
180 | }
181 |
182 | void VeDirectFrameHandler::callback(std::function func) // callback function when finnish request
183 | {
184 | requestCallback = func;
185 | }
186 |
--------------------------------------------------------------------------------
/src/VeDirectFrameHandler.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | //#include
4 | //#include
5 | #ifndef FRAMEHANDLER_H_
6 | #define FRAMEHANDLER_H_
7 |
8 | class VeDirectFrameHandler
9 | {
10 | public:
11 | static const size_t frameLen = 22; // VE.Direct Protocol: max frame size is 22
12 | static const size_t nameLen = 9; // VE.Direct Protocol: max name size is 9 including /0
13 | static const size_t valueLen = 33; // VE.Direct Protocol: max value size is 33 including /0
14 | static const size_t buffLen = 40; // Maximum number of lines possible from the device. Current protocol shows this to be the
15 |
16 |
17 |
18 |
19 | //VeDirectFrameHandler();
20 | //~VeDirectFrameHandler();
21 | //void setErrorHandler(logFunction f) { logEF = f; } // error handler
22 | void rxData(uint8_t inbyte); // byte of serial data to be passed by the application
23 | //void addHexCallback(hexFrameCallback, void *); // add function called back when hex frame is ready (sync or async)
24 | void callback(std::function func); // callback function
25 | std::function requestCallback;
26 | /*
27 | constexpr VeDirectFrameHandler() = default;
28 | void rxData(uint8_t inbyte); // byte of serial data to be passed by the application
29 | std::function recordNameCallback = [](bool, char const *){ };
30 | std::function recordValueCallback = [](bool, char const *){ };
31 | std::function checksumCallback = [](uint8_t){ };
32 | std::function hexFrameCallback = [](){ };
33 | std::function frameEndEventCallback = [](bool){ };
34 |
35 | void callback(std::function func); // callback function
36 | std::function requestCallback;
37 | */
38 |
39 | char veName[buffLen][nameLen] = { }; // public buffer for received names
40 | char veValue[buffLen][valueLen] = { }; // public buffer for received values
41 |
42 | size_t veEnd = 0; // current size (end) of the public buffer
43 | int veErrorTol = 10; // error counter for crc, if higher than error tollerance
44 | int veErrorCount = 0; // crc error counter
45 | bool veError = true; // error flag for crc
46 |
47 | private:
48 | enum States { // state machine
49 | IDLE,
50 | RECORD_BEGIN,
51 | RECORD_NAME,
52 | RECORD_VALUE,
53 | CHECKSUM,
54 | RECORD_HEX
55 | };
56 |
57 | int mState = IDLE; // current state
58 | uint8_t mChecksum = 0; // checksum value
59 | int frameIndex = 0; // which line of the frame are we on
60 |
61 | char * mTextPointer = nullptr; // pointer to the private buffer we're writing to, name or value
62 |
63 | char mName[nameLen] = { }; // buffer for the field name
64 | char mValue[valueLen] = { }; // buffer for the field value
65 | char tempName[frameLen][nameLen] = { }; // private buffer for received names
66 | char tempValue[frameLen][valueLen] = { }; // private buffer for received values
67 |
68 | void textRxEvent(char *, char *);
69 | void frameEndEvent(bool);
70 | bool hexRxEvent(uint8_t);
71 | };
72 | #endif // FRAMEHANDLER_H_
73 |
--------------------------------------------------------------------------------
/src/htmlProzessor.h:
--------------------------------------------------------------------------------
1 | // #include
2 | String htmlProcessor(const String &var)
3 | {
4 | extern Settings _settings;
5 | if (var == F("pre_head_template"))
6 | return (FPSTR(HTML_HEAD));
7 | if (var == F("pre_foot_template"))
8 | return (FPSTR(HTML_FOOT));
9 | if (var == F("pre_software_version"))
10 | return (SOFTWARE_VERSION);
11 | if (var == F("pre_swversion"))
12 | return (SWVERSION);
13 | if (var == F("pre_flash_size"))
14 | return (String(FlashSize).c_str());
15 | if (var == F("pre_esp01"))
16 | return (String(ESP01).c_str());
17 | if (var == F("pre_device_name"))
18 | return (_settings.data.deviceName);
19 | if (var == F("pre_mqtt_server"))
20 | return (_settings.data.mqttServer);
21 | if (var == F("pre_mqtt_port"))
22 | return (String(_settings.data.mqttPort).c_str());
23 | if (var == F("pre_mqtt_user"))
24 | return (_settings.data.mqttUser);
25 | if (var == F("pre_mqtt_pass"))
26 | return (_settings.data.mqttPassword);
27 | if (var == F("pre_mqtt_topic"))
28 | return (_settings.data.mqttTopic);
29 | if (var == F("pre_mqtt_refresh"))
30 | return (String(_settings.data.mqttRefresh).c_str());
31 | if (var == F("pre_mqtt_json"))
32 | return (_settings.data.mqttJson ? "checked" : "");
33 | if (var == F("pre_mqtt_mqtttrigger"))
34 | return (_settings.data.mqttTriggerPath);
35 | if (var == F("pre_darkmode"))
36 | return (_settings.data.webUIdarkmode ? "dark" : "light");
37 | if (var == F("pre_webuidarkmode"))
38 | return (_settings.data.webUIdarkmode ? "checked" : "");
39 | if (var == F("pre_http_user"))
40 | return (_settings.data.httpUser);
41 | if (var == F("pre_http_pass"))
42 | return (_settings.data.httpPass);
43 | if (var == F("pre_hadiscovery"))
44 | return (_settings.data.haDiscovery ? "checked" : "");
45 | //if (var == F("pre_debugmode"))
46 | // return (_settings.data.debugmode ? "checked" : "");
47 | if (var == F("pre_keeprcstate"))
48 | return (_settings.data.keepRcState ? "checked" : "");
49 | if (var == F("pre_led"))
50 | return (String(_settings.data.LEDBrightness));
51 | return String();
52 | }
53 |
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | /*Lot of ideas comes from here:
2 | * https://github.com/glitterkitty/EpEverSolarMonitor
3 | *
4 | */
5 | #include
6 | #include "main.h"
7 |
8 | #include
9 | #include
10 |
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | // #include
18 | #include
19 | #include
20 |
21 | #include
22 |
23 | #include
24 | #include
25 | #include
26 |
27 | #include "VeDirectFrameHandler.h"
28 | #include "VeDirectDataList.h"
29 | #include "VeDirectDeviceList.h"
30 | #include "VeDirectDeviceCodes.h"
31 | #include "Settings.h" //settings functions
32 | #include //new
33 |
34 | #include "html.h" //the HTML content
35 | #include "htmlProzessor.h" // The html Prozessor
36 |
37 | String topic = ""; // Default first part of topic. We will add device ID in setup
38 |
39 | // flag for saving data and other things
40 | bool shouldSaveConfig = false;
41 | bool restartNow = false;
42 | bool workerCanRun = true;
43 | bool dataProzessing = false;
44 | bool haDiscTrigger = false;
45 | bool haAutoDiscTrigger = false;
46 | bool remoteControlState = false;
47 | unsigned int jsonSize = 0;
48 | unsigned long mqtttimer = 0;
49 | unsigned long RestartTimer = 0;
50 | byte wsReqInvNum = 1;
51 | char mqtt_server[80];
52 | char mqttClientId[80];
53 | uint32_t bootcount = 0;
54 |
55 | WiFiClient client;
56 | Settings _settings;
57 | PubSubClient mqttclient(client);
58 | AsyncWebServer server(80);
59 | AsyncWebSocket ws("/ws");
60 | AsyncWebSocketClient *wsClient;
61 | DNSServer dns;
62 | VeDirectFrameHandler myve;
63 | SoftwareSerial veSerial;
64 | RTCMemory rtcMemory;
65 | WebSerial webSerial;
66 |
67 | OneWire oneWire(TEMPSENS_PIN);
68 | DallasTemperature dallasTemp(&oneWire);
69 | NonBlockingDallas tempSens(&dallasTemp);
70 |
71 | DynamicJsonDocument Json(JSON_BUFFER);
72 | JsonObject jsonESP = Json.createNestedObject("ESP_Data");
73 | #include "status-LED.h"
74 | ADC_MODE(ADC_VCC);
75 |
76 | //----------------------------------------------------------------------
77 | void saveConfigCallback()
78 | {
79 | writeLog("Should save config");
80 | shouldSaveConfig = true;
81 | }
82 |
83 | void notifyClients()
84 | {
85 | if (wsClient != nullptr && wsClient->canSend())
86 | {
87 | size_t len = measureJson(Json);
88 | AsyncWebSocketMessageBuffer *buffer = ws.makeBuffer(len);
89 | if (buffer)
90 | {
91 | serializeJson(Json, (char *)buffer->get(), len + 1);
92 | wsClient->text(buffer);
93 | }
94 | writeLog("Data sent to WebSocket");
95 | }
96 | }
97 |
98 | void handleWebSocketMessage(void *arg, uint8_t *data, size_t len)
99 | {
100 |
101 | AwsFrameInfo *info = (AwsFrameInfo *)arg;
102 | if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT)
103 | {
104 | data[len] = 0;
105 | if (strcmp((char *)data, "A9") != 0)
106 | {
107 | if (strcmp((char *)data, "remotecontrol_on") == 0)
108 | {
109 | remoteControl(true);
110 | }
111 | if (strcmp((char *)data, "remotecontrol_off") == 0)
112 | {
113 | remoteControl(false);
114 | }
115 | }
116 | }
117 | }
118 |
119 | void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len)
120 | {
121 | switch (type)
122 | {
123 | case WS_EVT_CONNECT:
124 | wsClient = client;
125 | writeLog("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
126 | if (!dataProzessing /*&& wsClient != nullptr && wsClient->canSend()*/)
127 | notifyClients();
128 | break;
129 | case WS_EVT_PING:
130 | break;
131 | case WS_EVT_DISCONNECT:
132 | writeLog("WebSocket client #%u disconnected\n", client->id());
133 | wsClient = nullptr;
134 | ws.cleanupClients();
135 | break;
136 | case WS_EVT_DATA:
137 | handleWebSocketMessage(arg, data, len);
138 | break;
139 | case WS_EVT_PONG:
140 | break;
141 | case WS_EVT_ERROR:
142 | wsClient = nullptr;
143 | ws.cleanupClients();
144 | break;
145 | }
146 | }
147 |
148 | void ReadVEData()
149 | {
150 | while (veSerial.available())
151 | {
152 | myve.rxData(veSerial.read());
153 | esp_yield();
154 | }
155 | // if (veSerial.available())
156 | // {
157 | // myve.rxData(veSerial.read());
158 | // }
159 | }
160 |
161 | bool remoteControl(bool sw)
162 | {
163 | writeLog("set Remote Control to: %d", sw);
164 | digitalWrite(MYPORT_TX, sw);
165 | rtcData *RTCmem = rtcMemory.getData();
166 | RTCmem->remoteControlState = sw;
167 | rtcMemory.save();
168 | if (_settings.data.keepRcState)
169 | {
170 | _settings.data.rcState = sw;
171 | _settings.save();
172 | }
173 | remoteControlState = sw;
174 | mqtttimer = 0;
175 | return remoteControlState;
176 | }
177 |
178 | void setup()
179 | {
180 | _settings.load();
181 | DBG_BEGIN(DBG_BAUD);
182 | pinMode(LED_PIN, OUTPUT);
183 | analogWrite(LED_PIN, 255 - _settings.data.LEDBrightness);
184 | pinMode(MYPORT_TX, OUTPUT);
185 |
186 | if (!rtcMemory.begin())
187 | {
188 | remoteControlState = false;
189 | }
190 | rtcData *RTCmem = rtcMemory.getData();
191 | remoteControlState = RTCmem->remoteControlState;
192 | Serial.printf("bootcount is: %d \n", RTCmem->bootcount);
193 | if (ESP.getResetInfoPtr()->reason == 6)
194 | {
195 | RTCmem->bootcount++;
196 | if (RTCmem->bootcount >= 10)
197 | {
198 | _settings.reset();
199 | ESP.eraseConfig();
200 | Serial.println("reset called");
201 | ESP.restart();
202 | }
203 | }
204 | rtcMemory.save();
205 | if (_settings.data.keepRcState)
206 | remoteControlState = _settings.data.rcState;
207 | digitalWrite(MYPORT_TX, remoteControlState);
208 |
209 | haAutoDiscTrigger = _settings.data.haDiscovery;
210 | WiFi.persistent(true); // fix wifi save bug
211 | veSerial.begin(VICTRON_BAUD, SWSERIAL_8N1, MYPORT_RX /*, MYPORT_TX, false*/);
212 | veSerial.flush();
213 | veSerial.enableRxGPIOPullUp(false);
214 | myve.callback(prozessData);
215 |
216 | sprintf(mqttClientId, "%s-%06X", _settings.data.deviceName, ESP.getChipId());
217 |
218 | AsyncWiFiManagerParameter custom_mqtt_server("mqtt_server", "MQTT server", NULL, 32);
219 | AsyncWiFiManagerParameter custom_mqtt_user("mqtt_user", "MQTT User", NULL, 32);
220 | AsyncWiFiManagerParameter custom_mqtt_pass("mqtt_pass", "MQTT Password", NULL, 32);
221 | AsyncWiFiManagerParameter custom_mqtt_topic("mqtt_topic", "MQTT Topic", "Victron", 32);
222 | AsyncWiFiManagerParameter custom_mqtt_port("mqtt_port", "MQTT Port", NULL, 6);
223 | AsyncWiFiManagerParameter custom_mqtt_refresh("mqtt_refresh", "MQTT Send Interval", "300", 4);
224 | AsyncWiFiManagerParameter custom_mqtt_triggerpath("mqtt_triggerpath", "MQTT Data Trigger Path", NULL, 80);
225 | AsyncWiFiManagerParameter custom_device_name("device_name", "Device Name", "Victron2MQTT", 32);
226 |
227 | AsyncWiFiManager wm(&server, &dns); // create wifimanager instance
228 |
229 | wm.addParameter(&custom_mqtt_server);
230 | wm.addParameter(&custom_mqtt_user);
231 | wm.addParameter(&custom_mqtt_pass);
232 | wm.addParameter(&custom_mqtt_topic);
233 | wm.addParameter(&custom_mqtt_port);
234 | wm.addParameter(&custom_mqtt_refresh);
235 | wm.addParameter(&custom_mqtt_triggerpath);
236 | wm.addParameter(&custom_device_name);
237 |
238 | wm.setDebugOutput(false); // disable wifimanager debug output
239 | wm.setMinimumSignalQuality(25); // filter weak wifi signals
240 | wm.setConnectTimeout(10); // how long to try to connect for before continuing
241 | wm.setConfigPortalTimeout(300); // auto close configportal after n seconds
242 | wm.setSaveConfigCallback(saveConfigCallback);
243 | bool res = wm.autoConnect("Victron2MQTT-AP");
244 |
245 | // save settings if wifi setup is fire up
246 | if (shouldSaveConfig)
247 | {
248 | strncpy(_settings.data.mqttServer, custom_mqtt_server.getValue(), 40);
249 | strncpy(_settings.data.mqttUser, custom_mqtt_user.getValue(), 40);
250 | strncpy(_settings.data.mqttPassword, custom_mqtt_pass.getValue(), 40);
251 | _settings.data.mqttPort = atoi(custom_mqtt_port.getValue());
252 | strncpy(_settings.data.deviceName, custom_device_name.getValue(), 40);
253 | strncpy(_settings.data.mqttTopic, custom_mqtt_topic.getValue(), 40);
254 | _settings.data.mqttRefresh = atoi(custom_mqtt_refresh.getValue());
255 | strncpy(_settings.data.mqttTriggerPath, custom_mqtt_triggerpath.getValue(), 80);
256 |
257 | _settings.save();
258 | ESP.restart();
259 | }
260 |
261 | topic = _settings.data.mqttTopic;
262 | mqttclient.setServer(_settings.data.mqttServer, _settings.data.mqttPort);
263 | mqttclient.setCallback(mqttCallback);
264 |
265 | if (res)
266 | {
267 | // set the device name
268 | MDNS.begin(_settings.data.deviceName);
269 | MDNS.addService("http", "tcp", 80);
270 | WiFi.hostname(_settings.data.deviceName);
271 |
272 | Json["Device_name"] = _settings.data.deviceName;
273 |
274 | server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
275 | {
276 | if(strlen(_settings.data.httpUser) > 0 && !request->authenticate(_settings.data.httpUser, _settings.data.httpPass)) return request->requestAuthentication();
277 | AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", HTML_MAIN, htmlProcessor);
278 | request->send(response); });
279 |
280 | server.on("/livejson", HTTP_GET, [](AsyncWebServerRequest *request)
281 | {
282 | if(strlen(_settings.data.httpUser) > 0 && !request->authenticate(_settings.data.httpUser, _settings.data.httpPass)) return request->requestAuthentication();
283 | AsyncResponseStream *response = request->beginResponseStream("application/json");
284 | serializeJson(Json, *response);
285 | request->send(response); });
286 |
287 | server.on("/reboot", HTTP_GET, [](AsyncWebServerRequest *request)
288 | {
289 | if(strlen(_settings.data.httpUser) > 0 && !request->authenticate(_settings.data.httpUser, _settings.data.httpPass)) return request->requestAuthentication();
290 | AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", HTML_REBOOT, htmlProcessor);
291 | request->send(response);
292 | restartNow = true;
293 | RestartTimer = millis(); });
294 |
295 | server.on("/confirmreset", HTTP_GET, [](AsyncWebServerRequest *request)
296 | {
297 | if(strlen(_settings.data.httpUser) > 0 && !request->authenticate(_settings.data.httpUser, _settings.data.httpPass)) return request->requestAuthentication();
298 | AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", HTML_CONFIRM_RESET, htmlProcessor);
299 | request->send(response); });
300 |
301 | server.on("/reset", HTTP_GET, [](AsyncWebServerRequest *request)
302 | {
303 | if(strlen(_settings.data.httpUser) > 0 && !request->authenticate(_settings.data.httpUser, _settings.data.httpPass)) return request->requestAuthentication();
304 | AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", "Device is Erasing...");
305 | response->addHeader("Refresh", "15; url=/");
306 | response->addHeader("Connection", "close");
307 | request->send(response);
308 | delay(500);
309 | _settings.reset();
310 | ESP.eraseConfig();
311 | ESP.reset(); });
312 |
313 | server.on("/set", HTTP_GET, [](AsyncWebServerRequest *request)
314 | {
315 | if(strlen(_settings.data.httpUser) > 0 && !request->authenticate(_settings.data.httpUser, _settings.data.httpPass)) return request->requestAuthentication();
316 | String message;
317 | if (request->hasParam("ha")) {
318 | haDiscTrigger = true;
319 | }
320 | if (request->hasParam("remotecontrol")) {
321 | message = request->getParam("remotecontrol")->value();
322 | remoteControl((message == "1") ? true:false);
323 | }
324 | request->send(200, "text/plain", "message received"); });
325 |
326 | server.on("/settings", HTTP_GET, [](AsyncWebServerRequest *request)
327 | {
328 | if(strlen(_settings.data.httpUser) > 0 && !request->authenticate(_settings.data.httpUser, _settings.data.httpPass)) return request->requestAuthentication();
329 | AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", HTML_SETTINGS, htmlProcessor);
330 | request->send(response); });
331 |
332 | server.on("/settingsedit", HTTP_GET, [](AsyncWebServerRequest *request)
333 | {
334 | if(strlen(_settings.data.httpUser) > 0 && !request->authenticate(_settings.data.httpUser, _settings.data.httpPass)) return request->requestAuthentication();
335 | AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", HTML_SETTINGS_EDIT, htmlProcessor);
336 | request->send(response); });
337 |
338 | server.on("/settingssave", HTTP_POST, [](AsyncWebServerRequest *request)
339 | {
340 | if(strlen(_settings.data.httpUser) > 0 && !request->authenticate(_settings.data.httpUser, _settings.data.httpPass)) return request->requestAuthentication();
341 | strncpy(_settings.data.mqttServer, request->arg("post_mqttServer").c_str(), 40);
342 | _settings.data.mqttPort = request->arg("post_mqttPort").toInt();
343 | strncpy(_settings.data.mqttUser, request->arg("post_mqttUser").c_str(), 40);
344 | strncpy(_settings.data.mqttPassword, request->arg("post_mqttPassword").c_str(), 40);
345 | strncpy(_settings.data.mqttTopic, request->arg("post_mqttTopic").c_str(), 40);
346 | _settings.data.mqttRefresh = request->arg("post_mqttRefresh").toInt() < 1 ? 1 : request->arg("post_mqttRefresh").toInt(); // prevent lower numbers
347 | strncpy(_settings.data.deviceName, request->arg("post_deviceName").c_str(), 40);
348 | _settings.data.mqttJson = (request->arg("post_mqttjson") == "true") ? true : false;
349 | strncpy(_settings.data.mqttTriggerPath, request->arg("post_mqtttrigger").c_str(), 80);
350 | _settings.data.webUIdarkmode = (request->arg("post_webuicolormode") == "true") ? true : false;
351 | strncpy(_settings.data.httpUser, request->arg("post_httpUser").c_str(), 40);
352 | strncpy(_settings.data.httpPass, request->arg("post_httpPass").c_str(), 40);
353 | _settings.data.haDiscovery = (request->arg("post_hadiscovery") == "true") ? true : false;
354 | //_settings.data.debugmode = (request->arg("post_debugmode") == "true") ? true : false;
355 | _settings.data.keepRcState = (request->arg("post_keeprcstate") == "true") ? true : false;
356 | _settings.data.LEDBrightness = request->arg("post_led").toInt();
357 | _settings.save();
358 | request->redirect("/reboot"); });
359 |
360 | server.on(
361 | "/update", HTTP_POST, [](AsyncWebServerRequest *request)
362 | {
363 | //https://gist.github.com/JMishou/60cb762047b735685e8a09cd2eb42a60
364 | // the request handler is triggered after the upload has finished...
365 | // create the response, add header, and send response
366 | if(strlen(_settings.data.httpUser) > 0 && !request->authenticate(_settings.data.httpUser, _settings.data.httpPass)) return request->requestAuthentication();
367 | AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", (Update.hasError())?"FAIL":"OK");
368 | response->addHeader("Connection", "close");
369 | response->addHeader("Access-Control-Allow-Origin", "*");
370 | //restartNow = true; // Tell the main loop to restart the ESP
371 | request->send(response); },
372 | [](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final)
373 | {
374 | // Upload handler chunks in data
375 |
376 | if (!index)
377 | { // if index == 0 then this is the first frame of data
378 | Serial.printf("UploadStart: %s\n", filename.c_str());
379 | Serial.setDebugOutput(true);
380 |
381 | // calculate sketch space required for the update
382 | uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
383 | if (!Update.begin(maxSketchSpace))
384 | { // start with max available size
385 | Update.printError(Serial);
386 | }
387 | Update.runAsync(true); // tell the updaterClass to run in async mode
388 | }
389 |
390 | // Write chunked data to the free sketch space
391 | if (Update.write(data, len) != len)
392 | {
393 | Update.printError(Serial);
394 | }
395 |
396 | if (final)
397 | { // if the final flag is set then this is the last frame of data
398 | if (Update.end(true))
399 | { // true to set the size to the current progress
400 | Serial.printf("Update Success: %u B\nRebooting...\n", index + len);
401 | }
402 | else
403 | {
404 | Update.printError(Serial);
405 | }
406 | Serial.setDebugOutput(false);
407 | }
408 | });
409 |
410 | server.onNotFound([](AsyncWebServerRequest *request)
411 | { request->send(418, "text/plain", "418 I'm a teapot"); });
412 |
413 | ws.onEvent(onEvent);
414 | server.addHandler(&ws);
415 |
416 | // WebSerial is accessible at "/webserial" in browser
417 | webSerial.begin(&server);
418 |
419 | server.begin();
420 | // MDNS.addService("http", "tcp", 80);
421 | // MDNS.update();
422 |
423 | jsonESP["IP"] = WiFi.localIP();
424 | jsonESP["sw_version"] = SOFTWARE_VERSION;
425 | tempSens.begin(NonBlockingDallas::resolution_12, TIME_INTERVAL);
426 | tempSens.onTemperatureChange(handleTemperatureChange);
427 | }
428 | analogWrite(LED_PIN, 255);
429 | RTCmem->bootcount = 0;
430 | rtcMemory.save();
431 | }
432 |
433 | void loop()
434 | {
435 | MDNS.update();
436 | tempSens.update();
437 | if (Update.isRunning())
438 | {
439 | workerCanRun = false;
440 | }
441 | if (workerCanRun)
442 | {
443 | ReadVEData();
444 |
445 | // Make sure wifi is in the right mode
446 | if (WiFi.status() == WL_CONNECTED)
447 | { // No use going to next step unless WIFI is up and running.
448 | // ws.cleanupClients(); // clean unused client connections
449 | // MDNS.update();
450 | if (millis() - mqtttimer > (_settings.data.mqttRefresh * 1000) || mqtttimer == 0)
451 | {
452 | writeLog(" Data Send...");
453 | sendtoMQTT(); // Update data to MQTT server if we should
454 | mqtttimer = millis();
455 | }
456 |
457 | mqttclient.loop(); // Check if we have something to read from MQTT
458 | }
459 | notificationLED(); // notification LED routine
460 |
461 | if ((haDiscTrigger || _settings.data.haDiscovery) && measureJson(Json) > jsonSize)
462 | {
463 | if (sendHaDiscovery())
464 | {
465 | haDiscTrigger = false;
466 | jsonSize = measureJson(Json);
467 | }
468 | }
469 | jsonESP["Wifi_RSSI"] = WiFi.RSSI();
470 | }
471 |
472 | if (restartNow && millis() >= (RestartTimer + 500))
473 | {
474 | writeLog("Restart");
475 | ESP.reset();
476 | }
477 | }
478 |
479 | void prozessData()
480 | {
481 | dataProzessing = true;
482 | writeLog("VE callback triggered... prozessing data");
483 | getJsonData();
484 | notifyClients();
485 | dataProzessing = false;
486 | }
487 |
488 | bool getJsonData()
489 | {
490 | jsonESP["ESP_VCC"] = (ESP.getVcc() / 1000.0) + 0.3;
491 | jsonESP["Wifi_RSSI"] = WiFi.RSSI();
492 | jsonESP["Free_Heap"] = ESP.getFreeHeap();
493 | jsonESP["json_space"] = Json.capacity() - Json.memoryUsage();
494 | jsonESP["WS_Clients"] = ws.count();
495 | jsonESP["Runtime"] = millis() / 1000;
496 | writeLog("VE data: %d:%d:%d", myve.veEnd, myve.veErrorCount, myve.veError);
497 | for (size_t i = 0; i < myve.veEnd; i++)
498 | {
499 |
500 | if (myve.veName[i] == NULL || strlen(myve.veName[i]) == 0 || myve.veValue[i] == NULL || strlen(myve.veValue[i]) == 0)
501 | {
502 | i = myve.veEnd;
503 | break;
504 | }
505 | // writeLog("[%s:%s]",myve.veName[i], myve.veValue[i]);
506 | // search for every Vevalue in the list and replace it with clear name
507 | for (size_t j = 0; j < VePrettyDataSize; j++)
508 | {
509 | VePrettyEntry entry;
510 | memcpy_P(&entry, &VePrettyData[j], sizeof(entry));
511 |
512 | char key[16];
513 | strcpy_P(key, entry.key);
514 |
515 | if (strcmp(key, myve.veName[i]) == 0)
516 | {
517 | char name[32];
518 | char op[8];
519 | strcpy_P(name, entry.name);
520 | strcpy_P(op, entry.op);
521 |
522 | if (strlen(op) > 0 && strcmp(op, "0") != 0)
523 | {
524 | Json[FPSTR(entry.name)] = (int)((atof(myve.veValue[i]) / atoi(op)) * 100 + 0.5) / 100.0;
525 | }
526 | else if (strcmp(op, "0") == 0)
527 | {
528 | Json[FPSTR(entry.name)] = atoi(myve.veValue[i]);
529 | }
530 | else
531 | {
532 | Json[FPSTR(entry.name)] = myve.veValue[i];
533 | }
534 | if (strcmp(name, "Device_model") == 0)
535 | {
536 | uint16_t deviceID = strtol(myve.veValue[i], nullptr, 16);
537 | VeDeviceEntry devEntry;
538 | size_t left = 0, right = VeDeviceListSize;
539 | const char *modelName = nullptr;
540 |
541 | while (left < right)
542 | {
543 | size_t mid = (left + right) / 2;
544 | memcpy_P(&devEntry, &VeDeviceList[mid], sizeof(devEntry));
545 | if (deviceID == devEntry.id)
546 | {
547 | modelName = (const char *)pgm_read_ptr(&devEntry.name);
548 | break;
549 | }
550 | else if (deviceID < devEntry.id)
551 | {
552 | right = mid;
553 | }
554 | else
555 | {
556 | left = mid + 1;
557 | }
558 | }
559 | if (modelName)
560 | {
561 | Json[FPSTR(entry.name)] = FPSTR(modelName);
562 | }
563 | }
564 | struct CodeMap
565 | {
566 | const char *label;
567 | const VeCodeEntry *table;
568 | size_t size;
569 | };
570 |
571 | const CodeMap maps[] = {
572 | {"Alarm_code", VeDirectDeviceCodeAR, VeDirectDeviceCodeARSize},
573 | {"Off_reason", VeDirectDeviceCodeOR, VeDirectDeviceCodeORSize},
574 | {"Operation_state", VeDirectDeviceCodeCS, VeDirectDeviceCodeCSSize},
575 | {"Current_error", VeDirectDeviceCodeERR, VeDirectDeviceCodeERRSize},
576 | {"Tracker_operation_mode", VeDirectDeviceCodeMPPT, VeDirectDeviceCodeMPPTSize},
577 | };
578 |
579 | for (const auto &map : maps)
580 | {
581 | if (strcmp(name, map.label) == 0)
582 | {
583 | for (size_t k = 0; k < map.size; k++)
584 | {
585 | VeCodeEntry codeEntry;
586 | memcpy_P(&codeEntry, &map.table[k], sizeof(codeEntry));
587 |
588 | char codeBuf[16];
589 | strcpy_P(codeBuf, codeEntry.code);
590 | if (strcmp(codeBuf, myve.veValue[i]) == 0)
591 | {
592 | Json[FPSTR(entry.name)] = FPSTR(codeEntry.text);
593 | break;
594 | }
595 | }
596 | }
597 | }
598 | break;
599 | }
600 | }
601 |
602 | Json["Device_connection"] = !myve.veError;
603 | Json["Remote_Control_State"] = remoteControlState;
604 | }
605 | return true;
606 | }
607 |
608 | bool connectMQTT()
609 | {
610 | if (!mqttclient.connected())
611 | {
612 | if (mqttclient.connect(mqttClientId, _settings.data.mqttUser, _settings.data.mqttPassword, (topic + "/Alive").c_str(), 0, true, "false", true))
613 | {
614 | mqttclient.publish((topic + String("/IP")).c_str(), String(WiFi.localIP().toString()).c_str());
615 | mqttclient.publish((topic + String("/Alive")).c_str(), "true", true); // LWT online message must be retained!
616 | mqttclient.subscribe((topic + "/Remote_Control").c_str());
617 |
618 | if (strlen(_settings.data.mqttTriggerPath) > 0)
619 | {
620 | writeLog("MQTT Data Trigger Subscribed");
621 | mqttclient.subscribe(_settings.data.mqttTriggerPath);
622 | }
623 | return true;
624 | }
625 | else
626 | {
627 | return false;
628 | }
629 | return false;
630 | }
631 | else
632 | {
633 | return true;
634 | }
635 | }
636 |
637 | bool sendtoMQTT()
638 | {
639 |
640 | if (!connectMQTT())
641 | {
642 | return false;
643 | }
644 |
645 | String mqttDeviceName = topic;
646 |
647 | //-----------------------------------------------------
648 | mqttclient.publish((mqttDeviceName + String("/Alive")).c_str(), "true", true); // LWT online message must be retained!
649 | mqttclient.publish((mqttDeviceName + String("/Wifi_RSSI")).c_str(), String(WiFi.RSSI()).c_str());
650 | mqttclient.publish((mqttDeviceName + String("/Remote_Control_State")).c_str(), remoteControlState ? "true" : "false");
651 | if (!_settings.data.mqttJson)
652 | {
653 |
654 | for (JsonPair i : Json.as())
655 | {
656 | mqttclient.publish((mqttDeviceName + "/" + i.key().c_str()).c_str(), i.value().as().c_str());
657 | }
658 | }
659 | else
660 | {
661 | mqttclient.beginPublish((String(mqttDeviceName + "/DATA")).c_str(), measureJson(Json), false);
662 | serializeJson(Json, mqttclient);
663 | mqttclient.endPublish();
664 | }
665 |
666 | return true;
667 | }
668 |
669 | void mqttCallback(char *top, byte *payload, unsigned int length) // Need rework
670 | {
671 | String messageTemp;
672 | // updateProgress = true; // stop servicing data
673 | // if (!_settings.data.mqttJson)
674 | // {
675 |
676 | for (unsigned int i = 0; i < length; i++)
677 | {
678 | messageTemp += (char)payload[i];
679 | }
680 | // }
681 | // else
682 | // {
683 | // StaticJsonDocument<1024> mqttJsonAnswer;
684 | // deserializeJson(mqttJsonAnswer, (const byte *)payload, length);
685 | // }
686 |
687 | if (strlen(_settings.data.mqttTriggerPath) > 0 && strcmp(top, _settings.data.mqttTriggerPath) == 0)
688 | {
689 | writeLog("MQTT Data Trigger Firered Up");
690 | mqtttimer = 0;
691 | }
692 | if (strcmp(top, (topic + "/Remote_Control").c_str()) == 0)
693 | {
694 | if (messageTemp == "true")
695 | {
696 | mqtttimer = 0;
697 | remoteControl(true);
698 | }
699 | if (messageTemp == "false")
700 | {
701 | mqtttimer = 0;
702 | remoteControl(false);
703 | }
704 | }
705 | }
706 |
707 | bool sendHaDiscovery()
708 | {
709 | if (!connectMQTT())
710 | {
711 | return false;
712 | }
713 | String haDeviceDescription = String("\"dev\":") +
714 | "{\"ids\":[\"" + mqttClientId + "\"]," +
715 | "\"name\":\"" + _settings.data.deviceName + "\"," +
716 | "\"cu\":\"http://" + WiFi.localIP().toString() + "\"," +
717 | "\"mdl\":\"" + Json["Device_model"].as().c_str() + "\"," +
718 | "\"mf\":\"SoftWareCrash\"," +
719 | "\"sw\":\"" + SOFTWARE_VERSION + "\"" +
720 | "}";
721 |
722 | char topBuff[128];
723 | // char configBuff[1024];
724 | // size_t mqttContentLength;
725 | for (size_t i = 0; i < sizeof haDescriptor / sizeof haDescriptor[0]; i++)
726 | {
727 | if (Json.containsKey(haDescriptor[i][0]))
728 | {
729 | String haPayLoad = String("{") +
730 | "\"name\":\"" + haDescriptor[i][0] + "\"," +
731 | "\"stat_t\":\"" + _settings.data.mqttTopic + "/" + haDescriptor[i][0] + "\"," +
732 | "\"avty_t\":\"" + _settings.data.mqttTopic + "/Alive\"," +
733 | "\"pl_avail\": \"true\"," +
734 | "\"pl_not_avail\": \"false\"," +
735 | "\"uniq_id\":\"" + mqttClientId + "." + haDescriptor[i][0] + "\"," +
736 | "\"ic\":\"mdi:" + haDescriptor[i][1] + "\",";
737 | if (strlen(haDescriptor[i][2]) != 0)
738 | haPayLoad += (String) "\"unit_of_meas\":\"" + haDescriptor[i][2] + "\",";
739 |
740 | if (strcmp(haDescriptor[i][2], "kWh") == 0 || strcmp(haDescriptor[i][2], "Wh") == 0)
741 | haPayLoad += (String) "\"state_class\":\"total\",";
742 | if (strcmp(haDescriptor[i][2], "A") == 0 || strcmp(haDescriptor[i][2], "V") == 0 || strcmp(haDescriptor[i][2], "W") == 0)
743 | haPayLoad += (String) "\"state_class\":\"measurement\",";
744 |
745 | if (strlen(haDescriptor[i][3]) != 0)
746 | haPayLoad += (String) "\"dev_cla\":\"" + haDescriptor[i][3] + "\",";
747 | haPayLoad += haDeviceDescription;
748 | haPayLoad += "}";
749 | sprintf(topBuff, "homeassistant/sensor/%s/%s/config", _settings.data.mqttTopic, haDescriptor[i][0]); // build the topic
750 | mqttclient.beginPublish(topBuff, haPayLoad.length(), true);
751 | for (size_t i = 0; i < haPayLoad.length(); i++)
752 | {
753 | mqttclient.write(haPayLoad[i]);
754 | }
755 | mqttclient.endPublish();
756 | }
757 | }
758 |
759 | // switch
760 | String haPayLoad = String("{") +
761 | "\"name\":\"Remote_Control\"," +
762 | "\"command_topic\":\"" + _settings.data.mqttTopic + "/Remote_Control\"," +
763 | "\"stat_t\":\"" + _settings.data.mqttTopic + "/Remote_Control_State\"," +
764 | "\"uniq_id\":\"" + mqttClientId + ".Remote_Control\"," +
765 | "\"avty_t\":\"" + _settings.data.mqttTopic + "/Alive\"," +
766 | "\"pl_avail\": \"true\"," +
767 | "\"pl_not_avail\": \"false\"," +
768 | "\"ic\":\"mdi:toggle-switch-off\"," +
769 | "\"pl_on\":\"true\"," +
770 | "\"pl_off\":\"false\"," +
771 | "\"stat_on\":\"true\"," +
772 | "\"stat_off\":\"false\",";
773 |
774 | haPayLoad += haDeviceDescription;
775 | haPayLoad += "}";
776 | sprintf(topBuff, "homeassistant/switch/%s/%s/config", _settings.data.mqttTopic, "Remote_Control"); // build the topic
777 |
778 | mqttclient.beginPublish(topBuff, haPayLoad.length(), true);
779 | for (size_t i = 0; i < haPayLoad.length(); i++)
780 | {
781 | mqttclient.write(haPayLoad[i]);
782 | }
783 | mqttclient.endPublish();
784 |
785 | return true;
786 | }
787 |
788 | void handleTemperatureChange(int deviceIndex, int32_t temperatureRAW)
789 | {
790 | float tempCels = tempSens.rawToCelsius(temperatureRAW);
791 | if (tempCels <= -55 || tempCels >= 125)
792 | return;
793 | writeLog(" DS18B20_%d Celsius:%f", deviceIndex + 1, tempCels);
794 | char msgBuffer[8];
795 | jsonESP["DS18B20_" + String(deviceIndex + 1)] = dtostrf(tempCels, 4, 2, msgBuffer);
796 | }
797 |
798 | void writeLog(const char *format, ...)
799 | {
800 | char msg[256];
801 | va_list args;
802 |
803 | va_start(args, format);
804 | vsnprintf(msg, sizeof(msg), format, args); // do check return value
805 | va_end(args);
806 |
807 | // write msg to the log
808 | DBG_PRINTLN(msg);
809 | DBG_WEBLN(msg);
810 | }
--------------------------------------------------------------------------------
/src/main.h:
--------------------------------------------------------------------------------
1 | #define ARDUINOJSON_USE_DOUBLE 0
2 | #define ARDUINOJSON_USE_LONG_LONG 0
3 |
4 | #define VICTRON_BAUD 19200 // baud rate for modbus
5 | #define MYPORT_TX 12
6 | #define MYPORT_RX 13
7 | #define LED_PIN 02 //D4 with the LED on Wemos D1 Mini
8 | #define TEMPSENS_PIN 04 // DS18B20 Pin
9 | #define TIME_INTERVAL 2500 // Time interval among sensor readings [milliseconds]
10 | #define JSON_BUFFER 1536//2048
11 |
12 | #define FlashSize ESP.getFreeSketchSpace()
13 | #define ESP01
14 | #ifdef ARDUINO_ESP8266_ESP01
15 | #ifdef MYPORT_TX
16 | #undef MYPORT_TX
17 | #define MYPORT_TX 0
18 | #endif
19 | #ifdef MYPORT_RX
20 | #undef MYPORT_RX
21 | #define MYPORT_RX 2
22 | #ifdef ESP01
23 | #undef ESP01
24 | #define ESP01 "display: none;"
25 | #endif
26 | #endif
27 | #endif
28 |
29 | //for wifimanager fork test
30 | #define CORE_DEBUG_LEVEL 0
31 |
32 | #define SOFTWARE_VERSION SWVERSION
33 | #define DBG_BAUD 115200
34 | #define DBG Serial
35 | #define DBG_WEBLN(...) webSerial.println(__VA_ARGS__)
36 | #define DBG_SERIAL(...) DBG(__VA_ARGS__)
37 | #define DBG_BEGIN(...) DBG.begin(__VA_ARGS__)
38 | #define DBG_PRINTLN(...) DBG.println(__VA_ARGS__)
39 |
40 | typedef struct {
41 | byte bootcount;
42 | bool remoteControlState;
43 | } rtcData;
44 |
45 | bool getJsonData();
46 |
47 | void notifyClients();
48 |
49 | void mqttCallback(char *top, byte *payload, unsigned int length);
50 |
51 | bool sendtoMQTT();
52 |
53 | /**
54 | * @brief function for uart callback to prozess avaible data
55 | *
56 | */
57 | void prozessData();
58 |
59 | /***
60 | *
61 | *
62 | */
63 | bool sendHaDiscovery();
64 |
65 | /**
66 | * @brief function for ext. TempSensors
67 | */
68 | void handleTemperatureChange(int deviceIndex, int32_t temperatureRAW);
69 |
70 | bool remoteControl(bool sw);
71 |
72 | /**
73 | * @brief this function act like s/n/printf() and give the output to the configured serial and webserial
74 | *
75 | */
76 | void writeLog(const char* format, ...);
--------------------------------------------------------------------------------
/src/status-LED.h:
--------------------------------------------------------------------------------
1 | #include
2 | /*
3 | Blinking LED = Relais Off
4 | Waveing LED = Relais On
5 | every 5 seconds:
6 | 1x all ok - Working
7 | 2x no Device Connection
8 | 3x no MQTT Connection
9 | 4x no WiFi Connection
10 |
11 | */
12 | extern Settings _settings;
13 | bool ledPin = 0;
14 | unsigned int ledTimer = 0;
15 | unsigned int repeatTime = 5000;
16 | unsigned int cycleTime = 250;
17 | unsigned int cycleMillis = 0;
18 | byte ledState = 0;
19 |
20 | //bool waveHelper = false;
21 | void notificationLED()
22 | {
23 | if (millis() > (ledTimer + repeatTime) && ledState == 0)
24 | {
25 | if (WiFi.status() != WL_CONNECTED)
26 | ledState = 4;
27 | else if (!mqttclient.connected() && strlen(_settings.data.mqttServer) > 0)
28 | ledState = 3;
29 | else if (strcmp(myve.veValue[0], "") == 0)
30 | ledState = 2;
31 | else if (WiFi.status() == WL_CONNECTED && (mqttclient.connected() || strlen(_settings.data.mqttServer) <= 0) && !myve.veError)
32 | ledState = 1;
33 | }
34 |
35 | if (ledState > 0)
36 | {
37 | if (millis() > (cycleMillis + cycleTime))
38 | {
39 | if (!ledPin)
40 | {
41 | ledPin = true;
42 | }
43 | else
44 | {
45 | ledPin = false;
46 | ledState--;
47 | }
48 | cycleMillis = millis();
49 | if (ledState == 0)
50 | {
51 | ledTimer = millis();
52 | }
53 | }
54 |
55 | }
56 | if (!ledPin)
57 | {
58 | analogWrite(LED_PIN, 255);
59 | } else
60 | {
61 | analogWrite(LED_PIN, 255-_settings.data.LEDBrightness);
62 | }
63 |
64 | }
--------------------------------------------------------------------------------
/src/webpages/HTML_CONFIRM_RESET.html:
--------------------------------------------------------------------------------
1 | %pre_head_template%
2 |
3 | Erase all Data?
4 |
5 |
9 | %pre_foot_template%
10 | Hidden Helper
--------------------------------------------------------------------------------
/src/webpages/HTML_FOOT.html:
--------------------------------------------------------------------------------
1 |
2 | Victron2MQTT %pre_software_version% By Softwarecrash
4 |
7 |
8 |
9 |
13 |
37 |