Compare commits
116 Commits
2024.10.22
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
653efb41a2 | ||
|
|
571ba2f350 | ||
|
|
220cfbf7ae | ||
|
|
db130f646e | ||
|
|
5510c9ff57 | ||
|
|
ebf4e921ee | ||
|
|
d068542c94 | ||
|
|
19fa310f43 | ||
|
|
87772cb76b | ||
|
|
50207a42bf | ||
|
|
a0e6942537 | ||
|
|
c37397acca | ||
|
|
498afe377b | ||
|
|
d43ac7fb92 | ||
|
|
11105944be | ||
|
|
c7fa4ff212 | ||
|
|
96ba58af8c | ||
|
|
d485d1b820 | ||
|
|
8f60a3a12a | ||
|
|
940027ab19 | ||
|
|
24b3f27364 | ||
|
|
5265c6281f | ||
|
|
8acae28c59 | ||
|
|
0061d5e159 | ||
|
|
5d14454185 | ||
|
|
58382be16c | ||
|
|
2edec642fb | ||
|
|
726a08ec2c | ||
|
|
d775ee9e89 | ||
|
|
1c1fcbea51 | ||
|
|
bf89fd7558 | ||
|
|
8247070aae | ||
|
|
241ee1e99d | ||
|
|
a75543c309 | ||
|
|
1c5a3cf6fe | ||
|
|
b2dcac549c | ||
|
|
37b173071e | ||
|
|
041ae7bae7 | ||
|
|
680863fb00 | ||
|
|
8297591853 | ||
|
|
cc3290be8e | ||
|
|
9bfded055a | ||
|
|
2b07e3c2c8 | ||
|
|
eac2e2fb39 | ||
|
|
d843ac6422 | ||
|
|
33a9b7454c | ||
|
|
3dc70ab40a | ||
|
|
ecb5e9cc32 | ||
|
|
9a53d6e209 | ||
|
|
63405a712c | ||
|
|
69c67f96e7 | ||
|
|
d088021902 | ||
|
|
74e3947cb2 | ||
|
|
ca060e406e | ||
|
|
53b496fd00 | ||
|
|
ab60875142 | ||
|
|
3948adf460 | ||
|
|
3c56ec3738 | ||
|
|
661ea6c022 | ||
|
|
3fa864ce52 | ||
|
|
71f312d830 | ||
|
|
f1c095e41d | ||
|
|
bac7179f73 | ||
|
|
9f315207d4 | ||
|
|
08f4d623c7 | ||
|
|
f85297d52f | ||
|
|
e724fb8375 | ||
|
|
54b4a2e9e8 | ||
|
|
94cecc23f5 | ||
|
|
866b539757 | ||
|
|
9132a88963 | ||
|
|
eecd7f7c28 | ||
|
|
ba304b2871 | ||
|
|
0832d3e18c | ||
|
|
3c188f2f9f | ||
|
|
68d2f7bf29 | ||
|
|
ad73fd8abd | ||
|
|
e6a994fd7a | ||
|
|
d324a5c83f | ||
|
|
0aba1595df | ||
|
|
376912d821 | ||
|
|
d3eabc3311 | ||
|
|
d06ea51c7a | ||
|
|
c750defc5f | ||
|
|
130d90ce04 | ||
|
|
55c98ef880 | ||
|
|
6c903abda1 | ||
|
|
28788070b2 | ||
|
|
0fc1ffc4d3 | ||
|
|
8019eaf182 | ||
|
|
225cab676a | ||
|
|
4594bcb23e | ||
|
|
b21e8f8c80 | ||
|
|
8566b08723 | ||
|
|
8452a8d110 | ||
|
|
70f301941b | ||
|
|
d259042542 | ||
|
|
6113e0737b | ||
|
|
b1edb13b3c | ||
|
|
2a21e53422 | ||
|
|
521fce35e4 | ||
|
|
2e23c7e0ae | ||
|
|
68c87c9217 | ||
|
|
4a247f5e94 | ||
|
|
05006e0642 | ||
|
|
d9a8461a2e | ||
|
|
c3d3d947d7 | ||
|
|
e29b86e4dc | ||
|
|
8257eb7aa2 | ||
|
|
1e857b79c1 | ||
|
|
16901482d9 | ||
|
|
aa9f36ee8f | ||
|
|
cf1693e1a0 | ||
|
|
e5cf12cebd | ||
|
|
bcf4b70dc9 | ||
|
|
d0b2b972e2 |
38
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -4,19 +4,19 @@ labels: ["bug"]
|
|||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: >
|
||||||
### ⚠️ Please remember: issues are for *bugs*⚠️
|
### ⚠️ Please remember: issues are for *bugs*
|
||||||
That is, something you believe affects every single user of OpenDTU-OnBattery, not just you. If you're not sure, start with one of the other options below.
|
That is, something you believe affects every single user of OpenDTU, not just you. If you're not sure, start with one of the other options below.
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
#### Have a question? 👉 [Start a new discussion](https://github.com/helgeerbe/OpenDTU-OnBattery/discussions/new/choose) or [ask in chat](https://discord.gg/WzhxEY62mB).
|
#### Have a question? 👉 [Start a new discussion](https://github.com/tbnobody/OpenDTU/discussions/new) or [ask in chat](https://discord.gg/WzhxEY62mB).
|
||||||
|
|
||||||
#### Before opening an issue, please double check:
|
#### Before opening an issue, please double check:
|
||||||
|
|
||||||
- [Documentation](https://opendtu-onbattery.net)
|
- [Documentation](https://www.opendtu.solar).
|
||||||
- [The FAQs](https://opendtu-onbattery.net/firmware/faq/)
|
- [The FAQs](https://www.opendtu.solar/firmware/faq/).
|
||||||
- [Existing issues and discussions](https://github.com/helgeerbe/OpenDTU-OnBattery/search?q=&type=issues)
|
- [Existing issues and discussions](https://github.com/tbnobody/OpenDTU/search?q=&type=issues).
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: what-happened
|
id: what-happened
|
||||||
attributes:
|
attributes:
|
||||||
@ -45,7 +45,7 @@ body:
|
|||||||
id: install_format
|
id: install_format
|
||||||
attributes:
|
attributes:
|
||||||
label: Install Method
|
label: Install Method
|
||||||
description: How did you install OpenDTU-OnBattery?
|
description: How did you install OpenDTU?
|
||||||
options:
|
options:
|
||||||
- Pre-Compiled binary from GitHub releases
|
- Pre-Compiled binary from GitHub releases
|
||||||
- Pre-Compiled binary from GitHub actions/pull-request
|
- Pre-Compiled binary from GitHub actions/pull-request
|
||||||
@ -55,25 +55,17 @@ body:
|
|||||||
- type: input
|
- type: input
|
||||||
id: version
|
id: version
|
||||||
attributes:
|
attributes:
|
||||||
label: What git-hash/version of OpenDTU-OnBattery?
|
label: What git-hash/version of OpenDTU?
|
||||||
description: You can find this in the Web UI at Info -> System.
|
description: You can find this in by going to Info -> System
|
||||||
placeholder: "e.g. 359d513"
|
placeholder: "e.g. 359d513"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: input
|
||||||
id: environment
|
id: environment
|
||||||
attributes:
|
attributes:
|
||||||
label: What firmware variant (PIO Environment)?
|
label: What firmware variant (PIO Environment) are you using?
|
||||||
description: You can find this in the Web UI at Info -> System.
|
description: You can find this in by going to Info -> System
|
||||||
options:
|
placeholder: "generic_esp32s3_usb"
|
||||||
- "generic_esp32s3_usb"
|
|
||||||
- "generic_esp32s3"
|
|
||||||
- "generic_esp32_8mb"
|
|
||||||
- "generic_esp32_4mb_no_ota"
|
|
||||||
- "generic_esp32"
|
|
||||||
- "generic"
|
|
||||||
- "opendtufusionv2"
|
|
||||||
- "other (tell us in 'Anything else?')"
|
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
@ -95,7 +87,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Please confirm the following
|
label: Please confirm the following
|
||||||
options:
|
options:
|
||||||
- label: I believe this issue is a bug that affects all users of OpenDTU-OnBattery, not something specific to my installation.
|
- label: I believe this issue is a bug that affects all users of OpenDTU, not something specific to my installation.
|
||||||
required: true
|
required: true
|
||||||
- label: I have already searched for relevant existing issues and discussions before opening this report.
|
- label: I have already searched for relevant existing issues and discussions before opening this report.
|
||||||
required: true
|
required: true
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -4,5 +4,5 @@ contact_links:
|
|||||||
url: https://discord.gg/WzhxEY62mB
|
url: https://discord.gg/WzhxEY62mB
|
||||||
about: Discuss with us on Discord
|
about: Discuss with us on Discord
|
||||||
- name: 🤔 Have questions or need support?
|
- name: 🤔 Have questions or need support?
|
||||||
url: https://github.com/helgeerbe/OpenDTU-OnBattery/discussions
|
url: https://github.com/tbnobody/OpenDTU/discussions
|
||||||
about: Use the GitHub Discussions feature
|
about: Use the GitHub Discussions feature
|
||||||
4
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@ -1,12 +1,12 @@
|
|||||||
name: ✨ Request a feature
|
name: ✨ Request a feature
|
||||||
description: Suggest an improvement idea for OpenDTU-OnBattery!
|
description: Suggest an improvement idea for OpenDTU!
|
||||||
title: "[Request]"
|
title: "[Request]"
|
||||||
labels: ["enhancement"]
|
labels: ["enhancement"]
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: >
|
value: >
|
||||||
**Thank you for wanting to request a feature in OpenDTU-OnBattery!**
|
**Thank you for wanting to request a feature in OpenDTU!**
|
||||||
|
|
||||||
Before you go ahead with your request, please first consider if it wouldn't be
|
Before you go ahead with your request, please first consider if it wouldn't be
|
||||||
better suited in a external home automation software like OpenHAB, ioBroker, Home Assistant etc.
|
better suited in a external home automation software like OpenHAB, ioBroker, Home Assistant etc.
|
||||||
|
|||||||
53
.github/workflows/build.yml
vendored
@ -1,15 +1,10 @@
|
|||||||
name: OpenDTU-OnBattery Build
|
name: OpenDTU Build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- docs/**
|
- docs/**
|
||||||
- '**/*.md'
|
- '**/*.md'
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- development
|
|
||||||
tags-ignore:
|
|
||||||
- 'v**'
|
|
||||||
pull_request:
|
pull_request:
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- docs/**
|
- docs/**
|
||||||
@ -60,15 +55,6 @@ jobs:
|
|||||||
- name: Get tags
|
- name: Get tags
|
||||||
run: git fetch --force --tags origin
|
run: git fetch --force --tags origin
|
||||||
|
|
||||||
- name: Create and switch to a meaningful branch for pull-requests
|
|
||||||
if: github.event_name == 'pull_request'
|
|
||||||
run: |
|
|
||||||
OWNER=${{ github.repository_owner }}
|
|
||||||
NAME=${{ github.event.repository.name }}
|
|
||||||
ID=${{ github.event.pull_request.number }}
|
|
||||||
DATE=$(date +'%Y%m%d%H%M')
|
|
||||||
git switch -c ${OWNER}/${NAME}/pr${ID}-${DATE}
|
|
||||||
|
|
||||||
- name: Cache pip
|
- name: Cache pip
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
@ -119,48 +105,27 @@ jobs:
|
|||||||
run: pio run -e ${{ matrix.environment }}
|
run: pio run -e ${{ matrix.environment }}
|
||||||
|
|
||||||
- name: Rename Firmware
|
- name: Rename Firmware
|
||||||
run: mv .pio/build/${{ matrix.environment }}/firmware.bin .pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.bin
|
run: mv .pio/build/${{ matrix.environment }}/firmware.bin .pio/build/${{ matrix.environment }}/opendtu-${{ matrix.environment }}.bin
|
||||||
|
|
||||||
- name: Rename Factory Firmware
|
- name: Rename Factory Firmware
|
||||||
run: mv .pio/build/${{ matrix.environment }}/firmware.factory.bin .pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.factory.bin
|
run: mv .pio/build/${{ matrix.environment }}/firmware.factory.bin .pio/build/${{ matrix.environment }}/opendtu-${{ matrix.environment }}.factory.bin
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: opendtu-onbattery-${{ matrix.environment }}
|
name: opendtu-${{ matrix.environment }}
|
||||||
path: |
|
path: |
|
||||||
.pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.bin
|
.pio/build/${{ matrix.environment }}/opendtu-${{ matrix.environment }}.bin
|
||||||
!.pio/build/generic_esp32_4mb_no_ota/opendtu-onbattery-generic_esp32_4mb_no_ota.bin
|
.pio/build/${{ matrix.environment }}/opendtu-${{ matrix.environment }}.factory.bin
|
||||||
.pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.factory.bin
|
|
||||||
|
|
||||||
release:
|
release:
|
||||||
name: Create Release
|
name: Create Release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [get_default_envs, build]
|
needs: [get_default_envs, build]
|
||||||
if: startsWith(github.ref, 'refs/tags/2')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Get tags
|
|
||||||
run: git fetch --force --tags origin
|
|
||||||
|
|
||||||
- name: Get openDTU core release
|
|
||||||
run: |
|
|
||||||
echo "OPEN_DTU_CORE_RELEASE=$(git for-each-ref --sort=creatordate --format '%(refname) %(creatordate)' refs/tags | grep 'refs/tags/v' | tail -1 | sed 's#.*/##' | sed 's/ .*//')" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
# disabled as uploading the changed gist failed repeatedly.
|
|
||||||
# maybe the token in secrets.GIST_SECRET has expired?
|
|
||||||
# need help from repo owner @helgeerbe to fix this.
|
|
||||||
# - name: Create openDTU-core-release-Badge
|
|
||||||
# uses: schneegans/dynamic-badges-action@v1.6.0
|
|
||||||
# with:
|
|
||||||
# auth: ${{ secrets.GIST_SECRET }}
|
|
||||||
# gistID: 68b47cc8c8994d04ab3a4fa9d8aee5e6
|
|
||||||
# filename: openDTUcoreRelease.json
|
|
||||||
# label: based on original OpenDTU
|
|
||||||
# message: ${{ env.OPEN_DTU_CORE_RELEASE }}
|
|
||||||
# color: lightblue
|
|
||||||
|
|
||||||
- name: Build Changelog
|
- name: Build Changelog
|
||||||
id: github_release
|
id: github_release
|
||||||
uses: mikepenz/release-changelog-builder-action@v4
|
uses: mikepenz/release-changelog-builder-action@v4
|
||||||
@ -179,7 +144,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
ls -R
|
ls -R
|
||||||
cd artifacts
|
cd artifacts
|
||||||
for i in */; do cp ${i}opendtu-onbattery-*.bin ./; done
|
for i in */; do cp ${i}opendtu-*.bin ./; done
|
||||||
|
|
||||||
- name: Create release
|
- name: Create release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
@ -189,4 +154,4 @@ jobs:
|
|||||||
files: |
|
files: |
|
||||||
artifacts/*.zip, artifacts/*.bin
|
artifacts/*.zip, artifacts/*.bin
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
@ -36,7 +36,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"template": "${{CHANGELOG}}",
|
"template": "${{CHANGELOG}}",
|
||||||
"pr_template": "- [${{TITLE}}](https://github.com/helgeerbe/OpenDTU-OnBattery/commit/${{MERGE_SHA}})",
|
"pr_template": "- [${{TITLE}}](https://github.com/tbnobody/OpenDTU/commit/${{MERGE_SHA}})",
|
||||||
"empty_template": "- no changes",
|
"empty_template": "- no changes",
|
||||||
"label_extractor": [
|
"label_extractor": [
|
||||||
{
|
{
|
||||||
|
|||||||
10
.github/workflows/cpplint.yml
vendored
@ -6,10 +6,6 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
# prevent push event from triggering if it's part of a local PR, see
|
|
||||||
# https://github.com/orgs/community/discussions/57827#discussioncomment-6579237
|
|
||||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
@ -19,9 +15,7 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install cpplint==1.6.1
|
pip install cpplint
|
||||||
- name: Linting
|
- name: Linting
|
||||||
run: |
|
run: |
|
||||||
cpplint --repository=. --recursive \
|
cpplint --repository=. --recursive --filter=-build/c++11,-runtime/references,-readability/braces,-whitespace,-legal,-build/include ./src ./include ./lib/Hoymiles ./lib/MqttSubscribeParser ./lib/TimeoutHelper ./lib/ResetReason
|
||||||
--filter=-build/c++11,-runtime/references,-readability/braces,-whitespace,-legal,-build/include \
|
|
||||||
./src ./include ./lib/Hoymiles ./lib/MqttSubscribeParser ./lib/TimeoutHelper ./lib/ResetReason
|
|
||||||
|
|||||||
4
.github/workflows/yarnlint.yml
vendored
@ -6,10 +6,6 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
# prevent push event from triggering if it's part of a local PR, see
|
|
||||||
# https://github.com/orgs/community/discussions/57827#discussioncomment-6579237
|
|
||||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
|
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: webapp
|
working-directory: webapp
|
||||||
|
|||||||
4
.github/workflows/yarnprettier.yml
vendored
@ -6,10 +6,6 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
# prevent push event from triggering if it's part of a local PR, see
|
|
||||||
# https://github.com/orgs/community/discussions/57827#discussioncomment-6579237
|
|
||||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
|
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: webapp
|
working-directory: webapp
|
||||||
|
|||||||
1
.gitignore
vendored
@ -7,5 +7,4 @@
|
|||||||
platformio-device-monitor*.log
|
platformio-device-monitor*.log
|
||||||
logs/device-monitor*.log
|
logs/device-monitor*.log
|
||||||
platformio_override.ini
|
platformio_override.ini
|
||||||
webapp_dist/
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
62
.vscode/settings.json
vendored
@ -1,63 +1,3 @@
|
|||||||
{
|
{
|
||||||
"C_Cpp.clang_format_style": "WebKit",
|
"C_Cpp.clang_format_style": "WebKit"
|
||||||
"files.associations": {
|
|
||||||
"*.tcc": "cpp",
|
|
||||||
"algorithm": "cpp",
|
|
||||||
"array": "cpp",
|
|
||||||
"atomic": "cpp",
|
|
||||||
"bitset": "cpp",
|
|
||||||
"cctype": "cpp",
|
|
||||||
"chrono": "cpp",
|
|
||||||
"clocale": "cpp",
|
|
||||||
"cmath": "cpp",
|
|
||||||
"condition_variable": "cpp",
|
|
||||||
"cstdarg": "cpp",
|
|
||||||
"cstddef": "cpp",
|
|
||||||
"cstdint": "cpp",
|
|
||||||
"cstdio": "cpp",
|
|
||||||
"cstdlib": "cpp",
|
|
||||||
"cstring": "cpp",
|
|
||||||
"ctime": "cpp",
|
|
||||||
"cwchar": "cpp",
|
|
||||||
"cwctype": "cpp",
|
|
||||||
"deque": "cpp",
|
|
||||||
"list": "cpp",
|
|
||||||
"unordered_map": "cpp",
|
|
||||||
"unordered_set": "cpp",
|
|
||||||
"vector": "cpp",
|
|
||||||
"exception": "cpp",
|
|
||||||
"functional": "cpp",
|
|
||||||
"iterator": "cpp",
|
|
||||||
"map": "cpp",
|
|
||||||
"memory": "cpp",
|
|
||||||
"memory_resource": "cpp",
|
|
||||||
"numeric": "cpp",
|
|
||||||
"optional": "cpp",
|
|
||||||
"random": "cpp",
|
|
||||||
"ratio": "cpp",
|
|
||||||
"regex": "cpp",
|
|
||||||
"string": "cpp",
|
|
||||||
"string_view": "cpp",
|
|
||||||
"system_error": "cpp",
|
|
||||||
"tuple": "cpp",
|
|
||||||
"type_traits": "cpp",
|
|
||||||
"utility": "cpp",
|
|
||||||
"fstream": "cpp",
|
|
||||||
"initializer_list": "cpp",
|
|
||||||
"iomanip": "cpp",
|
|
||||||
"iosfwd": "cpp",
|
|
||||||
"iostream": "cpp",
|
|
||||||
"istream": "cpp",
|
|
||||||
"limits": "cpp",
|
|
||||||
"mutex": "cpp",
|
|
||||||
"new": "cpp",
|
|
||||||
"ostream": "cpp",
|
|
||||||
"sstream": "cpp",
|
|
||||||
"stdexcept": "cpp",
|
|
||||||
"streambuf": "cpp",
|
|
||||||
"thread": "cpp",
|
|
||||||
"cinttypes": "cpp",
|
|
||||||
"typeinfo": "cpp",
|
|
||||||
"variant": "cpp"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
88
README.md
@ -1,71 +1,43 @@
|
|||||||
- [OpenDTU-OnBattery](#opendtu-onbattery)
|
# OpenDTU
|
||||||
- [What is OpenDTU-OnBattery](#what-is-opendtu-onbattery)
|
|
||||||
- [Documentation](#documentation)
|
|
||||||
- [State of the project](#state-of-the-project)
|
|
||||||
- [History of the project](#history-of-the-project)
|
|
||||||
- [Acknowledgments](#acknowledgments)
|
|
||||||
|
|
||||||
# OpenDTU-OnBattery
|
[](https://github.com/tbnobody/OpenDTU/actions/workflows/build.yml)
|
||||||
|
[](https://github.com/tbnobody/OpenDTU/actions/workflows/cpplint.yml)
|
||||||
|
[](https://github.com/tbnobody/OpenDTU/actions/workflows/yarnlint.yml)
|
||||||
|
|
||||||
This is a fork of [OpenDTU](https://github.com/tbnobody/OpenDTU).
|
## !! IMPORTANT UPGRADE NOTES !!
|
||||||
|
|
||||||
<!---
|
If you are upgrading from a version before 15.03.2023 you have to upgrade the partition table of the ESP32. Please follow the [this](docs/UpgradePartition.md) documentation!
|
||||||
disabled while "create release badge" action is broken, see .github/build.yml
|
|
||||||

|
|
||||||
--->
|
|
||||||
|
|
||||||
[](https://github.com/hoylabs/OpenDTU-OnBattery/actions/workflows/build.yml)
|
## Background
|
||||||
[](https://github.com/hoylabs/OpenDTU-OnBattery/actions/workflows/cpplint.yml)
|
|
||||||
[](https://github.com/hoylabs/OpenDTU-OnBattery/actions/workflows/yarnlint.yml)
|
|
||||||
|
|
||||||
## What is OpenDTU-OnBattery
|
This project was started from [this](https://www.mikrocontroller.net/topic/525778) discussion (Mikrocontroller.net).
|
||||||
|
It was the goal to replace the original Hoymiles DTU (Telemetry Gateway) with their cloud access. With a lot of reverse engineering the Hoymiles protocol was decrypted and analyzed.
|
||||||
OpenDTU-OnBattery is an extension of the original OpenDTU to support battery
|
|
||||||
chargers, battery management systems (BMS) and power meters on a single ESP32.
|
|
||||||
With the help of a Dynamic Power Limiter, the power production can be adjusted
|
|
||||||
to the actual consumption. In this way, it is possible to implement a zero
|
|
||||||
export policy.
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
The canonical documentation of OpenDTU-OnBattery is hosted at
|
The documentation can be found [here](https://tbnobody.github.io/OpenDTU-docs/).
|
||||||
[https://opendtu-onbattery.net](https://opendtu-onbattery.net).
|
Please feel free to support and create a PR in [this](https://github.com/tbnobody/OpenDTU-docs) repository to make the documentation even better.
|
||||||
|
|
||||||
You may find additional helpful information in the project's
|
## Breaking changes
|
||||||
community-maintained [Github
|
|
||||||
Wiki](https://github.com/hoylabs/OpenDTU-OnBattery/wiki).
|
|
||||||
|
|
||||||
To find out what's new or improved have a look at the changelog of the
|
Generated using: `git log --date=short --pretty=format:"* %h%x09%ad%x09%s" | grep BREAKING`
|
||||||
[releases](https://github.com/hoylabs/OpenDTU-OnBattery/releases).
|
|
||||||
|
|
||||||
## State of the project
|
```code
|
||||||
|
* 1b637f08 2024-01-30 BREAKING CHANGE: Web API Endpoint /api/livedata/status and /api/prometheus/metrics
|
||||||
|
* e1564780 2024-01-30 BREAKING CHANGE: Web API Endpoint /api/livedata/status and /api/prometheus/metrics
|
||||||
|
* f0b5542c 2024-01-30 BREAKING CHANGE: Web API Endpoint /api/livedata/status and /api/prometheus/metrics
|
||||||
|
* c27ecc36 2024-01-29 BREAKING CHANGE: Web API Endpoint /api/livedata/status
|
||||||
|
* 71d1b3b 2023-11-07 BREAKING CHANGE: Home Assistant Auto Discovery to new naming scheme
|
||||||
|
* 04f62e0 2023-04-20 BREAKING CHANGE: Web API Endpoint /api/eventlog/status no nested serial object
|
||||||
|
* 59f43a8 2023-04-17 BREAKING CHANGE: Web API Endpoint /api/devinfo/status requires GET parameter inv=
|
||||||
|
* 318136d 2023-03-15 BREAKING CHANGE: Updated partition table: Make sure you have a configuration backup and completly reflash the device!
|
||||||
|
* 3b7aef6 2023-02-13 BREAKING CHANGE: Web API!
|
||||||
|
* d4c838a 2023-02-06 BREAKING CHANGE: Prometheus API!
|
||||||
|
* daf847e 2022-11-14 BREAKING CHANGE: Removed deprecated config parsing method
|
||||||
|
* 69b675b 2022-11-01 BREAKING CHANGE: Structure WebAPI /api/livedata/status changed
|
||||||
|
* 27ed4e3 2022-10-31 BREAKING: Change power factor from percent value to value between 0 and 1
|
||||||
|
```
|
||||||
|
|
||||||
OpenDTU-OnBattery is actively maintained. Please note that OpenDTU-OnBattery
|
## Currently supported Inverters
|
||||||
may change significantly during its development. Bug reports, comments, feature
|
|
||||||
requests and pull requests are welcome!
|
|
||||||
|
|
||||||
## History of the project
|
A list of all currently supported inverters can be found [here](https://www.opendtu.solar/hardware/inverter_overview/)
|
||||||
|
|
||||||
The original OpenDTU project was started from [a discussion on
|
|
||||||
Mikrocontroller.net](https://www.mikrocontroller.net/topic/525778). It was the
|
|
||||||
goal to replace the original Hoymiles DTU (Telemetry Gateway) to avoid using
|
|
||||||
Hoymile's cloud. With a lot of reverse engineering the Hoymiles protocol was
|
|
||||||
decrypted and analyzed.
|
|
||||||
|
|
||||||
In the summer of 2022 @helgeerbe bought a Victron MPPT charge cntroller, and
|
|
||||||
didn't like the idea to set up a separate ESP32 to receive the charger's data.
|
|
||||||
He decided to fork OpenDTU and extend it with battery charger support and a
|
|
||||||
Dynamic Power Limiter.
|
|
||||||
|
|
||||||
In early October 2024, the project moved to the newly founded GitHub
|
|
||||||
organisation `hoylabs` and is since maintained by multiple community members.
|
|
||||||
|
|
||||||
## Acknowledgments
|
|
||||||
|
|
||||||
* Special thanks to Thomas Basler (@tbnobody), the author of the [upstream
|
|
||||||
project](https://github.com/tbnobody/OpenDTU), for hist continued effort!
|
|
||||||
* Thanks to @helgeerbe for starting OpenDTU-OnBattery and his dedication to the
|
|
||||||
project, as well as his trust in the current maintainers of the project,
|
|
||||||
which act as part of the `hoylabs` GitHub organisation.
|
|
||||||
* We like to thank all contributors. With your ideas and enhancements, you have
|
|
||||||
made OpenDTU-OnBattery much more than @helgeerbe originally had in mind.
|
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
# Moved
|
|
||||||
|
|
||||||
Have a look at the [OpenDTU-OnBattery documentation](https://opendtu-onbattery.net).
|
|
||||||
@ -1,3 +1,3 @@
|
|||||||
# Moved
|
# Device Profiles
|
||||||
|
|
||||||
Have a look at the [OpenDTU-OnBattery documentation](https://opendtu-onbattery.net/firmware/device_profiles/).
|
This documentation will has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/firmware/device_profiles/>
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
# Moved
|
# Display integration
|
||||||
|
|
||||||
Have a look at the [OpenDTU-OnBattery documentation](https://opendtu-onbattery.net/hardware/display/).
|
This documentation will has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/hardware/display/>
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
# Moved
|
# MQTT Topics
|
||||||
|
|
||||||
Have a look at the [OpenDTU-OnBattery documentation](https://opendtu-onbattery.net/firmware/mqtt_topics/).
|
This documentation will has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/firmware/mqtt_topics/>
|
||||||
|
|||||||
@ -1,3 +1,13 @@
|
|||||||
# Moved
|
# Documents - Table of content
|
||||||
|
|
||||||
Have a look at the [OpenDTU-OnBattery documentation](https://opendtu-onbattery.net).
|
More detailed descriptions for some topics can be found here.
|
||||||
|
|
||||||
|
## [Display Documentation](Display.md)
|
||||||
|
|
||||||
|
## [MQTT Topic Documentation](MQTT_Topics.md)
|
||||||
|
|
||||||
|
## [Web API Documentation](Web-API.md)
|
||||||
|
|
||||||
|
## [Device Profile Documentation](DeviceProfiles.md)
|
||||||
|
|
||||||
|
## [Builds](builds/README.md)
|
||||||
@ -1,3 +1,3 @@
|
|||||||
# Moved
|
# Upgrade Partition
|
||||||
|
|
||||||
Have a look at the [OpenDTU-OnBattery documentation](https://opendtu-onbattery.net/firmware/howto/upgrade_8mb/).
|
This documentation has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/firmware/howto/upgrade_partition/>
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
# Moved
|
# Web API
|
||||||
|
|
||||||
Have a look at the [OpenDTU-OnBattery documentation](https://opendtu-onbattery.net/firmware/web_api/).
|
This documentation will has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/firmware/web_api/>
|
||||||
|
|||||||
BIN
docs/Wiring_ESP32.fzz
Normal file
BIN
docs/Wiring_ESP32_Schematic.png
Normal file
|
After Width: | Height: | Size: 99 KiB |
BIN
docs/Wiring_ESP32_Symbol.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
docs/builds/202654506-8a4ac4ef-c883-490e-8ee1-1e1f7fa34972.jpg
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
28
docs/builds/README.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# Builds using different boards
|
||||||
|
|
||||||
|
## ESP32 Dev Board
|
||||||
|
|
||||||
|
### Build by @tbnobody, jan and @marove2000
|
||||||
|
* Used build environment: generic
|
||||||
|
* Case: https://www.printables.com/de/model/441037-opendtu-breakoutboard-case
|
||||||
|
* Soldering Kit: https://shop.blinkyparts.com/en/OpenDTU-Breakoutboard-Your-evaluation-for-your-balcony-solar-system/blink237542
|
||||||
|
* Breakout board: https://github.com/marove2000/openDTU_BreakoutBoard
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
### Build by @Marc--
|
||||||
|
* Used build environment: generic
|
||||||
|
* Case: https://www.thingiverse.com/thing:5435911
|
||||||
|

|
||||||
|
|
||||||
|
### Build by @cepresso
|
||||||
|
* Used build environment: generic
|
||||||
|
* Case: https://www.printables.com/de/model/293003-sol-opendtu-esp32-nrf24l01-case
|
||||||
|

|
||||||
|
|
||||||
|
## LILYGO® TTGO T-Internet-POE
|
||||||
|
### Build by @fromCologne
|
||||||
|
* Used build environment: LilyGO_T_ETH_POE
|
||||||
|
* Board info: http://www.lilygo.cn/claprod_view.aspx?TypeId=21&Id=1344&FId=t28:21:28
|
||||||
|
* Case: https://www.thingiverse.com/thing:5244895
|
||||||
|

|
||||||
BIN
docs/builds/large_display_PXL_20220715_145622277.jpg
Normal file
|
After Width: | Height: | Size: 137 KiB |
BIN
docs/builds/opendtu_breakoutboard.jpg
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
docs/builds/sol.webp
Normal file
|
After Width: | Height: | Size: 177 KiB |
BIN
docs/builds/thumbnail.jpg
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
docs/esp32_flash_download_tool.png
Normal file
|
After Width: | Height: | Size: 168 KiB |
@ -1,3 +0,0 @@
|
|||||||
# Moved
|
|
||||||
|
|
||||||
Have a look at the [OpenDTU-OnBattery documentation](https://opendtu-onbattery.net/firmware/).
|
|
||||||
BIN
docs/nodemcu-esp32.png
Normal file
|
After Width: | Height: | Size: 173 KiB |
BIN
docs/nrf24l01plus.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
docs/screenshots/01_LiveView.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
docs/screenshots/02_NetworkAdmin.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
docs/screenshots/03_NtpAdmin.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
docs/screenshots/04_MqttAdmin.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
docs/screenshots/05_InverterAdmin.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
docs/screenshots/06_DtuAdmin.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
docs/screenshots/07_FirmwareUpgrade.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
docs/screenshots/08_NetworkInfo.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
docs/screenshots/09_NtpInfo.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
docs/screenshots/10_MqttInfo.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
docs/screenshots/11_SystemInfo.png
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
docs/screenshots/12_Eventlog.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
docs/screenshots/13_InverterSettings.png
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
docs/screenshots/14_ConfigManagement.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
docs/screenshots/15_LimitSettings.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
docs/screenshots/16_PowerSettings.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
docs/screenshots/17_InverterInfo.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
docs/screenshots/18_Console.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
docs/screenshots/19_Reboot.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
docs/screenshots/20_DeviceManager_Pin.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
docs/screenshots/21_DeviceManager_Display.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
docs/screenshots/22_Security.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
91
docs/screenshots/README.md
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
# OpenDTU Screenshots
|
||||||
|
|
||||||
|
here are some screenshots of OpenDTU's web interface.
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|

|
||||||
@ -1,36 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
|
|
||||||
#include "BatteryStats.h"
|
|
||||||
|
|
||||||
class BatteryProvider {
|
|
||||||
public:
|
|
||||||
// returns true if the provider is ready for use, false otherwise
|
|
||||||
virtual bool init(bool verboseLogging) = 0;
|
|
||||||
virtual void deinit() = 0;
|
|
||||||
virtual void loop() = 0;
|
|
||||||
virtual std::shared_ptr<BatteryStats> getStats() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class BatteryClass {
|
|
||||||
public:
|
|
||||||
void init(Scheduler&);
|
|
||||||
void updateSettings();
|
|
||||||
|
|
||||||
float getDischargeCurrentLimit();
|
|
||||||
|
|
||||||
std::shared_ptr<BatteryStats const> getStats() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void loop();
|
|
||||||
|
|
||||||
Task _loopTask;
|
|
||||||
mutable std::mutex _mutex;
|
|
||||||
std::unique_ptr<BatteryProvider> _upProvider = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern BatteryClass Battery;
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Battery.h"
|
|
||||||
#include <driver/twai.h>
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
class BatteryCanReceiver : public BatteryProvider {
|
|
||||||
public:
|
|
||||||
bool init(bool verboseLogging, char const* providerName);
|
|
||||||
void deinit() final;
|
|
||||||
void loop() final;
|
|
||||||
|
|
||||||
virtual void onMessage(twai_message_t rx_message) = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
uint8_t readUnsignedInt8(uint8_t *data);
|
|
||||||
uint16_t readUnsignedInt16(uint8_t *data);
|
|
||||||
int16_t readSignedInt16(uint8_t *data);
|
|
||||||
uint32_t readUnsignedInt32(uint8_t *data);
|
|
||||||
int32_t readSignedInt24(uint8_t *data);
|
|
||||||
float scaleValue(int32_t value, float factor);
|
|
||||||
bool getBit(uint8_t value, uint8_t bit);
|
|
||||||
|
|
||||||
bool _verboseLogging = true;
|
|
||||||
|
|
||||||
private:
|
|
||||||
char const* _providerName = "Battery CAN";
|
|
||||||
};
|
|
||||||
@ -1,325 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "AsyncJson.h"
|
|
||||||
#include "Arduino.h"
|
|
||||||
#include "JkBmsDataPoints.h"
|
|
||||||
#include "VeDirectShuntController.h"
|
|
||||||
#include <cfloat>
|
|
||||||
|
|
||||||
// mandatory interface for all kinds of batteries
|
|
||||||
class BatteryStats {
|
|
||||||
public:
|
|
||||||
String const& getManufacturer() const { return _manufacturer; }
|
|
||||||
|
|
||||||
// the last time *any* data was updated
|
|
||||||
uint32_t getAgeSeconds() const { return (millis() - _lastUpdate) / 1000; }
|
|
||||||
bool updateAvailable(uint32_t since) const;
|
|
||||||
|
|
||||||
float getSoC() const { return _soc; }
|
|
||||||
uint32_t getSoCAgeSeconds() const { return (millis() - _lastUpdateSoC) / 1000; }
|
|
||||||
uint8_t getSoCPrecision() const { return _socPrecision; }
|
|
||||||
|
|
||||||
float getVoltage() const { return _voltage; }
|
|
||||||
uint32_t getVoltageAgeSeconds() const { return (millis() - _lastUpdateVoltage) / 1000; }
|
|
||||||
|
|
||||||
float getChargeCurrent() const { return _current; };
|
|
||||||
uint8_t getChargeCurrentPrecision() const { return _currentPrecision; }
|
|
||||||
|
|
||||||
float getDischargeCurrentLimit() const { return _dischargeCurrentLimit; };
|
|
||||||
uint32_t getDischargeCurrentLimitAgeSeconds() const { return (millis() - _lastUpdateDischargeCurrentLimit) / 1000; }
|
|
||||||
|
|
||||||
// convert stats to JSON for web application live view
|
|
||||||
virtual void getLiveViewData(JsonVariant& root) const;
|
|
||||||
|
|
||||||
void mqttLoop();
|
|
||||||
|
|
||||||
// the interval at which all battery data will be re-published, even
|
|
||||||
// if they did not change. used to calculate Home Assistent expiration.
|
|
||||||
virtual uint32_t getMqttFullPublishIntervalMs() const;
|
|
||||||
|
|
||||||
bool isSoCValid() const { return _lastUpdateSoC > 0; }
|
|
||||||
bool isVoltageValid() const { return _lastUpdateVoltage > 0; }
|
|
||||||
bool isCurrentValid() const { return _lastUpdateCurrent > 0; }
|
|
||||||
bool isDischargeCurrentLimitValid() const { return _lastUpdateDischargeCurrentLimit > 0; }
|
|
||||||
|
|
||||||
// returns true if the battery reached a critically low voltage/SoC,
|
|
||||||
// such that it is in need of charging to prevent degredation.
|
|
||||||
virtual bool getImmediateChargingRequest() const { return false; };
|
|
||||||
|
|
||||||
virtual float getChargeCurrentLimitation() const { return FLT_MAX; };
|
|
||||||
|
|
||||||
virtual bool supportsAlarmsAndWarnings() const { return true; };
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void mqttPublish() const;
|
|
||||||
|
|
||||||
void setSoC(float soc, uint8_t precision, uint32_t timestamp) {
|
|
||||||
_soc = soc;
|
|
||||||
_socPrecision = precision;
|
|
||||||
_lastUpdateSoC = _lastUpdate = timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setVoltage(float voltage, uint32_t timestamp) {
|
|
||||||
_voltage = voltage;
|
|
||||||
_lastUpdateVoltage = _lastUpdate = timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setCurrent(float current, uint8_t precision, uint32_t timestamp) {
|
|
||||||
_current = current;
|
|
||||||
_currentPrecision = precision;
|
|
||||||
_lastUpdateCurrent = _lastUpdate = timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setDischargeCurrentLimit(float dischargeCurrentLimit, uint32_t timestamp) {
|
|
||||||
_dischargeCurrentLimit = dischargeCurrentLimit;
|
|
||||||
_lastUpdateDischargeCurrentLimit = _lastUpdate = timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setManufacturer(const String& m);
|
|
||||||
|
|
||||||
String _hwversion = "";
|
|
||||||
String _fwversion = "";
|
|
||||||
String _serial = "";
|
|
||||||
uint32_t _lastUpdate = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
String _manufacturer = "unknown";
|
|
||||||
uint32_t _lastMqttPublish = 0;
|
|
||||||
float _soc = 0;
|
|
||||||
uint8_t _socPrecision = 0; // decimal places
|
|
||||||
uint32_t _lastUpdateSoC = 0;
|
|
||||||
float _voltage = 0; // total battery pack voltage
|
|
||||||
uint32_t _lastUpdateVoltage = 0;
|
|
||||||
|
|
||||||
// total current into (positive) or from (negative)
|
|
||||||
// the battery, i.e., the charging current
|
|
||||||
float _current = 0;
|
|
||||||
uint8_t _currentPrecision = 0; // decimal places
|
|
||||||
uint32_t _lastUpdateCurrent = 0;
|
|
||||||
|
|
||||||
float _dischargeCurrentLimit = 0;
|
|
||||||
uint32_t _lastUpdateDischargeCurrentLimit = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class PylontechBatteryStats : public BatteryStats {
|
|
||||||
friend class PylontechCanReceiver;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void getLiveViewData(JsonVariant& root) const final;
|
|
||||||
void mqttPublish() const final;
|
|
||||||
bool getImmediateChargingRequest() const { return _chargeImmediately; } ;
|
|
||||||
float getChargeCurrentLimitation() const { return _chargeCurrentLimitation; } ;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void setLastUpdate(uint32_t ts) { _lastUpdate = ts; }
|
|
||||||
|
|
||||||
float _chargeVoltage;
|
|
||||||
float _chargeCurrentLimitation;
|
|
||||||
float _dischargeVoltageLimitation;
|
|
||||||
uint16_t _stateOfHealth;
|
|
||||||
float _temperature;
|
|
||||||
|
|
||||||
bool _alarmOverCurrentDischarge;
|
|
||||||
bool _alarmOverCurrentCharge;
|
|
||||||
bool _alarmUnderTemperature;
|
|
||||||
bool _alarmOverTemperature;
|
|
||||||
bool _alarmUnderVoltage;
|
|
||||||
bool _alarmOverVoltage;
|
|
||||||
bool _alarmBmsInternal;
|
|
||||||
|
|
||||||
bool _warningHighCurrentDischarge;
|
|
||||||
bool _warningHighCurrentCharge;
|
|
||||||
bool _warningLowTemperature;
|
|
||||||
bool _warningHighTemperature;
|
|
||||||
bool _warningLowVoltage;
|
|
||||||
bool _warningHighVoltage;
|
|
||||||
bool _warningBmsInternal;
|
|
||||||
|
|
||||||
bool _chargeEnabled;
|
|
||||||
bool _dischargeEnabled;
|
|
||||||
bool _chargeImmediately;
|
|
||||||
|
|
||||||
uint8_t _moduleCount;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SBSBatteryStats : public BatteryStats {
|
|
||||||
friend class SBSCanReceiver;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void getLiveViewData(JsonVariant& root) const final;
|
|
||||||
void mqttPublish() const final;
|
|
||||||
float getChargeCurrent() const { return _current; } ;
|
|
||||||
float getChargeCurrentLimitation() const { return _chargeCurrentLimitation; } ;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void setLastUpdate(uint32_t ts) { _lastUpdate = ts; }
|
|
||||||
|
|
||||||
float _chargeVoltage;
|
|
||||||
float _chargeCurrentLimitation;
|
|
||||||
float _dischargeCurrentLimitation;
|
|
||||||
uint16_t _stateOfHealth;
|
|
||||||
float _current;
|
|
||||||
float _temperature;
|
|
||||||
|
|
||||||
bool _alarmUnderTemperature;
|
|
||||||
bool _alarmOverTemperature;
|
|
||||||
bool _alarmUnderVoltage;
|
|
||||||
bool _alarmOverVoltage;
|
|
||||||
bool _alarmBmsInternal;
|
|
||||||
|
|
||||||
bool _warningHighCurrentDischarge;
|
|
||||||
bool _warningHighCurrentCharge;
|
|
||||||
|
|
||||||
bool _chargeEnabled;
|
|
||||||
bool _dischargeEnabled;
|
|
||||||
};
|
|
||||||
|
|
||||||
class PytesBatteryStats : public BatteryStats {
|
|
||||||
friend class PytesCanReceiver;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void getLiveViewData(JsonVariant& root) const final;
|
|
||||||
void mqttPublish() const final;
|
|
||||||
bool getImmediateChargingRequest() const { return _chargeImmediately; };
|
|
||||||
float getChargeCurrentLimitation() const { return _chargeCurrentLimit; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
void setLastUpdate(uint32_t ts) { _lastUpdate = ts; }
|
|
||||||
void updateSerial() {
|
|
||||||
if (!_serialPart1.isEmpty() && !_serialPart2.isEmpty()) {
|
|
||||||
_serial = _serialPart1 + _serialPart2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String _serialPart1 = "";
|
|
||||||
String _serialPart2 = "";
|
|
||||||
|
|
||||||
float _chargeVoltageLimit;
|
|
||||||
float _chargeCurrentLimit;
|
|
||||||
float _dischargeVoltageLimit;
|
|
||||||
|
|
||||||
uint16_t _stateOfHealth;
|
|
||||||
int _chargeCycles = -1;
|
|
||||||
int _balance = -1;
|
|
||||||
|
|
||||||
float _temperature;
|
|
||||||
|
|
||||||
uint16_t _cellMinMilliVolt;
|
|
||||||
uint16_t _cellMaxMilliVolt;
|
|
||||||
float _cellMinTemperature;
|
|
||||||
float _cellMaxTemperature;
|
|
||||||
|
|
||||||
String _cellMinVoltageName;
|
|
||||||
String _cellMaxVoltageName;
|
|
||||||
String _cellMinTemperatureName;
|
|
||||||
String _cellMaxTemperatureName;
|
|
||||||
|
|
||||||
uint8_t _moduleCountOnline;
|
|
||||||
uint8_t _moduleCountOffline;
|
|
||||||
|
|
||||||
uint8_t _moduleCountBlockingCharge;
|
|
||||||
uint8_t _moduleCountBlockingDischarge;
|
|
||||||
|
|
||||||
float _totalCapacity;
|
|
||||||
float _availableCapacity;
|
|
||||||
uint8_t _capacityPrecision = 0; // decimal places
|
|
||||||
|
|
||||||
float _chargedEnergy = -1;
|
|
||||||
float _dischargedEnergy = -1;
|
|
||||||
|
|
||||||
bool _alarmUnderVoltage;
|
|
||||||
bool _alarmOverVoltage;
|
|
||||||
bool _alarmOverCurrentCharge;
|
|
||||||
bool _alarmOverCurrentDischarge;
|
|
||||||
bool _alarmUnderTemperature;
|
|
||||||
bool _alarmOverTemperature;
|
|
||||||
bool _alarmUnderTemperatureCharge;
|
|
||||||
bool _alarmOverTemperatureCharge;
|
|
||||||
bool _alarmInternalFailure;
|
|
||||||
bool _alarmCellImbalance;
|
|
||||||
|
|
||||||
bool _warningLowVoltage;
|
|
||||||
bool _warningHighVoltage;
|
|
||||||
bool _warningHighChargeCurrent;
|
|
||||||
bool _warningHighDischargeCurrent;
|
|
||||||
bool _warningLowTemperature;
|
|
||||||
bool _warningHighTemperature;
|
|
||||||
bool _warningLowTemperatureCharge;
|
|
||||||
bool _warningHighTemperatureCharge;
|
|
||||||
bool _warningInternalFailure;
|
|
||||||
bool _warningCellImbalance;
|
|
||||||
|
|
||||||
bool _chargeImmediately;
|
|
||||||
};
|
|
||||||
|
|
||||||
class JkBmsBatteryStats : public BatteryStats {
|
|
||||||
public:
|
|
||||||
void getLiveViewData(JsonVariant& root) const final {
|
|
||||||
getJsonData(root, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void getInfoViewData(JsonVariant& root) const {
|
|
||||||
getJsonData(root, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void mqttPublish() const final;
|
|
||||||
|
|
||||||
uint32_t getMqttFullPublishIntervalMs() const final { return 60 * 1000; }
|
|
||||||
|
|
||||||
void updateFrom(JkBms::DataPointContainer const& dp);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void getJsonData(JsonVariant& root, bool verbose) const;
|
|
||||||
|
|
||||||
JkBms::DataPointContainer _dataPoints;
|
|
||||||
mutable uint32_t _lastMqttPublish = 0;
|
|
||||||
mutable uint32_t _lastFullMqttPublish = 0;
|
|
||||||
|
|
||||||
uint16_t _cellMinMilliVolt = 0;
|
|
||||||
uint16_t _cellAvgMilliVolt = 0;
|
|
||||||
uint16_t _cellMaxMilliVolt = 0;
|
|
||||||
uint32_t _cellVoltageTimestamp = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class VictronSmartShuntStats : public BatteryStats {
|
|
||||||
public:
|
|
||||||
void getLiveViewData(JsonVariant& root) const final;
|
|
||||||
void mqttPublish() const final;
|
|
||||||
|
|
||||||
void updateFrom(VeDirectShuntController::data_t const& shuntData);
|
|
||||||
|
|
||||||
private:
|
|
||||||
float _temperature;
|
|
||||||
bool _tempPresent;
|
|
||||||
uint8_t _chargeCycles;
|
|
||||||
uint32_t _timeToGo;
|
|
||||||
float _chargedEnergy;
|
|
||||||
float _dischargedEnergy;
|
|
||||||
int32_t _instantaneousPower;
|
|
||||||
float _midpointVoltage;
|
|
||||||
float _midpointDeviation;
|
|
||||||
float _consumedAmpHours;
|
|
||||||
int32_t _lastFullCharge;
|
|
||||||
|
|
||||||
bool _alarmLowVoltage;
|
|
||||||
bool _alarmHighVoltage;
|
|
||||||
bool _alarmLowSOC;
|
|
||||||
bool _alarmLowTemperature;
|
|
||||||
bool _alarmHighTemperature;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MqttBatteryStats : public BatteryStats {
|
|
||||||
friend class MqttBattery;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// since the source of information was MQTT in the first place,
|
|
||||||
// we do NOT publish the same data under a different topic.
|
|
||||||
void mqttPublish() const final { }
|
|
||||||
|
|
||||||
void getLiveViewData(JsonVariant& root) const final;
|
|
||||||
|
|
||||||
bool supportsAlarmsAndWarnings() const final { return false; }
|
|
||||||
};
|
|
||||||
@ -3,17 +3,17 @@
|
|||||||
|
|
||||||
#include "PinMapping.h"
|
#include "PinMapping.h"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <ArduinoJson.h>
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
#define CONFIG_FILENAME "/config.json"
|
#define CONFIG_FILENAME "/config.json"
|
||||||
#define CONFIG_VERSION 0x00011c00 // 0.1.28 // make sure to clean all after change
|
#define CONFIG_VERSION 0x00011d00 // 0.1.29 // make sure to clean all after change
|
||||||
|
|
||||||
#define WIFI_MAX_SSID_STRLEN 32
|
#define WIFI_MAX_SSID_STRLEN 32
|
||||||
#define WIFI_MAX_PASSWORD_STRLEN 64
|
#define WIFI_MAX_PASSWORD_STRLEN 64
|
||||||
#define WIFI_MAX_HOSTNAME_STRLEN 31
|
#define WIFI_MAX_HOSTNAME_STRLEN 31
|
||||||
|
|
||||||
#define SYSLOG_MAX_HOSTNAME_STRLEN 128
|
|
||||||
|
|
||||||
#define NTP_MAX_SERVER_STRLEN 31
|
#define NTP_MAX_SERVER_STRLEN 31
|
||||||
#define NTP_MAX_TIMEZONE_STRLEN 50
|
#define NTP_MAX_TIMEZONE_STRLEN 50
|
||||||
#define NTP_MAX_TIMEZONEDESCR_STRLEN 50
|
#define NTP_MAX_TIMEZONEDESCR_STRLEN 50
|
||||||
@ -22,7 +22,7 @@
|
|||||||
#define MQTT_MAX_CLIENTID_STRLEN 64
|
#define MQTT_MAX_CLIENTID_STRLEN 64
|
||||||
#define MQTT_MAX_USERNAME_STRLEN 64
|
#define MQTT_MAX_USERNAME_STRLEN 64
|
||||||
#define MQTT_MAX_PASSWORD_STRLEN 64
|
#define MQTT_MAX_PASSWORD_STRLEN 64
|
||||||
#define MQTT_MAX_TOPIC_STRLEN 256
|
#define MQTT_MAX_TOPIC_STRLEN 32
|
||||||
#define MQTT_MAX_LWTVALUE_STRLEN 20
|
#define MQTT_MAX_LWTVALUE_STRLEN 20
|
||||||
#define MQTT_MAX_CERT_STRLEN 2560
|
#define MQTT_MAX_CERT_STRLEN 2560
|
||||||
|
|
||||||
@ -33,17 +33,7 @@
|
|||||||
#define CHAN_MAX_NAME_STRLEN 31
|
#define CHAN_MAX_NAME_STRLEN 31
|
||||||
|
|
||||||
#define DEV_MAX_MAPPING_NAME_STRLEN 63
|
#define DEV_MAX_MAPPING_NAME_STRLEN 63
|
||||||
|
#define LOCALE_STRLEN 2
|
||||||
#define HTTP_REQUEST_MAX_URL_STRLEN 1024
|
|
||||||
#define HTTP_REQUEST_MAX_USERNAME_STRLEN 64
|
|
||||||
#define HTTP_REQUEST_MAX_PASSWORD_STRLEN 64
|
|
||||||
#define HTTP_REQUEST_MAX_HEADER_KEY_STRLEN 64
|
|
||||||
#define HTTP_REQUEST_MAX_HEADER_VALUE_STRLEN 256
|
|
||||||
|
|
||||||
#define POWERMETER_MQTT_MAX_VALUES 3
|
|
||||||
#define POWERMETER_HTTP_JSON_MAX_VALUES 3
|
|
||||||
#define POWERMETER_HTTP_JSON_MAX_PATH_STRLEN 256
|
|
||||||
#define BATTERY_JSON_MAX_PATH_STRLEN 128
|
|
||||||
|
|
||||||
struct CHANNEL_CONFIG_T {
|
struct CHANNEL_CONFIG_T {
|
||||||
uint16_t MaxChannelPower;
|
uint16_t MaxChannelPower;
|
||||||
@ -67,91 +57,6 @@ struct INVERTER_CONFIG_T {
|
|||||||
CHANNEL_CONFIG_T channel[INV_MAX_CHAN_COUNT];
|
CHANNEL_CONFIG_T channel[INV_MAX_CHAN_COUNT];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HTTP_REQUEST_CONFIG_T {
|
|
||||||
char Url[HTTP_REQUEST_MAX_URL_STRLEN + 1];
|
|
||||||
|
|
||||||
enum Auth { None, Basic, Digest };
|
|
||||||
Auth AuthType;
|
|
||||||
|
|
||||||
char Username[HTTP_REQUEST_MAX_USERNAME_STRLEN + 1];
|
|
||||||
char Password[HTTP_REQUEST_MAX_PASSWORD_STRLEN + 1];
|
|
||||||
char HeaderKey[HTTP_REQUEST_MAX_HEADER_KEY_STRLEN + 1];
|
|
||||||
char HeaderValue[HTTP_REQUEST_MAX_HEADER_VALUE_STRLEN + 1];
|
|
||||||
uint16_t Timeout;
|
|
||||||
};
|
|
||||||
using HttpRequestConfig = struct HTTP_REQUEST_CONFIG_T;
|
|
||||||
|
|
||||||
struct POWERMETER_MQTT_VALUE_T {
|
|
||||||
char Topic[MQTT_MAX_TOPIC_STRLEN + 1];
|
|
||||||
char JsonPath[POWERMETER_HTTP_JSON_MAX_PATH_STRLEN + 1];
|
|
||||||
|
|
||||||
enum Unit { Watts = 0, MilliWatts = 1, KiloWatts = 2 };
|
|
||||||
Unit PowerUnit;
|
|
||||||
|
|
||||||
bool SignInverted;
|
|
||||||
};
|
|
||||||
using PowerMeterMqttValue = struct POWERMETER_MQTT_VALUE_T;
|
|
||||||
|
|
||||||
struct POWERMETER_MQTT_CONFIG_T {
|
|
||||||
PowerMeterMqttValue Values[POWERMETER_MQTT_MAX_VALUES];
|
|
||||||
};
|
|
||||||
using PowerMeterMqttConfig = struct POWERMETER_MQTT_CONFIG_T;
|
|
||||||
|
|
||||||
struct POWERMETER_SERIAL_SDM_CONFIG_T {
|
|
||||||
uint32_t Address;
|
|
||||||
uint32_t PollingInterval;
|
|
||||||
};
|
|
||||||
using PowerMeterSerialSdmConfig = struct POWERMETER_SERIAL_SDM_CONFIG_T;
|
|
||||||
|
|
||||||
struct POWERMETER_HTTP_JSON_VALUE_T {
|
|
||||||
HttpRequestConfig HttpRequest;
|
|
||||||
bool Enabled;
|
|
||||||
char JsonPath[POWERMETER_HTTP_JSON_MAX_PATH_STRLEN + 1];
|
|
||||||
|
|
||||||
enum Unit { Watts = 0, MilliWatts = 1, KiloWatts = 2 };
|
|
||||||
Unit PowerUnit;
|
|
||||||
|
|
||||||
bool SignInverted;
|
|
||||||
};
|
|
||||||
using PowerMeterHttpJsonValue = struct POWERMETER_HTTP_JSON_VALUE_T;
|
|
||||||
|
|
||||||
struct POWERMETER_HTTP_JSON_CONFIG_T {
|
|
||||||
uint32_t PollingInterval;
|
|
||||||
bool IndividualRequests;
|
|
||||||
PowerMeterHttpJsonValue Values[POWERMETER_HTTP_JSON_MAX_VALUES];
|
|
||||||
};
|
|
||||||
using PowerMeterHttpJsonConfig = struct POWERMETER_HTTP_JSON_CONFIG_T;
|
|
||||||
|
|
||||||
struct POWERMETER_HTTP_SML_CONFIG_T {
|
|
||||||
uint32_t PollingInterval;
|
|
||||||
HttpRequestConfig HttpRequest;
|
|
||||||
};
|
|
||||||
using PowerMeterHttpSmlConfig = struct POWERMETER_HTTP_SML_CONFIG_T;
|
|
||||||
|
|
||||||
enum BatteryVoltageUnit { Volts = 0, DeciVolts = 1, CentiVolts = 2, MilliVolts = 3 };
|
|
||||||
|
|
||||||
enum BatteryAmperageUnit { Amps = 0, MilliAmps = 1 };
|
|
||||||
|
|
||||||
struct BATTERY_CONFIG_T {
|
|
||||||
bool Enabled;
|
|
||||||
bool VerboseLogging;
|
|
||||||
uint8_t Provider;
|
|
||||||
uint8_t JkBmsInterface;
|
|
||||||
uint8_t JkBmsPollingInterval;
|
|
||||||
char MqttSocTopic[MQTT_MAX_TOPIC_STRLEN + 1];
|
|
||||||
char MqttSocJsonPath[BATTERY_JSON_MAX_PATH_STRLEN + 1];
|
|
||||||
char MqttVoltageTopic[MQTT_MAX_TOPIC_STRLEN + 1];
|
|
||||||
char MqttVoltageJsonPath[BATTERY_JSON_MAX_PATH_STRLEN + 1];
|
|
||||||
BatteryVoltageUnit MqttVoltageUnit;
|
|
||||||
bool EnableDischargeCurrentLimit;
|
|
||||||
float DischargeCurrentLimit;
|
|
||||||
bool UseBatteryReportedDischargeCurrentLimit;
|
|
||||||
char MqttDischargeCurrentTopic[MQTT_MAX_TOPIC_STRLEN + 1];
|
|
||||||
char MqttDischargeCurrentJsonPath[BATTERY_JSON_MAX_PATH_STRLEN + 1];
|
|
||||||
BatteryAmperageUnit MqttAmperageUnit;
|
|
||||||
};
|
|
||||||
using BatteryConfig = struct BATTERY_CONFIG_T;
|
|
||||||
|
|
||||||
struct CONFIG_T {
|
struct CONFIG_T {
|
||||||
struct {
|
struct {
|
||||||
uint32_t Version;
|
uint32_t Version;
|
||||||
@ -175,12 +80,6 @@ struct CONFIG_T {
|
|||||||
bool Enabled;
|
bool Enabled;
|
||||||
} Mdns;
|
} Mdns;
|
||||||
|
|
||||||
struct {
|
|
||||||
bool Enabled;
|
|
||||||
char Hostname[SYSLOG_MAX_HOSTNAME_STRLEN + 1];
|
|
||||||
uint16_t Port;
|
|
||||||
} Syslog;
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
char Server[NTP_MAX_SERVER_STRLEN + 1];
|
char Server[NTP_MAX_SERVER_STRLEN + 1];
|
||||||
char Timezone[NTP_MAX_TIMEZONE_STRLEN + 1];
|
char Timezone[NTP_MAX_TIMEZONE_STRLEN + 1];
|
||||||
@ -193,7 +92,6 @@ struct CONFIG_T {
|
|||||||
struct {
|
struct {
|
||||||
bool Enabled;
|
bool Enabled;
|
||||||
char Hostname[MQTT_MAX_HOSTNAME_STRLEN + 1];
|
char Hostname[MQTT_MAX_HOSTNAME_STRLEN + 1];
|
||||||
bool VerboseLogging;
|
|
||||||
uint32_t Port;
|
uint32_t Port;
|
||||||
char ClientId[MQTT_MAX_CLIENTID_STRLEN + 1];
|
char ClientId[MQTT_MAX_CLIENTID_STRLEN + 1];
|
||||||
char Username[MQTT_MAX_USERNAME_STRLEN + 1];
|
char Username[MQTT_MAX_USERNAME_STRLEN + 1];
|
||||||
@ -238,7 +136,6 @@ struct CONFIG_T {
|
|||||||
uint32_t Frequency;
|
uint32_t Frequency;
|
||||||
uint8_t CountryMode;
|
uint8_t CountryMode;
|
||||||
} Cmt;
|
} Cmt;
|
||||||
bool VerboseLogging;
|
|
||||||
} Dtu;
|
} Dtu;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@ -251,7 +148,7 @@ struct CONFIG_T {
|
|||||||
bool ScreenSaver;
|
bool ScreenSaver;
|
||||||
uint8_t Rotation;
|
uint8_t Rotation;
|
||||||
uint8_t Contrast;
|
uint8_t Contrast;
|
||||||
uint8_t Language;
|
char Locale[LOCALE_STRLEN + 1];
|
||||||
struct {
|
struct {
|
||||||
uint32_t Duration;
|
uint32_t Duration;
|
||||||
uint8_t Mode;
|
uint8_t Mode;
|
||||||
@ -262,98 +159,38 @@ struct CONFIG_T {
|
|||||||
uint8_t Brightness;
|
uint8_t Brightness;
|
||||||
} Led_Single[PINMAPPING_LED_COUNT];
|
} Led_Single[PINMAPPING_LED_COUNT];
|
||||||
|
|
||||||
struct {
|
|
||||||
bool Enabled;
|
|
||||||
bool VerboseLogging;
|
|
||||||
bool UpdatesOnly;
|
|
||||||
} Vedirect;
|
|
||||||
|
|
||||||
struct PowerMeterConfig {
|
|
||||||
bool Enabled;
|
|
||||||
bool VerboseLogging;
|
|
||||||
uint32_t Source;
|
|
||||||
PowerMeterMqttConfig Mqtt;
|
|
||||||
PowerMeterSerialSdmConfig SerialSdm;
|
|
||||||
PowerMeterHttpJsonConfig HttpJson;
|
|
||||||
PowerMeterHttpSmlConfig HttpSml;
|
|
||||||
} PowerMeter;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
bool Enabled;
|
|
||||||
bool VerboseLogging;
|
|
||||||
bool SolarPassThroughEnabled;
|
|
||||||
uint8_t SolarPassThroughLosses;
|
|
||||||
bool BatteryAlwaysUseAtNight;
|
|
||||||
uint32_t Interval;
|
|
||||||
bool IsInverterBehindPowerMeter;
|
|
||||||
bool IsInverterSolarPowered;
|
|
||||||
bool UseOverscalingToCompensateShading;
|
|
||||||
uint64_t InverterId;
|
|
||||||
uint8_t InverterChannelId;
|
|
||||||
int32_t TargetPowerConsumption;
|
|
||||||
int32_t TargetPowerConsumptionHysteresis;
|
|
||||||
int32_t LowerPowerLimit;
|
|
||||||
int32_t BaseLoadLimit;
|
|
||||||
int32_t UpperPowerLimit;
|
|
||||||
bool IgnoreSoc;
|
|
||||||
uint32_t BatterySocStartThreshold;
|
|
||||||
uint32_t BatterySocStopThreshold;
|
|
||||||
float VoltageStartThreshold;
|
|
||||||
float VoltageStopThreshold;
|
|
||||||
float VoltageLoadCorrectionFactor;
|
|
||||||
int8_t RestartHour;
|
|
||||||
uint32_t FullSolarPassThroughSoc;
|
|
||||||
float FullSolarPassThroughStartVoltage;
|
|
||||||
float FullSolarPassThroughStopVoltage;
|
|
||||||
} PowerLimiter;
|
|
||||||
|
|
||||||
BatteryConfig Battery;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
bool Enabled;
|
|
||||||
bool VerboseLogging;
|
|
||||||
uint32_t CAN_Controller_Frequency;
|
|
||||||
bool Auto_Power_Enabled;
|
|
||||||
bool Auto_Power_BatterySoC_Limits_Enabled;
|
|
||||||
bool Emergency_Charge_Enabled;
|
|
||||||
float Auto_Power_Voltage_Limit;
|
|
||||||
float Auto_Power_Enable_Voltage_Limit;
|
|
||||||
float Auto_Power_Lower_Power_Limit;
|
|
||||||
float Auto_Power_Upper_Power_Limit;
|
|
||||||
uint8_t Auto_Power_Stop_BatterySoC_Threshold;
|
|
||||||
float Auto_Power_Target_Power_Consumption;
|
|
||||||
} Huawei;
|
|
||||||
|
|
||||||
|
|
||||||
INVERTER_CONFIG_T Inverter[INV_MAX_COUNT];
|
INVERTER_CONFIG_T Inverter[INV_MAX_COUNT];
|
||||||
char Dev_PinMapping[DEV_MAX_MAPPING_NAME_STRLEN + 1];
|
char Dev_PinMapping[DEV_MAX_MAPPING_NAME_STRLEN + 1];
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConfigurationClass {
|
class ConfigurationClass {
|
||||||
public:
|
public:
|
||||||
void init();
|
void init(Scheduler& scheduler);
|
||||||
bool read();
|
bool read();
|
||||||
bool write();
|
bool write();
|
||||||
void migrate();
|
void migrate();
|
||||||
CONFIG_T& get();
|
CONFIG_T const& get();
|
||||||
|
|
||||||
|
class WriteGuard {
|
||||||
|
public:
|
||||||
|
WriteGuard();
|
||||||
|
CONFIG_T& getConfig();
|
||||||
|
~WriteGuard();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_lock<std::mutex> _lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
WriteGuard getWriteGuard();
|
||||||
|
|
||||||
INVERTER_CONFIG_T* getFreeInverterSlot();
|
INVERTER_CONFIG_T* getFreeInverterSlot();
|
||||||
INVERTER_CONFIG_T* getInverterConfig(const uint64_t serial);
|
INVERTER_CONFIG_T* getInverterConfig(const uint64_t serial);
|
||||||
void deleteInverterById(const uint8_t id);
|
void deleteInverterById(const uint8_t id);
|
||||||
|
|
||||||
static void serializeHttpRequestConfig(HttpRequestConfig const& source, JsonObject& target);
|
private:
|
||||||
static void serializePowerMeterMqttConfig(PowerMeterMqttConfig const& source, JsonObject& target);
|
void loop();
|
||||||
static void serializePowerMeterSerialSdmConfig(PowerMeterSerialSdmConfig const& source, JsonObject& target);
|
|
||||||
static void serializePowerMeterHttpJsonConfig(PowerMeterHttpJsonConfig const& source, JsonObject& target);
|
|
||||||
static void serializePowerMeterHttpSmlConfig(PowerMeterHttpSmlConfig const& source, JsonObject& target);
|
|
||||||
static void serializeBatteryConfig(BatteryConfig const& source, JsonObject& target);
|
|
||||||
|
|
||||||
static void deserializeHttpRequestConfig(JsonObject const& source, HttpRequestConfig& target);
|
Task _loopTask;
|
||||||
static void deserializePowerMeterMqttConfig(JsonObject const& source, PowerMeterMqttConfig& target);
|
|
||||||
static void deserializePowerMeterSerialSdmConfig(JsonObject const& source, PowerMeterSerialSdmConfig& target);
|
|
||||||
static void deserializePowerMeterHttpJsonConfig(JsonObject const& source, PowerMeterHttpJsonConfig& target);
|
|
||||||
static void deserializePowerMeterHttpSmlConfig(JsonObject const& source, PowerMeterHttpSmlConfig& target);
|
|
||||||
static void deserializeBatteryConfig(JsonObject const& source, BatteryConfig& target);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern ConfigurationClass Configuration;
|
extern ConfigurationClass Configuration;
|
||||||
|
|||||||
@ -40,7 +40,7 @@ public:
|
|||||||
void setContrast(const uint8_t contrast);
|
void setContrast(const uint8_t contrast);
|
||||||
void setStatus(const bool turnOn);
|
void setStatus(const bool turnOn);
|
||||||
void setOrientation(const uint8_t rotation = DISPLAY_ROTATION);
|
void setOrientation(const uint8_t rotation = DISPLAY_ROTATION);
|
||||||
void setLanguage(const uint8_t language);
|
void setLocale(const String& locale);
|
||||||
void setDiagramMode(DiagramMode_t mode);
|
void setDiagramMode(DiagramMode_t mode);
|
||||||
void setStartupDisplay();
|
void setStartupDisplay();
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ private:
|
|||||||
|
|
||||||
DisplayType_t _display_type = DisplayType_t::None;
|
DisplayType_t _display_type = DisplayType_t::None;
|
||||||
DiagramMode_t _diagram_mode = DiagramMode_t::Off;
|
DiagramMode_t _diagram_mode = DiagramMode_t::Off;
|
||||||
uint8_t _display_language = DISPLAY_LANGUAGE;
|
String _display_language = DISPLAY_LOCALE;
|
||||||
uint8_t _mExtra;
|
uint8_t _mExtra;
|
||||||
const uint16_t _period = 1000;
|
const uint16_t _period = 1000;
|
||||||
const uint16_t _interval = 60000; // interval at which to power save (milliseconds)
|
const uint16_t _interval = 60000; // interval at which to power save (milliseconds)
|
||||||
@ -73,6 +73,15 @@ private:
|
|||||||
char _fmtText[32];
|
char _fmtText[32];
|
||||||
bool _isLarge = false;
|
bool _isLarge = false;
|
||||||
uint8_t _lineOffsets[5];
|
uint8_t _lineOffsets[5];
|
||||||
|
|
||||||
|
String _i18n_offline;
|
||||||
|
String _i18n_yield_today_kwh;
|
||||||
|
String _i18n_yield_today_wh;
|
||||||
|
String _i18n_date_format;
|
||||||
|
String _i18n_current_power_kw;
|
||||||
|
String _i18n_current_power_w;
|
||||||
|
String _i18n_yield_total_mwh;
|
||||||
|
String _i18n_yield_total_kwh;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern DisplayGraphicClass Display;
|
extern DisplayGraphicClass Display;
|
||||||
|
|||||||
@ -1,90 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Configuration.h"
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
#include <utility>
|
|
||||||
#include <string>
|
|
||||||
#include <HTTPClient.h>
|
|
||||||
#include <WiFiClient.h>
|
|
||||||
|
|
||||||
class HttpGetterClient : public HTTPClient {
|
|
||||||
public:
|
|
||||||
void restartTCP() {
|
|
||||||
// keeps the NetworkClient, and closes the TCP connections (as we
|
|
||||||
// effectively do not support keep-alive with HTTP 1.0).
|
|
||||||
HTTPClient::disconnect(true);
|
|
||||||
HTTPClient::connect();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using up_http_client_t = std::unique_ptr<HttpGetterClient>;
|
|
||||||
using sp_wifi_client_t = std::shared_ptr<WiFiClient>;
|
|
||||||
|
|
||||||
class HttpRequestResult {
|
|
||||||
public:
|
|
||||||
HttpRequestResult(bool success,
|
|
||||||
up_http_client_t upHttpClient = nullptr,
|
|
||||||
sp_wifi_client_t spWiFiClient = nullptr)
|
|
||||||
: _success(success)
|
|
||||||
, _upHttpClient(std::move(upHttpClient))
|
|
||||||
, _spWiFiClient(std::move(spWiFiClient)) { }
|
|
||||||
|
|
||||||
~HttpRequestResult() {
|
|
||||||
// the wifi client *must* die *after* the http client, as the http
|
|
||||||
// client uses the wifi client in its destructor.
|
|
||||||
if (_upHttpClient) { _upHttpClient->end(); }
|
|
||||||
_upHttpClient = nullptr;
|
|
||||||
_spWiFiClient = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpRequestResult(HttpRequestResult const&) = delete;
|
|
||||||
HttpRequestResult(HttpRequestResult&&) = delete;
|
|
||||||
HttpRequestResult& operator=(HttpRequestResult const&) = delete;
|
|
||||||
HttpRequestResult& operator=(HttpRequestResult&&) = delete;
|
|
||||||
|
|
||||||
operator bool() const { return _success; }
|
|
||||||
|
|
||||||
Stream* getStream() {
|
|
||||||
if(!_upHttpClient) { return nullptr; }
|
|
||||||
return _upHttpClient->getStreamPtr();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool _success;
|
|
||||||
up_http_client_t _upHttpClient;
|
|
||||||
sp_wifi_client_t _spWiFiClient;
|
|
||||||
};
|
|
||||||
|
|
||||||
class HttpGetter {
|
|
||||||
public:
|
|
||||||
explicit HttpGetter(HttpRequestConfig const& cfg)
|
|
||||||
: _config(cfg) { }
|
|
||||||
|
|
||||||
bool init();
|
|
||||||
void addHeader(char const* key, char const* value);
|
|
||||||
HttpRequestResult performGetRequest();
|
|
||||||
|
|
||||||
char const* getErrorText() const { return _errBuffer; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::pair<bool, String> getAuthDigest();
|
|
||||||
HttpRequestConfig const& _config;
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void logError(char const* format, Args... args);
|
|
||||||
char _errBuffer[256];
|
|
||||||
|
|
||||||
bool _useHttps;
|
|
||||||
String _host;
|
|
||||||
String _uri;
|
|
||||||
uint16_t _port;
|
|
||||||
|
|
||||||
String _wwwAuthenticate = "";
|
|
||||||
unsigned _nonceCounter = 0;
|
|
||||||
|
|
||||||
sp_wifi_client_t _spWiFiClient; // reused for multiple HTTP requests
|
|
||||||
|
|
||||||
std::vector<std::pair<std::string, std::string>> _additionalHeaders;
|
|
||||||
};
|
|
||||||
@ -1,158 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include "SPI.h"
|
|
||||||
#include <mcp_can.h>
|
|
||||||
#include <mutex>
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
|
|
||||||
#ifndef HUAWEI_PIN_MISO
|
|
||||||
#define HUAWEI_PIN_MISO 12
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef HUAWEI_PIN_MOSI
|
|
||||||
#define HUAWEI_PIN_MOSI 13
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef HUAWEI_PIN_SCLK
|
|
||||||
#define HUAWEI_PIN_SCLK 26
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef HUAWEI_PIN_IRQ
|
|
||||||
#define HUAWEI_PIN_IRQ 25
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef HUAWEI_PIN_CS
|
|
||||||
#define HUAWEI_PIN_CS 15
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef HUAWEI_PIN_POWER
|
|
||||||
#define HUAWEI_PIN_POWER 33
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define HUAWEI_MINIMAL_OFFLINE_VOLTAGE 48
|
|
||||||
#define HUAWEI_MINIMAL_ONLINE_VOLTAGE 42
|
|
||||||
|
|
||||||
#define MAX_CURRENT_MULTIPLIER 20
|
|
||||||
|
|
||||||
// Index values for rec_values array
|
|
||||||
#define HUAWEI_INPUT_POWER_IDX 0
|
|
||||||
#define HUAWEI_INPUT_FREQ_IDX 1
|
|
||||||
#define HUAWEI_INPUT_CURRENT_IDX 2
|
|
||||||
#define HUAWEI_OUTPUT_POWER_IDX 3
|
|
||||||
#define HUAWEI_EFFICIENCY_IDX 4
|
|
||||||
#define HUAWEI_OUTPUT_VOLTAGE_IDX 5
|
|
||||||
#define HUAWEI_OUTPUT_CURRENT_MAX_IDX 6
|
|
||||||
#define HUAWEI_INPUT_VOLTAGE_IDX 7
|
|
||||||
#define HUAWEI_OUTPUT_TEMPERATURE_IDX 8
|
|
||||||
#define HUAWEI_INPUT_TEMPERATURE_IDX 9
|
|
||||||
#define HUAWEI_OUTPUT_CURRENT_IDX 10
|
|
||||||
#define HUAWEI_OUTPUT_CURRENT1_IDX 11
|
|
||||||
|
|
||||||
// Defines and index values for tx_values array
|
|
||||||
#define HUAWEI_OFFLINE_VOLTAGE 0x01
|
|
||||||
#define HUAWEI_ONLINE_VOLTAGE 0x00
|
|
||||||
#define HUAWEI_OFFLINE_CURRENT 0x04
|
|
||||||
#define HUAWEI_ONLINE_CURRENT 0x03
|
|
||||||
|
|
||||||
// Modes of operation
|
|
||||||
#define HUAWEI_MODE_OFF 0
|
|
||||||
#define HUAWEI_MODE_ON 1
|
|
||||||
#define HUAWEI_MODE_AUTO_EXT 2
|
|
||||||
#define HUAWEI_MODE_AUTO_INT 3
|
|
||||||
|
|
||||||
// Error codes
|
|
||||||
#define HUAWEI_ERROR_CODE_RX 0x01
|
|
||||||
#define HUAWEI_ERROR_CODE_TX 0x02
|
|
||||||
|
|
||||||
// Wait time/current before shuting down the PSU / charger
|
|
||||||
// This is set to allow the fan to run for some time
|
|
||||||
#define HUAWEI_AUTO_MODE_SHUTDOWN_DELAY 60000
|
|
||||||
#define HUAWEI_AUTO_MODE_SHUTDOWN_CURRENT 0.75
|
|
||||||
|
|
||||||
// Updateinterval used to request new values from the PSU
|
|
||||||
#define HUAWEI_DATA_REQUEST_INTERVAL_MS 2500
|
|
||||||
|
|
||||||
typedef struct RectifierParameters {
|
|
||||||
float input_voltage;
|
|
||||||
float input_frequency;
|
|
||||||
float input_current;
|
|
||||||
float input_power;
|
|
||||||
float input_temp;
|
|
||||||
float efficiency;
|
|
||||||
float output_voltage;
|
|
||||||
float output_current;
|
|
||||||
float max_output_current;
|
|
||||||
float output_power;
|
|
||||||
float output_temp;
|
|
||||||
float amp_hour;
|
|
||||||
} RectifierParameters_t;
|
|
||||||
|
|
||||||
class HuaweiCanCommClass {
|
|
||||||
public:
|
|
||||||
bool init(uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huawei_clk,
|
|
||||||
uint8_t huawei_irq, uint8_t huawei_cs, uint32_t frequency);
|
|
||||||
void loop();
|
|
||||||
bool gotNewRxDataFrame(bool clear);
|
|
||||||
uint8_t getErrorCode(bool clear);
|
|
||||||
uint32_t getParameterValue(uint8_t parameter);
|
|
||||||
void setParameterValue(uint16_t in, uint8_t parameterType);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void sendRequest();
|
|
||||||
|
|
||||||
SPIClass *SPI;
|
|
||||||
MCP_CAN *_CAN;
|
|
||||||
uint8_t _huaweiIrq; // IRQ pin
|
|
||||||
uint32_t _nextRequestMillis = 0; // When to send next data request to PSU
|
|
||||||
|
|
||||||
std::mutex _mutex;
|
|
||||||
|
|
||||||
uint32_t _recValues[12];
|
|
||||||
uint16_t _txValues[5];
|
|
||||||
bool _hasNewTxValue[5];
|
|
||||||
|
|
||||||
uint8_t _errorCode;
|
|
||||||
bool _completeUpdateReceived;
|
|
||||||
};
|
|
||||||
|
|
||||||
class HuaweiCanClass {
|
|
||||||
public:
|
|
||||||
void init(Scheduler& scheduler, uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huawei_clk, uint8_t huawei_irq, uint8_t huawei_cs, uint8_t huawei_power);
|
|
||||||
void updateSettings(uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huawei_clk, uint8_t huawei_irq, uint8_t huawei_cs, uint8_t huawei_power);
|
|
||||||
void setValue(float in, uint8_t parameterType);
|
|
||||||
void setMode(uint8_t mode);
|
|
||||||
|
|
||||||
RectifierParameters_t * get();
|
|
||||||
uint32_t getLastUpdate() const { return _lastUpdateReceivedMillis; };
|
|
||||||
bool getAutoPowerStatus() const { return _autoPowerEnabled; };
|
|
||||||
uint8_t getMode() const { return _mode; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
void loop();
|
|
||||||
void processReceivedParameters();
|
|
||||||
void _setValue(float in, uint8_t parameterType);
|
|
||||||
|
|
||||||
Task _loopTask;
|
|
||||||
|
|
||||||
TaskHandle_t _HuaweiCanCommunicationTaskHdl = NULL;
|
|
||||||
bool _initialized = false;
|
|
||||||
uint8_t _huaweiPower; // Power pin
|
|
||||||
uint8_t _mode = HUAWEI_MODE_AUTO_EXT;
|
|
||||||
|
|
||||||
RectifierParameters_t _rp;
|
|
||||||
|
|
||||||
uint32_t _lastUpdateReceivedMillis; // Timestamp for last data seen from the PSU
|
|
||||||
uint32_t _outputCurrentOnSinceMillis; // Timestamp since when the PSU was idle at zero amps
|
|
||||||
uint32_t _nextAutoModePeriodicIntMillis; // When to set the next output voltage in automatic mode
|
|
||||||
uint32_t _lastPowerMeterUpdateReceivedMillis; // Timestamp of last seen power meter value
|
|
||||||
uint32_t _autoModeBlockedTillMillis = 0; // Timestamp to block running auto mode for some time
|
|
||||||
|
|
||||||
uint8_t _autoPowerEnabledCounter = 0;
|
|
||||||
bool _autoPowerEnabled = false;
|
|
||||||
bool _batteryEmergencyCharging = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern HuaweiCanClass HuaweiCan;
|
|
||||||
extern HuaweiCanCommClass HuaweiCanComm;
|
|
||||||
35
include/I18n.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
#include <WString.h>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
struct LanguageInfo_t {
|
||||||
|
String code;
|
||||||
|
String name;
|
||||||
|
String filename;
|
||||||
|
};
|
||||||
|
|
||||||
|
class I18nClass {
|
||||||
|
public:
|
||||||
|
I18nClass();
|
||||||
|
void init(Scheduler& scheduler);
|
||||||
|
std::list<LanguageInfo_t> getAvailableLanguages();
|
||||||
|
String getFilenameByLocale(const String& locale) const;
|
||||||
|
void readDisplayStrings(
|
||||||
|
const String& locale,
|
||||||
|
String& date_format,
|
||||||
|
String& offline,
|
||||||
|
String& power_w, String& power_kw,
|
||||||
|
String& yield_today_wh, String& yield_today_kwh,
|
||||||
|
String& yield_total_kwh, String& yield_total_mwh);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void readLangPacks();
|
||||||
|
void readConfig(String file);
|
||||||
|
|
||||||
|
std::list<LanguageInfo_t> _availLanguages;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern I18nClass I18n;
|
||||||
@ -1,87 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
#include <frozen/string.h>
|
|
||||||
|
|
||||||
#include "Battery.h"
|
|
||||||
#include "JkBmsSerialMessage.h"
|
|
||||||
#include "JkBmsDummy.h"
|
|
||||||
|
|
||||||
//#define JKBMS_DUMMY_SERIAL
|
|
||||||
|
|
||||||
class DataPointContainer;
|
|
||||||
|
|
||||||
namespace JkBms {
|
|
||||||
|
|
||||||
class Controller : public BatteryProvider {
|
|
||||||
public:
|
|
||||||
Controller() = default;
|
|
||||||
|
|
||||||
bool init(bool verboseLogging) final;
|
|
||||||
void deinit() final;
|
|
||||||
void loop() final;
|
|
||||||
std::shared_ptr<BatteryStats> getStats() const final { return _stats; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
static char constexpr _serialPortOwner[] = "JK BMS";
|
|
||||||
|
|
||||||
#ifdef JKBMS_DUMMY_SERIAL
|
|
||||||
std::unique_ptr<DummySerial> _upSerial;
|
|
||||||
#else
|
|
||||||
std::unique_ptr<HardwareSerial> _upSerial;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum class Status : unsigned {
|
|
||||||
Initializing,
|
|
||||||
Timeout,
|
|
||||||
WaitingForPollInterval,
|
|
||||||
HwSerialNotAvailableForWrite,
|
|
||||||
BusyReading,
|
|
||||||
RequestSent,
|
|
||||||
FrameCompleted
|
|
||||||
};
|
|
||||||
|
|
||||||
frozen::string const& getStatusText(Status status);
|
|
||||||
void announceStatus(Status status);
|
|
||||||
void sendRequest(uint8_t pollInterval);
|
|
||||||
void rxData(uint8_t inbyte);
|
|
||||||
void reset();
|
|
||||||
void frameComplete();
|
|
||||||
void processDataPoints(DataPointContainer const& dataPoints);
|
|
||||||
|
|
||||||
enum class Interface : unsigned {
|
|
||||||
Invalid,
|
|
||||||
Uart,
|
|
||||||
Transceiver
|
|
||||||
};
|
|
||||||
|
|
||||||
Interface getInterface() const;
|
|
||||||
|
|
||||||
enum class ReadState : unsigned {
|
|
||||||
Idle,
|
|
||||||
WaitingForFrameStart,
|
|
||||||
FrameStartReceived,
|
|
||||||
StartMarkerReceived,
|
|
||||||
FrameLengthMsbReceived,
|
|
||||||
ReadingFrame
|
|
||||||
};
|
|
||||||
ReadState _readState;
|
|
||||||
void setReadState(ReadState state) {
|
|
||||||
_readState = state;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _verboseLogging = true;
|
|
||||||
int8_t _rxEnablePin = -1;
|
|
||||||
int8_t _txEnablePin = -1;
|
|
||||||
Status _lastStatus = Status::Initializing;
|
|
||||||
uint32_t _lastStatusPrinted = 0;
|
|
||||||
uint32_t _lastRequest = 0;
|
|
||||||
uint16_t _frameLength = 0;
|
|
||||||
uint8_t _protocolVersion = -1;
|
|
||||||
SerialResponse::tData _buffer = {};
|
|
||||||
std::shared_ptr<JkBmsBatteryStats> _stats =
|
|
||||||
std::make_shared<JkBmsBatteryStats>();
|
|
||||||
};
|
|
||||||
|
|
||||||
} /* namespace JkBms */
|
|
||||||
@ -1,304 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <map>
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <variant>
|
|
||||||
#include <frozen/map.h>
|
|
||||||
#include <frozen/string.h>
|
|
||||||
|
|
||||||
namespace JkBms {
|
|
||||||
|
|
||||||
#define ALARM_BITS(fnc) \
|
|
||||||
fnc(LowCapacity, (1<<0)) \
|
|
||||||
fnc(BmsOvertemperature, (1<<1)) \
|
|
||||||
fnc(ChargingOvervoltage, (1<<2)) \
|
|
||||||
fnc(DischargeUndervoltage, (1<<3)) \
|
|
||||||
fnc(BatteryOvertemperature, (1<<4)) \
|
|
||||||
fnc(ChargingOvercurrent, (1<<5)) \
|
|
||||||
fnc(DischargeOvercurrent, (1<<6)) \
|
|
||||||
fnc(CellVoltageDifference, (1<<7)) \
|
|
||||||
fnc(BatteryBoxOvertemperature, (1<<8)) \
|
|
||||||
fnc(BatteryUndertemperature, (1<<9)) \
|
|
||||||
fnc(CellOvervoltage, (1<<10)) \
|
|
||||||
fnc(CellUndervoltage, (1<<11)) \
|
|
||||||
fnc(AProtect, (1<<12)) \
|
|
||||||
fnc(BProtect, (1<<13)) \
|
|
||||||
fnc(Reserved1, (1<<14)) \
|
|
||||||
fnc(Reserved2, (1<<15))
|
|
||||||
|
|
||||||
enum class AlarmBits : uint16_t {
|
|
||||||
#define ALARM_ENUM(name, value) name = value,
|
|
||||||
ALARM_BITS(ALARM_ENUM)
|
|
||||||
#undef ALARM_ENUM
|
|
||||||
};
|
|
||||||
|
|
||||||
static const frozen::map<AlarmBits, frozen::string, 16> AlarmBitTexts = {
|
|
||||||
#define ALARM_TEXT(name, value) { AlarmBits::name, #name },
|
|
||||||
ALARM_BITS(ALARM_TEXT)
|
|
||||||
#undef ALARM_TEXT
|
|
||||||
};
|
|
||||||
|
|
||||||
#define STATUS_BITS(fnc) \
|
|
||||||
fnc(ChargingActive, (1<<0)) \
|
|
||||||
fnc(DischargingActive, (1<<1)) \
|
|
||||||
fnc(BalancingActive, (1<<2)) \
|
|
||||||
fnc(BatteryOnline, (1<<3))
|
|
||||||
|
|
||||||
enum class StatusBits : uint16_t {
|
|
||||||
#define STATUS_ENUM(name, value) name = value,
|
|
||||||
STATUS_BITS(STATUS_ENUM)
|
|
||||||
#undef STATUS_ENUM
|
|
||||||
};
|
|
||||||
|
|
||||||
static const frozen::map<StatusBits, frozen::string, 4> StatusBitTexts = {
|
|
||||||
#define STATUS_TEXT(name, value) { StatusBits::name, #name },
|
|
||||||
STATUS_BITS(STATUS_TEXT)
|
|
||||||
#undef STATUS_TEXT
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class DataPointLabel : uint8_t {
|
|
||||||
CellsMilliVolt = 0x79,
|
|
||||||
BmsTempCelsius = 0x80,
|
|
||||||
BatteryTempOneCelsius = 0x81,
|
|
||||||
BatteryTempTwoCelsius = 0x82,
|
|
||||||
BatteryVoltageMilliVolt = 0x83,
|
|
||||||
BatteryCurrentMilliAmps = 0x84,
|
|
||||||
BatterySoCPercent = 0x85,
|
|
||||||
BatteryTemperatureSensorAmount = 0x86,
|
|
||||||
BatteryCycles = 0x87,
|
|
||||||
BatteryCycleCapacity = 0x89,
|
|
||||||
BatteryCellAmount = 0x8a,
|
|
||||||
AlarmsBitmask = 0x8b,
|
|
||||||
StatusBitmask = 0x8c,
|
|
||||||
TotalOvervoltageThresholdMilliVolt = 0x8e,
|
|
||||||
TotalUndervoltageThresholdMilliVolt = 0x8f,
|
|
||||||
CellOvervoltageThresholdMilliVolt = 0x90,
|
|
||||||
CellOvervoltageRecoveryMilliVolt = 0x91,
|
|
||||||
CellOvervoltageProtectionDelaySeconds = 0x92,
|
|
||||||
CellUndervoltageThresholdMilliVolt = 0x93,
|
|
||||||
CellUndervoltageRecoveryMilliVolt = 0x94,
|
|
||||||
CellUndervoltageProtectionDelaySeconds = 0x95,
|
|
||||||
CellVoltageDiffThresholdMilliVolt = 0x96,
|
|
||||||
DischargeOvercurrentThresholdAmperes = 0x97,
|
|
||||||
DischargeOvercurrentDelaySeconds = 0x98,
|
|
||||||
ChargeOvercurrentThresholdAmps = 0x99,
|
|
||||||
ChargeOvercurrentDelaySeconds = 0x9a,
|
|
||||||
BalanceCellVoltageThresholdMilliVolt = 0x9b,
|
|
||||||
BalanceVoltageDiffThresholdMilliVolt = 0x9c,
|
|
||||||
BalancingEnabled = 0x9d,
|
|
||||||
BmsTempProtectionThresholdCelsius = 0x9e,
|
|
||||||
BmsTempRecoveryThresholdCelsius = 0x9f,
|
|
||||||
BatteryTempProtectionThresholdCelsius = 0xa0,
|
|
||||||
BatteryTempRecoveryThresholdCelsius = 0xa1,
|
|
||||||
BatteryTempDiffThresholdCelsius = 0xa2,
|
|
||||||
ChargeHighTempThresholdCelsius = 0xa3,
|
|
||||||
DischargeHighTempThresholdCelsius = 0xa4,
|
|
||||||
ChargeLowTempThresholdCelsius = 0xa5,
|
|
||||||
ChargeLowTempRecoveryCelsius = 0xa6,
|
|
||||||
DischargeLowTempThresholdCelsius = 0xa7,
|
|
||||||
DischargeLowTempRecoveryCelsius = 0xa8,
|
|
||||||
CellAmountSetting = 0xa9,
|
|
||||||
BatteryCapacitySettingAmpHours = 0xaa,
|
|
||||||
BatteryChargeEnabled = 0xab,
|
|
||||||
BatteryDischargeEnabled = 0xac,
|
|
||||||
CurrentCalibrationMilliAmps = 0xad,
|
|
||||||
BmsAddress = 0xae,
|
|
||||||
BatteryType = 0xaf,
|
|
||||||
SleepWaitTime = 0xb0, // what's this?
|
|
||||||
LowCapacityAlarmThresholdPercent = 0xb1,
|
|
||||||
ModificationPassword = 0xb2,
|
|
||||||
DedicatedChargerSwitch = 0xb3, // what's this?
|
|
||||||
EquipmentId = 0xb4,
|
|
||||||
DateOfManufacturing = 0xb5,
|
|
||||||
BmsHourMeterMinutes = 0xb6,
|
|
||||||
BmsSoftwareVersion = 0xb7,
|
|
||||||
CurrentCalibration = 0xb8,
|
|
||||||
ActualBatteryCapacityAmpHours = 0xb9,
|
|
||||||
ProductId = 0xba,
|
|
||||||
ProtocolVersion = 0xc0
|
|
||||||
};
|
|
||||||
|
|
||||||
using tCells = std::map<uint8_t, uint16_t>;
|
|
||||||
|
|
||||||
template<DataPointLabel> struct DataPointLabelTraits;
|
|
||||||
|
|
||||||
#define LABEL_TRAIT(n, t, u) template<> struct DataPointLabelTraits<DataPointLabel::n> { \
|
|
||||||
using type = t; \
|
|
||||||
static constexpr char const name[] = #n; \
|
|
||||||
static constexpr char const unit[] = u; \
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the types associated with the labels are the types for the respective data
|
|
||||||
* points in the JkBms::DataPoint class. they are *not* always equal to the
|
|
||||||
* type used in the serial message.
|
|
||||||
*
|
|
||||||
* it is unfortunate that we have to repeat all enum values here to define the
|
|
||||||
* traits. code generation could help here (labels are defined in a single
|
|
||||||
* source of truth and this code is generated -- no typing errors, etc.).
|
|
||||||
* however, the compiler will complain if an enum is misspelled or traits are
|
|
||||||
* defined for a removed enum, so we will notice. it will also complain when a
|
|
||||||
* trait is missing and if a data point for a label without traits is added to
|
|
||||||
* the DataPointContainer class, because the traits must be available then.
|
|
||||||
* even though this is tedious to maintain, human errors will be caught.
|
|
||||||
*/
|
|
||||||
LABEL_TRAIT(CellsMilliVolt, tCells, "mV");
|
|
||||||
LABEL_TRAIT(BmsTempCelsius, int16_t, "°C");
|
|
||||||
LABEL_TRAIT(BatteryTempOneCelsius, int16_t, "°C");
|
|
||||||
LABEL_TRAIT(BatteryTempTwoCelsius, int16_t, "°C");
|
|
||||||
LABEL_TRAIT(BatteryVoltageMilliVolt, uint32_t, "mV");
|
|
||||||
LABEL_TRAIT(BatteryCurrentMilliAmps, int32_t, "mA");
|
|
||||||
LABEL_TRAIT(BatterySoCPercent, uint8_t, "%");
|
|
||||||
LABEL_TRAIT(BatteryTemperatureSensorAmount, uint8_t, "");
|
|
||||||
LABEL_TRAIT(BatteryCycles, uint16_t, "");
|
|
||||||
LABEL_TRAIT(BatteryCycleCapacity, uint32_t, "Ah");
|
|
||||||
LABEL_TRAIT(BatteryCellAmount, uint16_t, "");
|
|
||||||
LABEL_TRAIT(AlarmsBitmask, uint16_t, "");
|
|
||||||
LABEL_TRAIT(StatusBitmask, uint16_t, "");
|
|
||||||
LABEL_TRAIT(TotalOvervoltageThresholdMilliVolt, uint32_t, "mV");
|
|
||||||
LABEL_TRAIT(TotalUndervoltageThresholdMilliVolt, uint32_t, "mV");
|
|
||||||
LABEL_TRAIT(CellOvervoltageThresholdMilliVolt, uint16_t, "mV");
|
|
||||||
LABEL_TRAIT(CellOvervoltageRecoveryMilliVolt, uint16_t, "mV");
|
|
||||||
LABEL_TRAIT(CellOvervoltageProtectionDelaySeconds, uint16_t, "s");
|
|
||||||
LABEL_TRAIT(CellUndervoltageThresholdMilliVolt, uint16_t, "mV");
|
|
||||||
LABEL_TRAIT(CellUndervoltageRecoveryMilliVolt, uint16_t, "mV");
|
|
||||||
LABEL_TRAIT(CellUndervoltageProtectionDelaySeconds, uint16_t, "s");
|
|
||||||
LABEL_TRAIT(CellVoltageDiffThresholdMilliVolt, uint16_t, "mV");
|
|
||||||
LABEL_TRAIT(DischargeOvercurrentThresholdAmperes, uint16_t, "A");
|
|
||||||
LABEL_TRAIT(DischargeOvercurrentDelaySeconds, uint16_t, "s");
|
|
||||||
LABEL_TRAIT(ChargeOvercurrentThresholdAmps, uint16_t, "A");
|
|
||||||
LABEL_TRAIT(ChargeOvercurrentDelaySeconds, uint16_t, "s");
|
|
||||||
LABEL_TRAIT(BalanceCellVoltageThresholdMilliVolt, uint16_t, "mV");
|
|
||||||
LABEL_TRAIT(BalanceVoltageDiffThresholdMilliVolt, uint16_t, "mV");
|
|
||||||
LABEL_TRAIT(BalancingEnabled, bool, "");
|
|
||||||
LABEL_TRAIT(BmsTempProtectionThresholdCelsius, uint16_t, "°C");
|
|
||||||
LABEL_TRAIT(BmsTempRecoveryThresholdCelsius, uint16_t, "°C");
|
|
||||||
LABEL_TRAIT(BatteryTempProtectionThresholdCelsius, uint16_t, "°C");
|
|
||||||
LABEL_TRAIT(BatteryTempRecoveryThresholdCelsius, uint16_t, "°C");
|
|
||||||
LABEL_TRAIT(BatteryTempDiffThresholdCelsius, uint16_t, "°C");
|
|
||||||
LABEL_TRAIT(ChargeHighTempThresholdCelsius, uint16_t, "°C");
|
|
||||||
LABEL_TRAIT(DischargeHighTempThresholdCelsius, uint16_t, "°C");
|
|
||||||
LABEL_TRAIT(ChargeLowTempThresholdCelsius, int16_t, "°C");
|
|
||||||
LABEL_TRAIT(ChargeLowTempRecoveryCelsius, int16_t, "°C");
|
|
||||||
LABEL_TRAIT(DischargeLowTempThresholdCelsius, int16_t, "°C");
|
|
||||||
LABEL_TRAIT(DischargeLowTempRecoveryCelsius, int16_t, "°C");
|
|
||||||
LABEL_TRAIT(CellAmountSetting, uint8_t, "");
|
|
||||||
LABEL_TRAIT(BatteryCapacitySettingAmpHours, uint32_t, "Ah");
|
|
||||||
LABEL_TRAIT(BatteryChargeEnabled, bool, "");
|
|
||||||
LABEL_TRAIT(BatteryDischargeEnabled, bool, "");
|
|
||||||
LABEL_TRAIT(CurrentCalibrationMilliAmps, uint16_t, "mA");
|
|
||||||
LABEL_TRAIT(BmsAddress, uint8_t, "");
|
|
||||||
LABEL_TRAIT(BatteryType, uint8_t, "");
|
|
||||||
LABEL_TRAIT(SleepWaitTime, uint16_t, "s");
|
|
||||||
LABEL_TRAIT(LowCapacityAlarmThresholdPercent, uint8_t, "%");
|
|
||||||
LABEL_TRAIT(ModificationPassword, std::string, "");
|
|
||||||
LABEL_TRAIT(DedicatedChargerSwitch, bool, "");
|
|
||||||
LABEL_TRAIT(EquipmentId, std::string, "");
|
|
||||||
LABEL_TRAIT(DateOfManufacturing, std::string, "");
|
|
||||||
LABEL_TRAIT(BmsHourMeterMinutes, uint32_t, "min");
|
|
||||||
LABEL_TRAIT(BmsSoftwareVersion, std::string, "");
|
|
||||||
LABEL_TRAIT(CurrentCalibration, bool, "");
|
|
||||||
LABEL_TRAIT(ActualBatteryCapacityAmpHours, uint32_t, "Ah");
|
|
||||||
LABEL_TRAIT(ProductId, std::string, "");
|
|
||||||
LABEL_TRAIT(ProtocolVersion, uint8_t, "");
|
|
||||||
#undef LABEL_TRAIT
|
|
||||||
|
|
||||||
class DataPoint {
|
|
||||||
friend class DataPointContainer;
|
|
||||||
|
|
||||||
public:
|
|
||||||
using tValue = std::variant<bool, uint8_t, uint16_t, uint32_t,
|
|
||||||
int16_t, int32_t, std::string, tCells>;
|
|
||||||
|
|
||||||
DataPoint() = delete;
|
|
||||||
|
|
||||||
DataPoint(DataPoint const& other)
|
|
||||||
: _strLabel(other._strLabel)
|
|
||||||
, _strValue(other._strValue)
|
|
||||||
, _strUnit(other._strUnit)
|
|
||||||
, _value(other._value)
|
|
||||||
, _timestamp(other._timestamp) { }
|
|
||||||
|
|
||||||
DataPoint(std::string const& strLabel, std::string const& strValue,
|
|
||||||
std::string const& strUnit, tValue value, uint32_t timestamp)
|
|
||||||
: _strLabel(strLabel)
|
|
||||||
, _strValue(strValue)
|
|
||||||
, _strUnit(strUnit)
|
|
||||||
, _value(std::move(value))
|
|
||||||
, _timestamp(timestamp) { }
|
|
||||||
|
|
||||||
std::string const& getLabelText() const { return _strLabel; }
|
|
||||||
std::string const& getValueText() const { return _strValue; }
|
|
||||||
std::string const& getUnitText() const { return _strUnit; }
|
|
||||||
uint32_t getTimestamp() const { return _timestamp; }
|
|
||||||
|
|
||||||
bool operator==(DataPoint const& other) const {
|
|
||||||
return _value == other._value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string _strLabel;
|
|
||||||
std::string _strValue;
|
|
||||||
std::string _strUnit;
|
|
||||||
tValue _value;
|
|
||||||
uint32_t _timestamp;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T> std::string dataPointValueToStr(T const& v);
|
|
||||||
|
|
||||||
class DataPointContainer {
|
|
||||||
public:
|
|
||||||
DataPointContainer() = default;
|
|
||||||
|
|
||||||
using Label = DataPointLabel;
|
|
||||||
template<Label L> using Traits = JkBms::DataPointLabelTraits<L>;
|
|
||||||
|
|
||||||
template<Label L>
|
|
||||||
void add(typename Traits<L>::type val) {
|
|
||||||
_dataPoints.emplace(
|
|
||||||
L,
|
|
||||||
DataPoint(
|
|
||||||
Traits<L>::name,
|
|
||||||
dataPointValueToStr(val),
|
|
||||||
Traits<L>::unit,
|
|
||||||
DataPoint::tValue(std::move(val)),
|
|
||||||
millis()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure add() is only called with the type expected for the
|
|
||||||
// respective label, no implicit conversions allowed.
|
|
||||||
template<Label L, typename T>
|
|
||||||
void add(T) = delete;
|
|
||||||
|
|
||||||
template<Label L>
|
|
||||||
std::optional<DataPoint const> getDataPointFor() const {
|
|
||||||
auto it = _dataPoints.find(L);
|
|
||||||
if (it == _dataPoints.end()) { return std::nullopt; }
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<Label L>
|
|
||||||
std::optional<typename Traits<L>::type> get() const {
|
|
||||||
auto optionalDataPoint = getDataPointFor<L>();
|
|
||||||
if (!optionalDataPoint.has_value()) { return std::nullopt; }
|
|
||||||
return std::get<typename Traits<L>::type>(optionalDataPoint->_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
using tMap = std::unordered_map<Label, DataPoint const>;
|
|
||||||
tMap::const_iterator cbegin() const { return _dataPoints.cbegin(); }
|
|
||||||
tMap::const_iterator cend() const { return _dataPoints.cend(); }
|
|
||||||
|
|
||||||
// copy all data points from source into this instance, overwriting
|
|
||||||
// existing data points in this instance.
|
|
||||||
void updateFrom(DataPointContainer const& source);
|
|
||||||
|
|
||||||
private:
|
|
||||||
tMap _dataPoints;
|
|
||||||
};
|
|
||||||
|
|
||||||
} /* namespace JkBms */
|
|
||||||
@ -1,196 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <vector>
|
|
||||||
#include "MessageOutput.h"
|
|
||||||
|
|
||||||
namespace JkBms {
|
|
||||||
|
|
||||||
class DummySerial {
|
|
||||||
public:
|
|
||||||
DummySerial() = default;
|
|
||||||
void begin(uint32_t, uint32_t, int8_t, int8_t) {
|
|
||||||
MessageOutput.println("JK BMS Dummy Serial: begin()");
|
|
||||||
}
|
|
||||||
void end() { MessageOutput.println("JK BMS Dummy Serial: end()"); }
|
|
||||||
void flush() { }
|
|
||||||
bool availableForWrite() const { return true; }
|
|
||||||
size_t write(const uint8_t *buffer, size_t size) {
|
|
||||||
MessageOutput.printf("JK BMS Dummy Serial: write(%d Bytes)\r\n", size);
|
|
||||||
_byte_idx = 0;
|
|
||||||
_msg_idx = (_msg_idx + 1) % _data.size();
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
bool available() const {
|
|
||||||
return _byte_idx < _data[_msg_idx].size();
|
|
||||||
}
|
|
||||||
int read() {
|
|
||||||
if (_byte_idx >= _data[_msg_idx].size()) { return 0; }
|
|
||||||
return _data[_msg_idx][_byte_idx++];
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<std::vector<uint8_t>> const _data =
|
|
||||||
{
|
|
||||||
{
|
|
||||||
0x4e, 0x57, 0x01, 0x21, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x06, 0x00, 0x01, 0x79, 0x30, 0x01, 0x0c, 0xfb,
|
|
||||||
0x02, 0x0c, 0xfb, 0x03, 0x0c, 0xfb, 0x04, 0x0c,
|
|
||||||
0xfb, 0x05, 0x0c, 0xfb, 0x06, 0x0c, 0xfb, 0x07,
|
|
||||||
0x0c, 0xfb, 0x08, 0x0c, 0xf7, 0x09, 0x0d, 0x01,
|
|
||||||
0x0a, 0x0c, 0xf9, 0x0b, 0x0c, 0xfb, 0x0c, 0x0c,
|
|
||||||
0xfb, 0x0d, 0x0c, 0xfb, 0x0e, 0x0c, 0xf8, 0x0f,
|
|
||||||
0x0c, 0xf9, 0x10, 0x0c, 0xfb, 0x80, 0x00, 0x1a,
|
|
||||||
0x81, 0x00, 0x12, 0x82, 0x00, 0x12, 0x83, 0x14,
|
|
||||||
0xc3, 0x84, 0x83, 0xf4, 0x85, 0x2e, 0x86, 0x02,
|
|
||||||
0x87, 0x00, 0x15, 0x89, 0x00, 0x00, 0x13, 0x52,
|
|
||||||
0x8a, 0x00, 0x10, 0x8b, 0x00, 0x00, 0x8c, 0x00,
|
|
||||||
0x03, 0x8e, 0x16, 0x80, 0x8f, 0x12, 0xc0, 0x90,
|
|
||||||
0x0e, 0x10, 0x91, 0x0c, 0xda, 0x92, 0x00, 0x05,
|
|
||||||
0x93, 0x0b, 0xb8, 0x94, 0x0c, 0x80, 0x95, 0x00,
|
|
||||||
0x05, 0x96, 0x01, 0x2c, 0x97, 0x00, 0x28, 0x98,
|
|
||||||
0x01, 0x2c, 0x99, 0x00, 0x28, 0x9a, 0x00, 0x1e,
|
|
||||||
0x9b, 0x0b, 0xb8, 0x9c, 0x00, 0x0a, 0x9d, 0x01,
|
|
||||||
0x9e, 0x00, 0x64, 0x9f, 0x00, 0x50, 0xa0, 0x00,
|
|
||||||
0x64, 0xa1, 0x00, 0x64, 0xa2, 0x00, 0x14, 0xa3,
|
|
||||||
0x00, 0x46, 0xa4, 0x00, 0x46, 0xa5, 0x00, 0x00,
|
|
||||||
0xa6, 0x00, 0x02, 0xa7, 0xff, 0xec, 0xa8, 0xff,
|
|
||||||
0xf6, 0xa9, 0x10, 0xaa, 0x00, 0x00, 0x00, 0xe6,
|
|
||||||
0xab, 0x01, 0xac, 0x01, 0xad, 0x04, 0x4d, 0xae,
|
|
||||||
0x01, 0xaf, 0x00, 0xb0, 0x00, 0x0a, 0xb1, 0x14,
|
|
||||||
0xb2, 0x32, 0x32, 0x31, 0x31, 0x38, 0x37, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0xb3, 0x00, 0xb4, 0x62, 0x65,
|
|
||||||
0x6b, 0x69, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x32,
|
|
||||||
0x33, 0x30, 0x36, 0xb6, 0x00, 0x01, 0x4a, 0xc3,
|
|
||||||
0xb7, 0x31, 0x31, 0x2e, 0x58, 0x57, 0x5f, 0x53,
|
|
||||||
0x31, 0x31, 0x2e, 0x32, 0x36, 0x32, 0x48, 0x5f,
|
|
||||||
0xb8, 0x00, 0xb9, 0x00, 0x00, 0x00, 0xe6, 0xba,
|
|
||||||
0x62, 0x65, 0x6b, 0x69, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x4a, 0x4b, 0x5f, 0x42,
|
|
||||||
0x31, 0x41, 0x32, 0x34, 0x53, 0x31, 0x35, 0x50,
|
|
||||||
0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
|
|
||||||
0x00, 0x53, 0xbb
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0x4e, 0x57, 0x01, 0x21, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x06, 0x00, 0x01, 0x79, 0x30, 0x01, 0x0c, 0xc0,
|
|
||||||
0x02, 0x0c, 0xc1, 0x03, 0x0c, 0xc0, 0x04, 0x0c,
|
|
||||||
0xc4, 0x05, 0x0c, 0xc4, 0x06, 0x0c, 0xc2, 0x07,
|
|
||||||
0x0c, 0xc2, 0x08, 0x0c, 0xc1, 0x09, 0x0c, 0xba,
|
|
||||||
0x0a, 0x0c, 0xc1, 0x0b, 0x0c, 0xc2, 0x0c, 0x0c,
|
|
||||||
0xc2, 0x0d, 0x0c, 0xc2, 0x0e, 0x0c, 0xc4, 0x0f,
|
|
||||||
0x0c, 0xc2, 0x10, 0x0c, 0xc1, 0x80, 0x00, 0x1b,
|
|
||||||
0x81, 0x00, 0x1b, 0x82, 0x00, 0x1a, 0x83, 0x14,
|
|
||||||
0x68, 0x84, 0x03, 0x70, 0x85, 0x3c, 0x86, 0x02,
|
|
||||||
0x87, 0x00, 0x19, 0x89, 0x00, 0x00, 0x16, 0x86,
|
|
||||||
0x8a, 0x00, 0x10, 0x8b, 0x00, 0x00, 0x8c, 0x00,
|
|
||||||
0x07, 0x8e, 0x16, 0x80, 0x8f, 0x12, 0xc0, 0x90,
|
|
||||||
0x0e, 0x10, 0x91, 0x0c, 0xda, 0x92, 0x00, 0x05,
|
|
||||||
0x93, 0x0b, 0xb8, 0x94, 0x0c, 0x80, 0x95, 0x00,
|
|
||||||
0x05, 0x96, 0x01, 0x2c, 0x97, 0x00, 0x28, 0x98,
|
|
||||||
0x01, 0x2c, 0x99, 0x00, 0x28, 0x9a, 0x00, 0x1e,
|
|
||||||
0x9b, 0x0b, 0xb8, 0x9c, 0x00, 0x0a, 0x9d, 0x01,
|
|
||||||
0x9e, 0x00, 0x64, 0x9f, 0x00, 0x50, 0xa0, 0x00,
|
|
||||||
0x64, 0xa1, 0x00, 0x64, 0xa2, 0x00, 0x14, 0xa3,
|
|
||||||
0x00, 0x46, 0xa4, 0x00, 0x46, 0xa5, 0x00, 0x00,
|
|
||||||
0xa6, 0x00, 0x02, 0xa7, 0xff, 0xec, 0xa8, 0xff,
|
|
||||||
0xf6, 0xa9, 0x10, 0xaa, 0x00, 0x00, 0x00, 0xe6,
|
|
||||||
0xab, 0x01, 0xac, 0x01, 0xad, 0x04, 0x4d, 0xae,
|
|
||||||
0x01, 0xaf, 0x00, 0xb0, 0x00, 0x0a, 0xb1, 0x14,
|
|
||||||
0xb2, 0x32, 0x32, 0x31, 0x31, 0x38, 0x37, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0xb3, 0x00, 0xb4, 0x62, 0x65,
|
|
||||||
0x6b, 0x69, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x32,
|
|
||||||
0x33, 0x30, 0x36, 0xb6, 0x00, 0x01, 0x7f, 0x2a,
|
|
||||||
0xb7, 0x31, 0x31, 0x2e, 0x58, 0x57, 0x5f, 0x53,
|
|
||||||
0x31, 0x31, 0x2e, 0x32, 0x36, 0x32, 0x48, 0x5f,
|
|
||||||
0xb8, 0x00, 0xb9, 0x00, 0x00, 0x00, 0xe6, 0xba,
|
|
||||||
0x62, 0x65, 0x6b, 0x69, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x4a, 0x4b, 0x5f, 0x42,
|
|
||||||
0x31, 0x41, 0x32, 0x34, 0x53, 0x31, 0x35, 0x50,
|
|
||||||
0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
|
|
||||||
0x00, 0x4f, 0xc1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0x4e, 0x57, 0x01, 0x21, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x06, 0x00, 0x01, 0x79, 0x30, 0x01, 0x0c, 0x13,
|
|
||||||
0x02, 0x0c, 0x12, 0x03, 0x0c, 0x0f, 0x04, 0x0c,
|
|
||||||
0x15, 0x05, 0x0c, 0x0d, 0x06, 0x0c, 0x13, 0x07,
|
|
||||||
0x0c, 0x16, 0x08, 0x0c, 0x13, 0x09, 0x0b, 0xdb,
|
|
||||||
0x0a, 0x0b, 0xf6, 0x0b, 0x0c, 0x17, 0x0c, 0x0b,
|
|
||||||
0xf5, 0x0d, 0x0c, 0x16, 0x0e, 0x0c, 0x1a, 0x0f,
|
|
||||||
0x0c, 0x1b, 0x10, 0x0c, 0x1c, 0x80, 0x00, 0x18,
|
|
||||||
0x81, 0x00, 0x18, 0x82, 0x00, 0x18, 0x83, 0x13,
|
|
||||||
0x49, 0x84, 0x00, 0x00, 0x85, 0x00, 0x86, 0x02,
|
|
||||||
0x87, 0x00, 0x23, 0x89, 0x00, 0x00, 0x20, 0x14,
|
|
||||||
0x8a, 0x00, 0x10, 0x8b, 0x00, 0x08, 0x8c, 0x00,
|
|
||||||
0x05, 0x8e, 0x16, 0x80, 0x8f, 0x12, 0xc0, 0x90,
|
|
||||||
0x0e, 0x10, 0x91, 0x0c, 0xda, 0x92, 0x00, 0x05,
|
|
||||||
0x93, 0x0b, 0xb8, 0x94, 0x0c, 0x80, 0x95, 0x00,
|
|
||||||
0x05, 0x96, 0x01, 0x2c, 0x97, 0x00, 0x28, 0x98,
|
|
||||||
0x01, 0x2c, 0x99, 0x00, 0x28, 0x9a, 0x00, 0x1e,
|
|
||||||
0x9b, 0x0b, 0xb8, 0x9c, 0x00, 0x0a, 0x9d, 0x01,
|
|
||||||
0x9e, 0x00, 0x64, 0x9f, 0x00, 0x50, 0xa0, 0x00,
|
|
||||||
0x64, 0xa1, 0x00, 0x64, 0xa2, 0x00, 0x14, 0xa3,
|
|
||||||
0x00, 0x46, 0xa4, 0x00, 0x46, 0xa5, 0x00, 0x00,
|
|
||||||
0xa6, 0x00, 0x02, 0xa7, 0xff, 0xec, 0xa8, 0xff,
|
|
||||||
0xf6, 0xa9, 0x10, 0xaa, 0x00, 0x00, 0x00, 0xe6,
|
|
||||||
0xab, 0x01, 0xac, 0x01, 0xad, 0x04, 0x4d, 0xae,
|
|
||||||
0x01, 0xaf, 0x00, 0xb0, 0x00, 0x0a, 0xb1, 0x14,
|
|
||||||
0xb2, 0x32, 0x32, 0x31, 0x31, 0x38, 0x37, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0xb3, 0x00, 0xb4, 0x62, 0x65,
|
|
||||||
0x6b, 0x69, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x32,
|
|
||||||
0x33, 0x30, 0x36, 0xb6, 0x00, 0x02, 0x17, 0x10,
|
|
||||||
0xb7, 0x31, 0x31, 0x2e, 0x58, 0x57, 0x5f, 0x53,
|
|
||||||
0x31, 0x31, 0x2e, 0x32, 0x36, 0x32, 0x48, 0x5f,
|
|
||||||
0xb8, 0x00, 0xb9, 0x00, 0x00, 0x00, 0xe6, 0xba,
|
|
||||||
0x62, 0x65, 0x6b, 0x69, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x4a, 0x4b, 0x5f, 0x42,
|
|
||||||
0x31, 0x41, 0x32, 0x34, 0x53, 0x31, 0x35, 0x50,
|
|
||||||
0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
|
|
||||||
0x00, 0x45, 0xce
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0x4e, 0x57, 0x01, 0x21, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x06, 0x00, 0x01, 0x79, 0x30, 0x01, 0x0c, 0x07,
|
|
||||||
0x02, 0x0c, 0x0a, 0x03, 0x0c, 0x0b, 0x04, 0x0c,
|
|
||||||
0x08, 0x05, 0x0c, 0x05, 0x06, 0x0c, 0x0b, 0x07,
|
|
||||||
0x0c, 0x07, 0x08, 0x0c, 0x0a, 0x09, 0x0c, 0x08,
|
|
||||||
0x0a, 0x0c, 0x06, 0x0b, 0x0c, 0x0a, 0x0c, 0x0c,
|
|
||||||
0x05, 0x0d, 0x0c, 0x0a, 0x0e, 0x0c, 0x0a, 0x0f,
|
|
||||||
0x0c, 0x0a, 0x10, 0x0c, 0x0a, 0x80, 0x00, 0x06,
|
|
||||||
0x81, 0x00, 0x03, 0x82, 0x00, 0x03, 0x83, 0x13,
|
|
||||||
0x40, 0x84, 0x00, 0x00, 0x85, 0x29, 0x86, 0x02,
|
|
||||||
0x87, 0x00, 0x01, 0x89, 0x00, 0x00, 0x01, 0x0a,
|
|
||||||
0x8a, 0x00, 0x10, 0x8b, 0x02, 0x00, 0x8c, 0x00,
|
|
||||||
0x02, 0x8e, 0x16, 0x80, 0x8f, 0x10, 0x40, 0x90,
|
|
||||||
0x0e, 0x10, 0x91, 0x0d, 0xde, 0x92, 0x00, 0x05,
|
|
||||||
0x93, 0x0a, 0x28, 0x94, 0x0a, 0x5a, 0x95, 0x00,
|
|
||||||
0x05, 0x96, 0x01, 0x2c, 0x97, 0x00, 0x28, 0x98,
|
|
||||||
0x01, 0x2c, 0x99, 0x00, 0x28, 0x9a, 0x00, 0x1e,
|
|
||||||
0x9b, 0x0b, 0xb8, 0x9c, 0x00, 0x0a, 0x9d, 0x01,
|
|
||||||
0x9e, 0x00, 0x5a, 0x9f, 0x00, 0x50, 0xa0, 0x00,
|
|
||||||
0x64, 0xa1, 0x00, 0x64, 0xa2, 0x00, 0x14, 0xa3,
|
|
||||||
0x00, 0x37, 0xa4, 0x00, 0x37, 0xa5, 0x00, 0x03,
|
|
||||||
0xa6, 0x00, 0x05, 0xa7, 0xff, 0xec, 0xa8, 0xff,
|
|
||||||
0xf6, 0xa9, 0x10, 0xaa, 0x00, 0x00, 0x00, 0xe6,
|
|
||||||
0xab, 0x01, 0xac, 0x01, 0xad, 0x04, 0x4d, 0xae,
|
|
||||||
0x01, 0xaf, 0x00, 0xb0, 0x00, 0x0a, 0xb1, 0x14,
|
|
||||||
0xb2, 0x32, 0x32, 0x31, 0x31, 0x38, 0x37, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0xb3, 0x00, 0xb4, 0x62, 0x65,
|
|
||||||
0x6b, 0x69, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x32,
|
|
||||||
0x33, 0x30, 0x36, 0xb6, 0x00, 0x03, 0xb7, 0x2d,
|
|
||||||
0xb7, 0x31, 0x31, 0x2e, 0x58, 0x57, 0x5f, 0x53,
|
|
||||||
0x31, 0x31, 0x2e, 0x32, 0x36, 0x32, 0x48, 0x5f,
|
|
||||||
0xb8, 0x00, 0xb9, 0x00, 0x00, 0x00, 0xe6, 0xba,
|
|
||||||
0x62, 0x65, 0x6b, 0x69, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x4a, 0x4b, 0x5f, 0x42,
|
|
||||||
0x31, 0x41, 0x32, 0x34, 0x53, 0x31, 0x35, 0x50,
|
|
||||||
0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
|
|
||||||
0x00, 0x41, 0x7b
|
|
||||||
}
|
|
||||||
};
|
|
||||||
size_t _msg_idx = 0;
|
|
||||||
size_t _byte_idx = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
} /* namespace JkBms */
|
|
||||||
@ -1,93 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
#include "JkBmsDataPoints.h"
|
|
||||||
|
|
||||||
namespace JkBms {
|
|
||||||
|
|
||||||
class SerialMessage {
|
|
||||||
public:
|
|
||||||
using tData = std::vector<uint8_t>;
|
|
||||||
|
|
||||||
SerialMessage() = delete;
|
|
||||||
|
|
||||||
enum class Command : uint8_t {
|
|
||||||
Activate = 0x01,
|
|
||||||
Write = 0x02,
|
|
||||||
Read = 0x03,
|
|
||||||
Password = 0x05,
|
|
||||||
ReadAll = 0x06
|
|
||||||
};
|
|
||||||
|
|
||||||
Command getCommand() const { return static_cast<Command>(_raw[8]); }
|
|
||||||
|
|
||||||
enum class Source : uint8_t {
|
|
||||||
BMS = 0x00,
|
|
||||||
Bluetooth = 0x01,
|
|
||||||
GPS = 0x02,
|
|
||||||
Host = 0x03
|
|
||||||
};
|
|
||||||
Source getSource() const { return static_cast<Source>(_raw[9]); }
|
|
||||||
|
|
||||||
enum class Type : uint8_t {
|
|
||||||
Command = 0x00,
|
|
||||||
Response = 0x01,
|
|
||||||
Unsolicited = 0x02
|
|
||||||
};
|
|
||||||
Type getType() const { return static_cast<Type>(_raw[10]); }
|
|
||||||
|
|
||||||
// this does *not* include the two byte start marker
|
|
||||||
uint16_t getFrameLength() const { return get<uint16_t>(_raw.cbegin()+2); }
|
|
||||||
|
|
||||||
uint32_t getTerminalId() const { return get<uint32_t>(_raw.cbegin()+4); }
|
|
||||||
|
|
||||||
// there are 20 bytes of overhead. two of those are the start marker
|
|
||||||
// bytes, which are *not* counted by the frame length.
|
|
||||||
uint16_t getVariableFieldLength() const { return getFrameLength() - 18; }
|
|
||||||
|
|
||||||
// the upper byte of the 4-byte "record number" is reserved (for encryption)
|
|
||||||
uint32_t getSequence() const { return get<uint32_t>(_raw.cend()-9) >> 8; }
|
|
||||||
|
|
||||||
bool isValid() const;
|
|
||||||
|
|
||||||
uint8_t const* data() { return _raw.data(); }
|
|
||||||
size_t size() { return _raw.size(); }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
template <typename... Args>
|
|
||||||
explicit SerialMessage(Args&&... args) : _raw(std::forward<Args>(args)...) { }
|
|
||||||
|
|
||||||
template<typename T, typename It> T get(It&& pos) const;
|
|
||||||
template<typename It> bool getBool(It&& pos) const;
|
|
||||||
template<typename It> int16_t getTemperature(It&& pos) const;
|
|
||||||
template<typename It> std::string getString(It&& pos, size_t len, bool replaceZeroes = false) const;
|
|
||||||
void processBatteryCurrent(tData::const_iterator& pos, uint8_t protocolVersion);
|
|
||||||
template<typename T> void set(tData::iterator const& pos, T val);
|
|
||||||
uint16_t calcChecksum() const;
|
|
||||||
void updateChecksum();
|
|
||||||
|
|
||||||
tData _raw;
|
|
||||||
JkBms::DataPointContainer _dp;
|
|
||||||
|
|
||||||
static constexpr uint16_t startMarker = 0x4e57;
|
|
||||||
static constexpr uint8_t endMarker = 0x68;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SerialResponse : public SerialMessage {
|
|
||||||
public:
|
|
||||||
using tData = SerialMessage::tData;
|
|
||||||
explicit SerialResponse(tData&& raw, uint8_t protocolVersion = -1);
|
|
||||||
|
|
||||||
DataPointContainer const& getDataPoints() const { return _dp; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class SerialCommand : public SerialMessage {
|
|
||||||
public:
|
|
||||||
using Command = SerialMessage::Command;
|
|
||||||
explicit SerialCommand(Command cmd);
|
|
||||||
};
|
|
||||||
|
|
||||||
} /* namespace JkBms */
|
|
||||||
@ -2,13 +2,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AsyncWebSocket.h>
|
#include <AsyncWebSocket.h>
|
||||||
|
#include <HardwareSerial.h>
|
||||||
|
#include <Stream.h>
|
||||||
#include <TaskSchedulerDeclarations.h>
|
#include <TaskSchedulerDeclarations.h>
|
||||||
#include <Print.h>
|
|
||||||
#include <freertos/task.h>
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <vector>
|
|
||||||
#include <unordered_map>
|
#define BUFFER_SIZE 500
|
||||||
#include <queue>
|
|
||||||
|
|
||||||
class MessageOutputClass : public Print {
|
class MessageOutputClass : public Print {
|
||||||
public:
|
public:
|
||||||
@ -23,19 +22,13 @@ private:
|
|||||||
|
|
||||||
Task _loopTask;
|
Task _loopTask;
|
||||||
|
|
||||||
using message_t = std::vector<uint8_t>;
|
|
||||||
|
|
||||||
// we keep a buffer for every task and only write complete lines to the
|
|
||||||
// serial output and then move them to be pushed through the websocket.
|
|
||||||
// this way we prevent mangling of messages from different contexts.
|
|
||||||
std::unordered_map<TaskHandle_t, message_t> _task_messages;
|
|
||||||
std::queue<message_t> _lines;
|
|
||||||
|
|
||||||
AsyncWebSocket* _ws = nullptr;
|
AsyncWebSocket* _ws = nullptr;
|
||||||
|
char _buffer[BUFFER_SIZE];
|
||||||
|
uint16_t _buff_pos = 0;
|
||||||
|
uint32_t _lastSend = 0;
|
||||||
|
bool _forceSend = false;
|
||||||
|
|
||||||
std::mutex _msgLock;
|
std::mutex _msgLock;
|
||||||
|
|
||||||
void serialWrite(message_t const& m);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern MessageOutputClass MessageOutput;
|
extern MessageOutputClass MessageOutput;
|
||||||
|
|||||||
@ -1,32 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include "Battery.h"
|
|
||||||
#include <espMqttClient.h>
|
|
||||||
|
|
||||||
class MqttBattery : public BatteryProvider {
|
|
||||||
public:
|
|
||||||
MqttBattery() = default;
|
|
||||||
|
|
||||||
bool init(bool verboseLogging) final;
|
|
||||||
void deinit() final;
|
|
||||||
void loop() final { return; } // this class is event-driven
|
|
||||||
std::shared_ptr<BatteryStats> getStats() const final { return _stats; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool _verboseLogging = false;
|
|
||||||
String _socTopic;
|
|
||||||
String _voltageTopic;
|
|
||||||
String _dischargeCurrentLimitTopic;
|
|
||||||
std::shared_ptr<MqttBatteryStats> _stats = std::make_shared<MqttBatteryStats>();
|
|
||||||
|
|
||||||
void onMqttMessageSoC(espMqttClientTypes::MessageProperties const& properties,
|
|
||||||
char const* topic, uint8_t const* payload, size_t len, size_t index, size_t total,
|
|
||||||
char const* jsonPath);
|
|
||||||
void onMqttMessageVoltage(espMqttClientTypes::MessageProperties const& properties,
|
|
||||||
char const* topic, uint8_t const* payload, size_t len, size_t index, size_t total,
|
|
||||||
char const* jsonPath);
|
|
||||||
void onMqttMessageDischargeCurrentLimit(espMqttClientTypes::MessageProperties const& properties,
|
|
||||||
char const* topic, uint8_t const* payload, size_t len, size_t index, size_t total,
|
|
||||||
char const* jsonPath);
|
|
||||||
};
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ArduinoJson.h>
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
|
|
||||||
class MqttHandleBatteryHassClass {
|
|
||||||
public:
|
|
||||||
void init(Scheduler& scheduler);
|
|
||||||
void forceUpdate() { _doPublish = true; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
void loop();
|
|
||||||
void publish(const String& subtopic, const String& payload);
|
|
||||||
void publishBinarySensor(const char* caption, const char* icon, const char* subTopic, const char* payload_on, const char* payload_off);
|
|
||||||
void publishSensor(const char* caption, const char* icon, const char* subTopic, const char* deviceClass = NULL, const char* stateClass = NULL, const char* unitOfMeasurement = NULL);
|
|
||||||
void createDeviceInfo(JsonObject& object);
|
|
||||||
|
|
||||||
Task _loopTask;
|
|
||||||
|
|
||||||
bool _doPublish = true;
|
|
||||||
String serial = "0001"; // pseudo-serial, can be replaced in future with real serialnumber
|
|
||||||
};
|
|
||||||
|
|
||||||
extern MqttHandleBatteryHassClass MqttHandleBatteryHass;
|
|
||||||
@ -69,9 +69,6 @@ public:
|
|||||||
void publishConfig();
|
void publishConfig();
|
||||||
void forceUpdate();
|
void forceUpdate();
|
||||||
|
|
||||||
static String getDtuUniqueId();
|
|
||||||
static String getDtuUrl();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void loop();
|
void loop();
|
||||||
static void publish(const String& subtopic, const String& payload);
|
static void publish(const String& subtopic, const String& payload);
|
||||||
@ -98,6 +95,9 @@ private:
|
|||||||
|
|
||||||
static void createDeviceInfo(JsonDocument& doc, const String& name, const String& identifiers, const String& configuration_url, const String& manufacturer, const String& model, const String& sw_version, const String& via_device = "");
|
static void createDeviceInfo(JsonDocument& doc, const String& name, const String& identifiers, const String& configuration_url, const String& manufacturer, const String& model, const String& sw_version, const String& via_device = "");
|
||||||
|
|
||||||
|
static String getDtuUniqueId();
|
||||||
|
static String getDtuUrl();
|
||||||
|
|
||||||
Task _loopTask;
|
Task _loopTask;
|
||||||
|
|
||||||
bool _wasConnected = false;
|
bool _wasConnected = false;
|
||||||
|
|||||||
@ -1,60 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Configuration.h"
|
|
||||||
#include <Huawei_can.h>
|
|
||||||
#include <espMqttClient.h>
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
#include <mutex>
|
|
||||||
#include <deque>
|
|
||||||
#include <functional>
|
|
||||||
#include <frozen/map.h>
|
|
||||||
#include <frozen/string.h>
|
|
||||||
|
|
||||||
class MqttHandleHuaweiClass {
|
|
||||||
public:
|
|
||||||
void init(Scheduler& scheduler);
|
|
||||||
|
|
||||||
void forceUpdate();
|
|
||||||
|
|
||||||
void subscribeTopics();
|
|
||||||
void unsubscribeTopics();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void loop();
|
|
||||||
|
|
||||||
enum class Topic : unsigned {
|
|
||||||
LimitOnlineVoltage,
|
|
||||||
LimitOnlineCurrent,
|
|
||||||
LimitOfflineVoltage,
|
|
||||||
LimitOfflineCurrent,
|
|
||||||
Mode
|
|
||||||
};
|
|
||||||
|
|
||||||
static constexpr frozen::string _cmdtopic = "huawei/cmd/";
|
|
||||||
static constexpr frozen::map<frozen::string, Topic, 5> _subscriptions = {
|
|
||||||
{ "limit_online_voltage", Topic::LimitOnlineVoltage },
|
|
||||||
{ "limit_online_current", Topic::LimitOnlineCurrent },
|
|
||||||
{ "limit_offline_voltage", Topic::LimitOfflineVoltage },
|
|
||||||
{ "limit_offline_current", Topic::LimitOfflineCurrent },
|
|
||||||
{ "mode", Topic::Mode },
|
|
||||||
};
|
|
||||||
|
|
||||||
void onMqttMessage(Topic t,
|
|
||||||
const espMqttClientTypes::MessageProperties& properties,
|
|
||||||
const char* topic, const uint8_t* payload, size_t len,
|
|
||||||
size_t index, size_t total);
|
|
||||||
|
|
||||||
Task _loopTask;
|
|
||||||
|
|
||||||
uint32_t _lastPublishStats;
|
|
||||||
uint32_t _lastPublish;
|
|
||||||
|
|
||||||
// MQTT callbacks to process updates on subscribed topics are executed in
|
|
||||||
// the MQTT thread's context. we use this queue to switch processing the
|
|
||||||
// user requests into the main loop's context (TaskScheduler context).
|
|
||||||
mutable std::mutex _mqttMutex;
|
|
||||||
std::deque<std::function<void()>> _mqttCallbacks;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern MqttHandleHuaweiClass MqttHandleHuawei;
|
|
||||||
@ -1,66 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Configuration.h"
|
|
||||||
#include <espMqttClient.h>
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
#include <mutex>
|
|
||||||
#include <deque>
|
|
||||||
#include <functional>
|
|
||||||
#include <frozen/map.h>
|
|
||||||
#include <frozen/string.h>
|
|
||||||
|
|
||||||
class MqttHandlePowerLimiterClass {
|
|
||||||
public:
|
|
||||||
void init(Scheduler& scheduler);
|
|
||||||
|
|
||||||
void forceUpdate();
|
|
||||||
|
|
||||||
void subscribeTopics();
|
|
||||||
void unsubscribeTopics();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void loop();
|
|
||||||
|
|
||||||
enum class MqttPowerLimiterCommand : unsigned {
|
|
||||||
Mode,
|
|
||||||
BatterySoCStartThreshold,
|
|
||||||
BatterySoCStopThreshold,
|
|
||||||
FullSolarPassthroughSoC,
|
|
||||||
VoltageStartThreshold,
|
|
||||||
VoltageStopThreshold,
|
|
||||||
FullSolarPassThroughStartVoltage,
|
|
||||||
FullSolarPassThroughStopVoltage,
|
|
||||||
UpperPowerLimit,
|
|
||||||
TargetPowerConsumption
|
|
||||||
};
|
|
||||||
|
|
||||||
static constexpr frozen::string _cmdtopic = "powerlimiter/cmd/";
|
|
||||||
static constexpr frozen::map<frozen::string, MqttPowerLimiterCommand, 10> _subscriptions = {
|
|
||||||
{ "threshold/soc/start", MqttPowerLimiterCommand::BatterySoCStartThreshold },
|
|
||||||
{ "threshold/soc/stop", MqttPowerLimiterCommand::BatterySoCStopThreshold },
|
|
||||||
{ "threshold/soc/full_solar_passthrough", MqttPowerLimiterCommand::FullSolarPassthroughSoC },
|
|
||||||
{ "threshold/voltage/start", MqttPowerLimiterCommand::VoltageStartThreshold },
|
|
||||||
{ "threshold/voltage/stop", MqttPowerLimiterCommand::VoltageStopThreshold },
|
|
||||||
{ "threshold/voltage/full_solar_passthrough_start", MqttPowerLimiterCommand::FullSolarPassThroughStartVoltage },
|
|
||||||
{ "threshold/voltage/full_solar_passthrough_stop", MqttPowerLimiterCommand::FullSolarPassThroughStopVoltage },
|
|
||||||
{ "mode", MqttPowerLimiterCommand::Mode },
|
|
||||||
{ "upper_power_limit", MqttPowerLimiterCommand::UpperPowerLimit },
|
|
||||||
{ "target_power_consumption", MqttPowerLimiterCommand::TargetPowerConsumption },
|
|
||||||
};
|
|
||||||
|
|
||||||
void onMqttCmd(MqttPowerLimiterCommand command, const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total);
|
|
||||||
|
|
||||||
Task _loopTask;
|
|
||||||
|
|
||||||
uint32_t _lastPublishStats;
|
|
||||||
uint32_t _lastPublish;
|
|
||||||
|
|
||||||
// MQTT callbacks to process updates on subscribed topics are executed in
|
|
||||||
// the MQTT thread's context. we use this queue to switch processing the
|
|
||||||
// user requests into the main loop's context (TaskScheduler context).
|
|
||||||
mutable std::mutex _mqttMutex;
|
|
||||||
std::deque<std::function<void()>> _mqttCallbacks;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern MqttHandlePowerLimiterClass MqttHandlePowerLimiter;
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ArduinoJson.h>
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
|
|
||||||
class MqttHandlePowerLimiterHassClass {
|
|
||||||
public:
|
|
||||||
void init(Scheduler& scheduler);
|
|
||||||
void publishConfig();
|
|
||||||
void forceUpdate();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void loop();
|
|
||||||
void publish(const String& subtopic, const String& payload);
|
|
||||||
void publishNumber(const char* caption, const char* icon, const char* category, const char* commandTopic, const char* stateTopic, const char* unitOfMeasure, const int16_t min, const int16_t max, const float step);
|
|
||||||
void publishSelect(const char* caption, const char* icon, const char* category, const char* commandTopic, const char* stateTopic);
|
|
||||||
void publishBinarySensor(const char* caption, const char* icon, const char* stateTopic, const char* payload_on, const char* payload_off);
|
|
||||||
void createDeviceInfo(JsonDocument& root);
|
|
||||||
|
|
||||||
Task _loopTask;
|
|
||||||
|
|
||||||
bool _wasConnected = false;
|
|
||||||
bool _updateForced = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern MqttHandlePowerLimiterHassClass MqttHandlePowerLimiterHass;
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "VeDirectMpptController.h"
|
|
||||||
#include "Configuration.h"
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <map>
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
|
|
||||||
#ifndef VICTRON_PIN_RX
|
|
||||||
#define VICTRON_PIN_RX 22
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef VICTRON_PIN_TX
|
|
||||||
#define VICTRON_PIN_TX 21
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class MqttHandleVedirectClass {
|
|
||||||
public:
|
|
||||||
void init(Scheduler& scheduler);
|
|
||||||
void forceUpdate();
|
|
||||||
private:
|
|
||||||
void loop();
|
|
||||||
std::map<std::string, VeDirectMpptController::data_t> _kvFrames;
|
|
||||||
|
|
||||||
Task _loopTask;
|
|
||||||
|
|
||||||
// point of time in millis() when updated values will be published
|
|
||||||
uint32_t _nextPublishUpdatesOnly = 0;
|
|
||||||
|
|
||||||
// point of time in millis() when all values will be published
|
|
||||||
uint32_t _nextPublishFull = 1;
|
|
||||||
|
|
||||||
bool _PublishFull;
|
|
||||||
|
|
||||||
void publish_mppt_data(const VeDirectMpptController::data_t &mpptData,
|
|
||||||
const VeDirectMpptController::data_t &frame) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern MqttHandleVedirectClass MqttHandleVedirect;
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ArduinoJson.h>
|
|
||||||
#include "VeDirectMpptController.h"
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
|
|
||||||
class MqttHandleVedirectHassClass {
|
|
||||||
public:
|
|
||||||
void init(Scheduler& scheduler);
|
|
||||||
void publishConfig();
|
|
||||||
void forceUpdate();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void loop();
|
|
||||||
void publish(const String& subtopic, const String& payload);
|
|
||||||
void publishBinarySensor(const char *caption, const char *icon, const char *subTopic,
|
|
||||||
const char *payload_on, const char *payload_off,
|
|
||||||
const VeDirectMpptController::data_t &mpptData);
|
|
||||||
void publishSensor(const char *caption, const char *icon, const char *subTopic,
|
|
||||||
const char *deviceClass, const char *stateClass,
|
|
||||||
const char *unitOfMeasurement,
|
|
||||||
const VeDirectMpptController::data_t &mpptData);
|
|
||||||
void createDeviceInfo(JsonObject &object,
|
|
||||||
const VeDirectMpptController::data_t &mpptData);
|
|
||||||
|
|
||||||
Task _loopTask;
|
|
||||||
|
|
||||||
bool _wasConnected = false;
|
|
||||||
bool _updateForced = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern MqttHandleVedirectHassClass MqttHandleVedirectHass;
|
|
||||||
@ -11,7 +11,6 @@ class MqttSettingsClass {
|
|||||||
public:
|
public:
|
||||||
MqttSettingsClass();
|
MqttSettingsClass();
|
||||||
void init();
|
void init();
|
||||||
void loop();
|
|
||||||
void performReconnect();
|
void performReconnect();
|
||||||
bool getConnected();
|
bool getConnected();
|
||||||
void publish(const String& subtopic, const String& payload);
|
void publish(const String& subtopic, const String& payload);
|
||||||
@ -21,7 +20,7 @@ public:
|
|||||||
void unsubscribe(const String& topic);
|
void unsubscribe(const String& topic);
|
||||||
|
|
||||||
String getPrefix() const;
|
String getPrefix() const;
|
||||||
String getClientId();
|
String getClientId() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void NetworkEvent(network_event event);
|
void NetworkEvent(network_event event);
|
||||||
@ -39,7 +38,6 @@ private:
|
|||||||
Ticker _mqttReconnectTimer;
|
Ticker _mqttReconnectTimer;
|
||||||
MqttSubscribeParser _mqttSubscribeParser;
|
MqttSubscribeParser _mqttSubscribeParser;
|
||||||
std::mutex _clientLock;
|
std::mutex _clientLock;
|
||||||
bool _verboseLogging = true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern MqttSettingsClass MqttSettings;
|
extern MqttSettingsClass MqttSettings;
|
||||||
|
|||||||
@ -51,29 +51,6 @@ struct PinMapping_t {
|
|||||||
uint8_t display_reset;
|
uint8_t display_reset;
|
||||||
|
|
||||||
int8_t led[PINMAPPING_LED_COUNT];
|
int8_t led[PINMAPPING_LED_COUNT];
|
||||||
|
|
||||||
// OpenDTU-OnBattery-specific pins below
|
|
||||||
int8_t victron_tx;
|
|
||||||
int8_t victron_rx;
|
|
||||||
int8_t victron_tx2;
|
|
||||||
int8_t victron_rx2;
|
|
||||||
int8_t victron_tx3;
|
|
||||||
int8_t victron_rx3;
|
|
||||||
int8_t battery_rx;
|
|
||||||
int8_t battery_rxen;
|
|
||||||
int8_t battery_tx;
|
|
||||||
int8_t battery_txen;
|
|
||||||
int8_t huawei_miso;
|
|
||||||
int8_t huawei_mosi;
|
|
||||||
int8_t huawei_clk;
|
|
||||||
int8_t huawei_irq;
|
|
||||||
int8_t huawei_cs;
|
|
||||||
int8_t huawei_power;
|
|
||||||
int8_t powermeter_rx;
|
|
||||||
int8_t powermeter_tx;
|
|
||||||
int8_t powermeter_dere;
|
|
||||||
int8_t powermeter_rxen;
|
|
||||||
int8_t powermeter_txen;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class PinMappingClass {
|
class PinMappingClass {
|
||||||
@ -82,16 +59,19 @@ public:
|
|||||||
bool init(const String& deviceMapping);
|
bool init(const String& deviceMapping);
|
||||||
PinMapping_t& get();
|
PinMapping_t& get();
|
||||||
|
|
||||||
|
bool isMappingSelected() const { return _mappingSelected; }
|
||||||
|
|
||||||
bool isValidNrf24Config() const;
|
bool isValidNrf24Config() const;
|
||||||
bool isValidCmt2300Config() const;
|
bool isValidCmt2300Config() const;
|
||||||
bool isValidW5500Config() const;
|
bool isValidW5500Config() const;
|
||||||
#if CONFIG_ETH_USE_ESP32_EMAC
|
#if CONFIG_ETH_USE_ESP32_EMAC
|
||||||
bool isValidEthConfig() const;
|
bool isValidEthConfig() const;
|
||||||
#endif
|
#endif
|
||||||
bool isValidHuaweiConfig() const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PinMapping_t _pinMapping;
|
PinMapping_t _pinMapping;
|
||||||
|
|
||||||
|
bool _mappingSelected = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern PinMappingClass PinMapping;
|
extern PinMappingClass PinMapping;
|
||||||
|
|||||||
@ -1,107 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Configuration.h"
|
|
||||||
#include <espMqttClient.h>
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <Hoymiles.h>
|
|
||||||
#include <memory>
|
|
||||||
#include <functional>
|
|
||||||
#include <optional>
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
#include <frozen/string.h>
|
|
||||||
|
|
||||||
#define PL_UI_STATE_INACTIVE 0
|
|
||||||
#define PL_UI_STATE_CHARGING 1
|
|
||||||
#define PL_UI_STATE_USE_SOLAR_ONLY 2
|
|
||||||
#define PL_UI_STATE_USE_SOLAR_AND_BATTERY 3
|
|
||||||
|
|
||||||
class PowerLimiterClass {
|
|
||||||
public:
|
|
||||||
enum class Status : unsigned {
|
|
||||||
Initializing,
|
|
||||||
DisabledByConfig,
|
|
||||||
DisabledByMqtt,
|
|
||||||
WaitingForValidTimestamp,
|
|
||||||
PowerMeterPending,
|
|
||||||
InverterInvalid,
|
|
||||||
InverterChanged,
|
|
||||||
InverterOffline,
|
|
||||||
InverterCommandsDisabled,
|
|
||||||
InverterLimitPending,
|
|
||||||
InverterPowerCmdPending,
|
|
||||||
InverterDevInfoPending,
|
|
||||||
InverterStatsPending,
|
|
||||||
CalculatedLimitBelowMinLimit,
|
|
||||||
UnconditionalSolarPassthrough,
|
|
||||||
NoVeDirect,
|
|
||||||
NoEnergy,
|
|
||||||
HuaweiPsu,
|
|
||||||
Stable,
|
|
||||||
};
|
|
||||||
|
|
||||||
void init(Scheduler& scheduler);
|
|
||||||
uint8_t getInverterUpdateTimeouts() const { return _inverterUpdateTimeouts; }
|
|
||||||
uint8_t getPowerLimiterState();
|
|
||||||
int32_t getLastRequestedPowerLimit() { return _lastRequestedPowerLimit; }
|
|
||||||
bool getFullSolarPassThroughEnabled() const { return _fullSolarPassThroughEnabled; }
|
|
||||||
|
|
||||||
enum class Mode : unsigned {
|
|
||||||
Normal = 0,
|
|
||||||
Disabled = 1,
|
|
||||||
UnconditionalFullSolarPassthrough = 2
|
|
||||||
};
|
|
||||||
|
|
||||||
void setMode(Mode m) { _mode = m; }
|
|
||||||
Mode getMode() const { return _mode; }
|
|
||||||
void calcNextInverterRestart();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void loop();
|
|
||||||
|
|
||||||
Task _loopTask;
|
|
||||||
|
|
||||||
int32_t _lastRequestedPowerLimit = 0;
|
|
||||||
bool _shutdownPending = false;
|
|
||||||
std::optional<uint32_t> _oInverterStatsMillis = std::nullopt;
|
|
||||||
std::optional<uint32_t> _oUpdateStartMillis = std::nullopt;
|
|
||||||
std::optional<int32_t> _oTargetPowerLimitWatts = std::nullopt;
|
|
||||||
std::optional<bool> _oTargetPowerState = std::nullopt;
|
|
||||||
Status _lastStatus = Status::Initializing;
|
|
||||||
uint32_t _lastStatusPrinted = 0;
|
|
||||||
uint32_t _lastCalculation = 0;
|
|
||||||
static constexpr uint32_t _calculationBackoffMsDefault = 128;
|
|
||||||
uint32_t _calculationBackoffMs = _calculationBackoffMsDefault;
|
|
||||||
Mode _mode = Mode::Normal;
|
|
||||||
std::shared_ptr<InverterAbstract> _inverter = nullptr;
|
|
||||||
bool _batteryDischargeEnabled = false;
|
|
||||||
bool _nighttimeDischarging = false;
|
|
||||||
uint32_t _nextInverterRestart = 0; // Values: 0->not calculated / 1->no restart configured / >1->time of next inverter restart in millis()
|
|
||||||
uint32_t _nextCalculateCheck = 5000; // time in millis for next NTP check to calulate restart
|
|
||||||
bool _fullSolarPassThroughEnabled = false;
|
|
||||||
bool _verboseLogging = true;
|
|
||||||
uint8_t _inverterUpdateTimeouts = 0;
|
|
||||||
|
|
||||||
frozen::string const& getStatusText(Status status);
|
|
||||||
void announceStatus(Status status);
|
|
||||||
bool shutdown(Status status);
|
|
||||||
bool shutdown() { return shutdown(_lastStatus); }
|
|
||||||
float getBatteryVoltage(bool log = false);
|
|
||||||
int32_t inverterPowerDcToAc(std::shared_ptr<InverterAbstract> inverter, int32_t dcPower);
|
|
||||||
void unconditionalSolarPassthrough(std::shared_ptr<InverterAbstract> inverter);
|
|
||||||
bool canUseDirectSolarPower();
|
|
||||||
bool calcPowerLimit(std::shared_ptr<InverterAbstract> inverter, int32_t solarPower, int32_t batteryPowerLimit, bool batteryPower);
|
|
||||||
bool updateInverter();
|
|
||||||
bool setNewPowerLimit(std::shared_ptr<InverterAbstract> inverter, int32_t newPowerLimit);
|
|
||||||
int32_t getSolarPower();
|
|
||||||
int32_t getBatteryDischargeLimit();
|
|
||||||
float getLoadCorrectedVoltage();
|
|
||||||
bool testThreshold(float socThreshold, float voltThreshold,
|
|
||||||
std::function<bool(float, float)> compare);
|
|
||||||
bool isStartThresholdReached();
|
|
||||||
bool isStopThresholdReached();
|
|
||||||
bool isBelowStopThreshold();
|
|
||||||
bool useFullSolarPassthrough();
|
|
||||||
};
|
|
||||||
|
|
||||||
extern PowerLimiterClass PowerLimiter;
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "PowerMeterProvider.h"
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
class PowerMeterClass {
|
|
||||||
public:
|
|
||||||
void init(Scheduler& scheduler);
|
|
||||||
|
|
||||||
void updateSettings();
|
|
||||||
|
|
||||||
float getPowerTotal() const;
|
|
||||||
uint32_t getLastUpdate() const;
|
|
||||||
bool isDataValid() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void loop();
|
|
||||||
|
|
||||||
Task _loopTask;
|
|
||||||
mutable std::mutex _mutex;
|
|
||||||
std::unique_ptr<PowerMeterProvider> _upProvider = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern PowerMeterClass PowerMeter;
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <array>
|
|
||||||
#include <variant>
|
|
||||||
#include <memory>
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <mutex>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include "HttpGetter.h"
|
|
||||||
#include "Configuration.h"
|
|
||||||
#include "PowerMeterProvider.h"
|
|
||||||
|
|
||||||
using Auth_t = HttpRequestConfig::Auth;
|
|
||||||
using Unit_t = PowerMeterHttpJsonValue::Unit;
|
|
||||||
|
|
||||||
class PowerMeterHttpJson : public PowerMeterProvider {
|
|
||||||
public:
|
|
||||||
explicit PowerMeterHttpJson(PowerMeterHttpJsonConfig const& cfg)
|
|
||||||
: _cfg(cfg) { }
|
|
||||||
|
|
||||||
~PowerMeterHttpJson();
|
|
||||||
|
|
||||||
bool init() final;
|
|
||||||
void loop() final;
|
|
||||||
float getPowerTotal() const final;
|
|
||||||
bool isDataValid() const final;
|
|
||||||
void doMqttPublish() const final;
|
|
||||||
|
|
||||||
using power_values_t = std::array<float, POWERMETER_HTTP_JSON_MAX_VALUES>;
|
|
||||||
using poll_result_t = std::variant<power_values_t, String>;
|
|
||||||
poll_result_t poll();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static void pollingLoopHelper(void* context);
|
|
||||||
std::atomic<bool> _taskDone;
|
|
||||||
void pollingLoop();
|
|
||||||
|
|
||||||
PowerMeterHttpJsonConfig const _cfg;
|
|
||||||
|
|
||||||
uint32_t _lastPoll = 0;
|
|
||||||
|
|
||||||
mutable std::mutex _valueMutex;
|
|
||||||
power_values_t _powerValues = {};
|
|
||||||
|
|
||||||
std::array<std::unique_ptr<HttpGetter>, POWERMETER_HTTP_JSON_MAX_VALUES> _httpGetters;
|
|
||||||
|
|
||||||
TaskHandle_t _taskHandle = nullptr;
|
|
||||||
bool _stopPolling;
|
|
||||||
mutable std::mutex _pollingMutex;
|
|
||||||
std::condition_variable _cv;
|
|
||||||
};
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <memory>
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <mutex>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include "HttpGetter.h"
|
|
||||||
#include "Configuration.h"
|
|
||||||
#include "PowerMeterSml.h"
|
|
||||||
|
|
||||||
class PowerMeterHttpSml : public PowerMeterSml {
|
|
||||||
public:
|
|
||||||
explicit PowerMeterHttpSml(PowerMeterHttpSmlConfig const& cfg)
|
|
||||||
: PowerMeterSml("PowerMeterHttpSml")
|
|
||||||
, _cfg(cfg) { }
|
|
||||||
|
|
||||||
~PowerMeterHttpSml();
|
|
||||||
|
|
||||||
bool init() final;
|
|
||||||
void loop() final;
|
|
||||||
bool isDataValid() const final;
|
|
||||||
|
|
||||||
// returns an empty string on success,
|
|
||||||
// returns an error message otherwise.
|
|
||||||
String poll();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static void pollingLoopHelper(void* context);
|
|
||||||
std::atomic<bool> _taskDone;
|
|
||||||
void pollingLoop();
|
|
||||||
|
|
||||||
PowerMeterHttpSmlConfig const _cfg;
|
|
||||||
|
|
||||||
uint32_t _lastPoll = 0;
|
|
||||||
|
|
||||||
std::unique_ptr<HttpGetter> _upHttpGetter;
|
|
||||||
|
|
||||||
TaskHandle_t _taskHandle = nullptr;
|
|
||||||
bool _stopPolling;
|
|
||||||
mutable std::mutex _pollingMutex;
|
|
||||||
std::condition_variable _cv;
|
|
||||||
};
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Configuration.h"
|
|
||||||
#include "PowerMeterProvider.h"
|
|
||||||
#include <espMqttClient.h>
|
|
||||||
#include <vector>
|
|
||||||
#include <mutex>
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
class PowerMeterMqtt : public PowerMeterProvider {
|
|
||||||
public:
|
|
||||||
explicit PowerMeterMqtt(PowerMeterMqttConfig const& cfg)
|
|
||||||
: _cfg(cfg) { }
|
|
||||||
|
|
||||||
~PowerMeterMqtt();
|
|
||||||
|
|
||||||
bool init() final;
|
|
||||||
void loop() final { }
|
|
||||||
float getPowerTotal() const final;
|
|
||||||
|
|
||||||
private:
|
|
||||||
using MsgProperties = espMqttClientTypes::MessageProperties;
|
|
||||||
void onMessage(MsgProperties const& properties, char const* topic,
|
|
||||||
uint8_t const* payload, size_t len, size_t index,
|
|
||||||
size_t total, float* targetVariable, PowerMeterMqttValue const* cfg);
|
|
||||||
|
|
||||||
// we don't need to republish data received from MQTT
|
|
||||||
void doMqttPublish() const final { };
|
|
||||||
|
|
||||||
PowerMeterMqttConfig const _cfg;
|
|
||||||
|
|
||||||
using power_values_t = std::array<float, POWERMETER_MQTT_MAX_VALUES>;
|
|
||||||
power_values_t _powerValues;
|
|
||||||
|
|
||||||
std::vector<String> _mqttSubscriptions;
|
|
||||||
|
|
||||||
mutable std::mutex _mutex;
|
|
||||||
};
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include "Configuration.h"
|
|
||||||
|
|
||||||
class PowerMeterProvider {
|
|
||||||
public:
|
|
||||||
virtual ~PowerMeterProvider() { }
|
|
||||||
|
|
||||||
enum class Type : unsigned {
|
|
||||||
MQTT = 0,
|
|
||||||
SDM1PH = 1,
|
|
||||||
SDM3PH = 2,
|
|
||||||
HTTP_JSON = 3,
|
|
||||||
SERIAL_SML = 4,
|
|
||||||
SMAHM2 = 5,
|
|
||||||
HTTP_SML = 6
|
|
||||||
};
|
|
||||||
|
|
||||||
// returns true if the provider is ready for use, false otherwise
|
|
||||||
virtual bool init() = 0;
|
|
||||||
|
|
||||||
virtual void loop() = 0;
|
|
||||||
virtual float getPowerTotal() const = 0;
|
|
||||||
virtual bool isDataValid() const;
|
|
||||||
|
|
||||||
uint32_t getLastUpdate() const { return _lastUpdate; }
|
|
||||||
void mqttLoop() const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
PowerMeterProvider() {
|
|
||||||
auto const& config = Configuration.get();
|
|
||||||
_verboseLogging = config.PowerMeter.VerboseLogging;
|
|
||||||
}
|
|
||||||
|
|
||||||
void gotUpdate() { _lastUpdate = millis(); }
|
|
||||||
|
|
||||||
void mqttPublish(String const& topic, float const& value) const;
|
|
||||||
|
|
||||||
bool _verboseLogging;
|
|
||||||
|
|
||||||
private:
|
|
||||||
virtual void doMqttPublish() const = 0;
|
|
||||||
|
|
||||||
// gotUpdate() updates this variable potentially from a different thread
|
|
||||||
// than users that request to read this variable through getLastUpdate().
|
|
||||||
std::atomic<uint32_t> _lastUpdate = 0;
|
|
||||||
|
|
||||||
mutable uint32_t _lastMqttPublish = 0;
|
|
||||||
};
|
|
||||||
@ -1,60 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <mutex>
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <SoftwareSerial.h>
|
|
||||||
#include "Configuration.h"
|
|
||||||
#include "PowerMeterProvider.h"
|
|
||||||
#include "SDM.h"
|
|
||||||
|
|
||||||
class PowerMeterSerialSdm : public PowerMeterProvider {
|
|
||||||
public:
|
|
||||||
enum class Phases {
|
|
||||||
One,
|
|
||||||
Three
|
|
||||||
};
|
|
||||||
|
|
||||||
PowerMeterSerialSdm(Phases phases, PowerMeterSerialSdmConfig const& cfg)
|
|
||||||
: _phases(phases)
|
|
||||||
, _cfg(cfg) { }
|
|
||||||
|
|
||||||
~PowerMeterSerialSdm();
|
|
||||||
|
|
||||||
bool init() final;
|
|
||||||
void loop() final;
|
|
||||||
float getPowerTotal() const final;
|
|
||||||
bool isDataValid() const final;
|
|
||||||
void doMqttPublish() const final;
|
|
||||||
|
|
||||||
private:
|
|
||||||
static void pollingLoopHelper(void* context);
|
|
||||||
bool readValue(std::unique_lock<std::mutex>& lock, uint16_t reg, float& targetVar);
|
|
||||||
std::atomic<bool> _taskDone;
|
|
||||||
void pollingLoop();
|
|
||||||
|
|
||||||
Phases _phases;
|
|
||||||
PowerMeterSerialSdmConfig const _cfg;
|
|
||||||
|
|
||||||
uint32_t _lastPoll = 0;
|
|
||||||
|
|
||||||
float _phase1Power = 0.0;
|
|
||||||
float _phase2Power = 0.0;
|
|
||||||
float _phase3Power = 0.0;
|
|
||||||
float _phase1Voltage = 0.0;
|
|
||||||
float _phase2Voltage = 0.0;
|
|
||||||
float _phase3Voltage = 0.0;
|
|
||||||
float _energyImport = 0.0;
|
|
||||||
float _energyExport = 0.0;
|
|
||||||
|
|
||||||
mutable std::mutex _valueMutex;
|
|
||||||
|
|
||||||
std::unique_ptr<SoftwareSerial> _upSdmSerial = nullptr;
|
|
||||||
std::unique_ptr<SDM> _upSdm = nullptr;
|
|
||||||
|
|
||||||
TaskHandle_t _taskHandle = nullptr;
|
|
||||||
bool _stopPolling;
|
|
||||||
mutable std::mutex _pollingMutex;
|
|
||||||
std::condition_variable _cv;
|
|
||||||
};
|
|
||||||
@ -1,44 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "PowerMeterSml.h"
|
|
||||||
#include <SoftwareSerial.h>
|
|
||||||
|
|
||||||
class PowerMeterSerialSml : public PowerMeterSml {
|
|
||||||
public:
|
|
||||||
PowerMeterSerialSml()
|
|
||||||
: PowerMeterSml("PowerMeterSerialSml") { }
|
|
||||||
|
|
||||||
~PowerMeterSerialSml();
|
|
||||||
|
|
||||||
bool init() final;
|
|
||||||
void loop() final;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// we assume that an SML datagram is complete after no additional
|
|
||||||
// characters were received for this many milliseconds.
|
|
||||||
static uint8_t constexpr _datagramGapMillis = 50;
|
|
||||||
|
|
||||||
static uint32_t constexpr _baud = 9600;
|
|
||||||
|
|
||||||
// size in bytes of the software serial receive buffer. must have the
|
|
||||||
// capacity to hold a full SML datagram, as we are processing the datagrams
|
|
||||||
// only after all data of one datagram was received.
|
|
||||||
static int constexpr _bufCapacity = 1024; // memory usage: 1 byte each
|
|
||||||
|
|
||||||
// amount of bits (RX pin state transitions) the software serial can buffer
|
|
||||||
// without decoding bits to bytes and storing those in the receive buffer.
|
|
||||||
// this value dictates how ofter we need to call a function of the software
|
|
||||||
// serial instance that performs bit decoding (we call available()).
|
|
||||||
static int constexpr _isrCapacity = 256; // memory usage: 8 bytes each (timestamp + pointer)
|
|
||||||
|
|
||||||
static void pollingLoopHelper(void* context);
|
|
||||||
std::atomic<bool> _taskDone;
|
|
||||||
void pollingLoop();
|
|
||||||
|
|
||||||
TaskHandle_t _taskHandle = nullptr;
|
|
||||||
bool _stopPolling;
|
|
||||||
mutable std::mutex _pollingMutex;
|
|
||||||
|
|
||||||
std::unique_ptr<SoftwareSerial> _upSmlSerial = nullptr;
|
|
||||||
};
|
|
||||||
@ -1,69 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <list>
|
|
||||||
#include <mutex>
|
|
||||||
#include <optional>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <HTTPClient.h>
|
|
||||||
#include "Configuration.h"
|
|
||||||
#include "PowerMeterProvider.h"
|
|
||||||
#include "sml.h"
|
|
||||||
|
|
||||||
class PowerMeterSml : public PowerMeterProvider {
|
|
||||||
public:
|
|
||||||
float getPowerTotal() const final;
|
|
||||||
void doMqttPublish() const final;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
explicit PowerMeterSml(char const* user)
|
|
||||||
: _user(user) { }
|
|
||||||
|
|
||||||
void reset();
|
|
||||||
void processSmlByte(uint8_t byte);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string _user;
|
|
||||||
mutable std::mutex _mutex;
|
|
||||||
|
|
||||||
using values_t = struct {
|
|
||||||
std::optional<float> activePowerTotal = std::nullopt;
|
|
||||||
std::optional<float> activePowerL1 = std::nullopt;
|
|
||||||
std::optional<float> activePowerL2 = std::nullopt;
|
|
||||||
std::optional<float> activePowerL3 = std::nullopt;
|
|
||||||
std::optional<float> voltageL1 = std::nullopt;
|
|
||||||
std::optional<float> voltageL2 = std::nullopt;
|
|
||||||
std::optional<float> voltageL3 = std::nullopt;
|
|
||||||
std::optional<float> currentL1 = std::nullopt;
|
|
||||||
std::optional<float> currentL2 = std::nullopt;
|
|
||||||
std::optional<float> currentL3 = std::nullopt;
|
|
||||||
std::optional<float> energyImport = std::nullopt;
|
|
||||||
std::optional<float> energyExport = std::nullopt;
|
|
||||||
};
|
|
||||||
|
|
||||||
values_t _values;
|
|
||||||
values_t _cache;
|
|
||||||
|
|
||||||
using OBISHandler = struct {
|
|
||||||
uint8_t const OBIS[6];
|
|
||||||
void (*decoder)(float&);
|
|
||||||
std::optional<float>* target;
|
|
||||||
char const* name;
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::list<OBISHandler> smlHandlerList{
|
|
||||||
{{0x01, 0x00, 0x10, 0x07, 0x00, 0xff}, &smlOBISW, &_cache.activePowerTotal, "active power total"},
|
|
||||||
{{0x01, 0x00, 0x24, 0x07, 0x00, 0xff}, &smlOBISW, &_cache.activePowerL1, "active power L1"},
|
|
||||||
{{0x01, 0x00, 0x38, 0x07, 0x00, 0xff}, &smlOBISW, &_cache.activePowerL2, "active power L2"},
|
|
||||||
{{0x01, 0x00, 0x4c, 0x07, 0x00, 0xff}, &smlOBISW, &_cache.activePowerL3, "active power L3"},
|
|
||||||
{{0x01, 0x00, 0x20, 0x07, 0x00, 0xff}, &smlOBISVolt, &_cache.voltageL1, "voltage L1"},
|
|
||||||
{{0x01, 0x00, 0x34, 0x07, 0x00, 0xff}, &smlOBISVolt, &_cache.voltageL2, "voltage L2"},
|
|
||||||
{{0x01, 0x00, 0x48, 0x07, 0x00, 0xff}, &smlOBISVolt, &_cache.voltageL3, "voltage L3"},
|
|
||||||
{{0x01, 0x00, 0x1f, 0x07, 0x00, 0xff}, &smlOBISAmpere, &_cache.currentL1, "current L1"},
|
|
||||||
{{0x01, 0x00, 0x33, 0x07, 0x00, 0xff}, &smlOBISAmpere, &_cache.currentL2, "current L2"},
|
|
||||||
{{0x01, 0x00, 0x47, 0x07, 0x00, 0xff}, &smlOBISAmpere, &_cache.currentL3, "current L3"},
|
|
||||||
{{0x01, 0x00, 0x01, 0x08, 0x00, 0xff}, &smlOBISWh, &_cache.energyImport, "energy import"},
|
|
||||||
{{0x01, 0x00, 0x02, 0x08, 0x00, 0xff}, &smlOBISWh, &_cache.energyExport, "energy export"}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2024 Holger-Steffen Stapf
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include "PowerMeterProvider.h"
|
|
||||||
|
|
||||||
class PowerMeterUdpSmaHomeManager : public PowerMeterProvider {
|
|
||||||
public:
|
|
||||||
~PowerMeterUdpSmaHomeManager();
|
|
||||||
|
|
||||||
bool init() final;
|
|
||||||
void loop() final;
|
|
||||||
float getPowerTotal() const final { return _powerMeterPower; }
|
|
||||||
void doMqttPublish() const final;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void Soutput(int kanal, int index, int art, int tarif,
|
|
||||||
char const* name, float value, uint32_t timestamp);
|
|
||||||
|
|
||||||
uint8_t* decodeGroup(uint8_t* offset, uint16_t grouplen);
|
|
||||||
|
|
||||||
float _powerMeterPower = 0.0;
|
|
||||||
float _powerMeterL1 = 0.0;
|
|
||||||
float _powerMeterL2 = 0.0;
|
|
||||||
float _powerMeterL3 = 0.0;
|
|
||||||
uint32_t _previousMillis = 0;
|
|
||||||
uint32_t _serial = 0;
|
|
||||||
};
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Configuration.h"
|
|
||||||
#include "Battery.h"
|
|
||||||
#include "BatteryCanReceiver.h"
|
|
||||||
#include <driver/twai.h>
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
class PylontechCanReceiver : public BatteryCanReceiver {
|
|
||||||
public:
|
|
||||||
bool init(bool verboseLogging) final;
|
|
||||||
void onMessage(twai_message_t rx_message) final;
|
|
||||||
|
|
||||||
std::shared_ptr<BatteryStats> getStats() const final { return _stats; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
void dummyData();
|
|
||||||
|
|
||||||
std::shared_ptr<PylontechBatteryStats> _stats =
|
|
||||||
std::make_shared<PylontechBatteryStats>();
|
|
||||||
};
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Configuration.h"
|
|
||||||
#include "Battery.h"
|
|
||||||
#include "BatteryCanReceiver.h"
|
|
||||||
#include <driver/twai.h>
|
|
||||||
|
|
||||||
class PytesCanReceiver : public BatteryCanReceiver {
|
|
||||||
public:
|
|
||||||
bool init(bool verboseLogging) final;
|
|
||||||
void onMessage(twai_message_t rx_message) final;
|
|
||||||
|
|
||||||
std::shared_ptr<BatteryStats> getStats() const final { return _stats; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<PytesBatteryStats> _stats =
|
|
||||||
std::make_shared<PytesBatteryStats>();
|
|
||||||
};
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "BatteryCanReceiver.h"
|
|
||||||
#include <driver/twai.h>
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
class SBSCanReceiver : public BatteryCanReceiver {
|
|
||||||
public:
|
|
||||||
bool init(bool verboseLogging) final;
|
|
||||||
void onMessage(twai_message_t rx_message) final;
|
|
||||||
|
|
||||||
std::shared_ptr<BatteryStats> getStats() const final { return _stats; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
void dummyData();
|
|
||||||
std::shared_ptr<SBSBatteryStats> _stats =
|
|
||||||
std::make_shared<SBSBatteryStats>();
|
|
||||||
};
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
class SerialPortManagerClass {
|
|
||||||
public:
|
|
||||||
void init();
|
|
||||||
|
|
||||||
std::optional<uint8_t> allocatePort(std::string const& owner);
|
|
||||||
void freePort(std::string const& owner);
|
|
||||||
|
|
||||||
private:
|
|
||||||
// the amount of hardare UARTs available on supported ESP32 chips
|
|
||||||
static size_t constexpr _num_controllers = 3;
|
|
||||||
std::array<std::string, _num_controllers> _ports = { "" };
|
|
||||||
};
|
|
||||||
|
|
||||||
extern SerialPortManagerClass SerialPortManager;
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
#include <WiFiUdp.h>
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
class SyslogLogger {
|
|
||||||
public:
|
|
||||||
SyslogLogger();
|
|
||||||
void init(Scheduler& scheduler);
|
|
||||||
void updateSettings(const String&& hostname);
|
|
||||||
void write(const uint8_t *buffer, size_t size);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void loop();
|
|
||||||
void disable();
|
|
||||||
void enable();
|
|
||||||
bool resolveAndStart();
|
|
||||||
bool isResolved() const {
|
|
||||||
return _address != INADDR_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
Task _loopTask;
|
|
||||||
std::mutex _mutex;
|
|
||||||
WiFiUDP _udp;
|
|
||||||
IPAddress _address;
|
|
||||||
String _syslog_hostname;
|
|
||||||
String _proc_id;
|
|
||||||
String _header;
|
|
||||||
uint16_t _port;
|
|
||||||
bool _enabled;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern SyslogLogger Syslog;
|
|
||||||
@ -2,8 +2,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
|
#include <LittleFS.h>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
class Utils {
|
class Utils {
|
||||||
public:
|
public:
|
||||||
@ -12,12 +12,6 @@ public:
|
|||||||
static int getTimezoneOffset();
|
static int getTimezoneOffset();
|
||||||
static bool checkJsonAlloc(const JsonDocument& doc, const char* function, const uint16_t line);
|
static bool checkJsonAlloc(const JsonDocument& doc, const char* function, const uint16_t line);
|
||||||
static void removeAllFiles();
|
static void removeAllFiles();
|
||||||
|
static String generateMd5FromFile(String file);
|
||||||
/* OpenDTU-OnBatter-specific utils go here: */
|
static void skipBom(File& f);
|
||||||
template<typename T>
|
|
||||||
static std::pair<T, String> getJsonValueByPath(JsonDocument const& root, String const& path);
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
static std::optional<T> getNumericValueFromMqttPayload(char const* client,
|
|
||||||
std::string const& src, char const* topic, char const* jsonPath);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,63 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "VeDirectMpptController.h"
|
|
||||||
#include "Configuration.h"
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
|
|
||||||
class VictronMpptClass {
|
|
||||||
public:
|
|
||||||
VictronMpptClass() = default;
|
|
||||||
~VictronMpptClass() = default;
|
|
||||||
|
|
||||||
void init(Scheduler& scheduler);
|
|
||||||
void updateSettings();
|
|
||||||
|
|
||||||
bool isDataValid() const;
|
|
||||||
bool isDataValid(size_t idx) const;
|
|
||||||
|
|
||||||
// returns the data age of all controllers,
|
|
||||||
// i.e, the youngest data's age is returned.
|
|
||||||
uint32_t getDataAgeMillis() const;
|
|
||||||
uint32_t getDataAgeMillis(size_t idx) const;
|
|
||||||
|
|
||||||
size_t controllerAmount() const { return _controllers.size(); }
|
|
||||||
std::optional<VeDirectMpptController::data_t> getData(size_t idx = 0) const;
|
|
||||||
|
|
||||||
// total output of all MPPT charge controllers in Watts
|
|
||||||
int32_t getPowerOutputWatts() const;
|
|
||||||
|
|
||||||
// total panel input power of all MPPT charge controllers in Watts
|
|
||||||
int32_t getPanelPowerWatts() const;
|
|
||||||
|
|
||||||
// sum of total yield of all MPPT charge controllers in kWh
|
|
||||||
float getYieldTotal() const;
|
|
||||||
|
|
||||||
// sum of today's yield of all MPPT charge controllers in kWh
|
|
||||||
float getYieldDay() const;
|
|
||||||
|
|
||||||
// minimum of all MPPT charge controllers' output voltages in V
|
|
||||||
float getOutputVoltage() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void loop();
|
|
||||||
VictronMpptClass(VictronMpptClass const& other) = delete;
|
|
||||||
VictronMpptClass(VictronMpptClass&& other) = delete;
|
|
||||||
VictronMpptClass& operator=(VictronMpptClass const& other) = delete;
|
|
||||||
VictronMpptClass& operator=(VictronMpptClass&& other) = delete;
|
|
||||||
|
|
||||||
Task _loopTask;
|
|
||||||
|
|
||||||
mutable std::mutex _mutex;
|
|
||||||
using controller_t = std::unique_ptr<VeDirectMpptController>;
|
|
||||||
std::vector<controller_t> _controllers;
|
|
||||||
|
|
||||||
std::vector<String> _serialPortOwners;
|
|
||||||
bool initController(int8_t rx, int8_t tx, bool logging,
|
|
||||||
uint8_t instance);
|
|
||||||
};
|
|
||||||
|
|
||||||
extern VictronMpptClass VictronMppt;
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Battery.h"
|
|
||||||
|
|
||||||
class VictronSmartShunt : public BatteryProvider {
|
|
||||||
public:
|
|
||||||
bool init(bool verboseLogging) final;
|
|
||||||
void deinit() final;
|
|
||||||
void loop() final;
|
|
||||||
std::shared_ptr<BatteryStats> getStats() const final { return _stats; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
static char constexpr _serialPortOwner[] = "SmartShunt";
|
|
||||||
|
|
||||||
uint32_t _lastUpdate = 0;
|
|
||||||
std::shared_ptr<VictronSmartShuntStats> _stats =
|
|
||||||
std::make_shared<VictronSmartShuntStats>();
|
|
||||||
};
|
|
||||||
@ -1,15 +1,15 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "WebApi_battery.h"
|
|
||||||
#include "WebApi_config.h"
|
|
||||||
#include "WebApi_device.h"
|
#include "WebApi_device.h"
|
||||||
#include "WebApi_devinfo.h"
|
#include "WebApi_devinfo.h"
|
||||||
#include "WebApi_dtu.h"
|
#include "WebApi_dtu.h"
|
||||||
#include "WebApi_errors.h"
|
#include "WebApi_errors.h"
|
||||||
#include "WebApi_eventlog.h"
|
#include "WebApi_eventlog.h"
|
||||||
|
#include "WebApi_file.h"
|
||||||
#include "WebApi_firmware.h"
|
#include "WebApi_firmware.h"
|
||||||
#include "WebApi_gridprofile.h"
|
#include "WebApi_gridprofile.h"
|
||||||
|
#include "WebApi_i18n.h"
|
||||||
#include "WebApi_inverter.h"
|
#include "WebApi_inverter.h"
|
||||||
#include "WebApi_limit.h"
|
#include "WebApi_limit.h"
|
||||||
#include "WebApi_maintenance.h"
|
#include "WebApi_maintenance.h"
|
||||||
@ -17,8 +17,6 @@
|
|||||||
#include "WebApi_network.h"
|
#include "WebApi_network.h"
|
||||||
#include "WebApi_ntp.h"
|
#include "WebApi_ntp.h"
|
||||||
#include "WebApi_power.h"
|
#include "WebApi_power.h"
|
||||||
#include "WebApi_powermeter.h"
|
|
||||||
#include "WebApi_powerlimiter.h"
|
|
||||||
#include "WebApi_prometheus.h"
|
#include "WebApi_prometheus.h"
|
||||||
#include "WebApi_security.h"
|
#include "WebApi_security.h"
|
||||||
#include "WebApi_sysstatus.h"
|
#include "WebApi_sysstatus.h"
|
||||||
@ -26,11 +24,6 @@
|
|||||||
#include "WebApi_ws_console.h"
|
#include "WebApi_ws_console.h"
|
||||||
#include "WebApi_ws_live.h"
|
#include "WebApi_ws_live.h"
|
||||||
#include <AsyncJson.h>
|
#include <AsyncJson.h>
|
||||||
#include "WebApi_ws_vedirect_live.h"
|
|
||||||
#include "WebApi_vedirect.h"
|
|
||||||
#include "WebApi_ws_Huawei.h"
|
|
||||||
#include "WebApi_Huawei.h"
|
|
||||||
#include "WebApi_ws_battery.h"
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
#include <TaskSchedulerDeclarations.h>
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
@ -54,14 +47,14 @@ public:
|
|||||||
private:
|
private:
|
||||||
AsyncWebServer _server;
|
AsyncWebServer _server;
|
||||||
|
|
||||||
WebApiBatteryClass _webApiBattery;
|
|
||||||
WebApiConfigClass _webApiConfig;
|
|
||||||
WebApiDeviceClass _webApiDevice;
|
WebApiDeviceClass _webApiDevice;
|
||||||
WebApiDevInfoClass _webApiDevInfo;
|
WebApiDevInfoClass _webApiDevInfo;
|
||||||
WebApiDtuClass _webApiDtu;
|
WebApiDtuClass _webApiDtu;
|
||||||
WebApiEventlogClass _webApiEventlog;
|
WebApiEventlogClass _webApiEventlog;
|
||||||
|
WebApiFileClass _webApiFile;
|
||||||
WebApiFirmwareClass _webApiFirmware;
|
WebApiFirmwareClass _webApiFirmware;
|
||||||
WebApiGridProfileClass _webApiGridprofile;
|
WebApiGridProfileClass _webApiGridprofile;
|
||||||
|
WebApiI18nClass _webApiI18n;
|
||||||
WebApiInverterClass _webApiInverter;
|
WebApiInverterClass _webApiInverter;
|
||||||
WebApiLimitClass _webApiLimit;
|
WebApiLimitClass _webApiLimit;
|
||||||
WebApiMaintenanceClass _webApiMaintenance;
|
WebApiMaintenanceClass _webApiMaintenance;
|
||||||
@ -69,19 +62,12 @@ private:
|
|||||||
WebApiNetworkClass _webApiNetwork;
|
WebApiNetworkClass _webApiNetwork;
|
||||||
WebApiNtpClass _webApiNtp;
|
WebApiNtpClass _webApiNtp;
|
||||||
WebApiPowerClass _webApiPower;
|
WebApiPowerClass _webApiPower;
|
||||||
WebApiPowerMeterClass _webApiPowerMeter;
|
|
||||||
WebApiPowerLimiterClass _webApiPowerLimiter;
|
|
||||||
WebApiPrometheusClass _webApiPrometheus;
|
WebApiPrometheusClass _webApiPrometheus;
|
||||||
WebApiSecurityClass _webApiSecurity;
|
WebApiSecurityClass _webApiSecurity;
|
||||||
WebApiSysstatusClass _webApiSysstatus;
|
WebApiSysstatusClass _webApiSysstatus;
|
||||||
WebApiWebappClass _webApiWebapp;
|
WebApiWebappClass _webApiWebapp;
|
||||||
WebApiWsConsoleClass _webApiWsConsole;
|
WebApiWsConsoleClass _webApiWsConsole;
|
||||||
WebApiWsLiveClass _webApiWsLive;
|
WebApiWsLiveClass _webApiWsLive;
|
||||||
WebApiWsVedirectLiveClass _webApiWsVedirectLive;
|
|
||||||
WebApiVedirectClass _webApiVedirect;
|
|
||||||
WebApiHuaweiClass _webApiHuaweiClass;
|
|
||||||
WebApiWsHuaweiLiveClass _webApiWsHuaweiLive;
|
|
||||||
WebApiWsBatteryLiveClass _webApiWsBatteryLive;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern WebApiClass WebApi;
|
extern WebApiClass WebApi;
|
||||||
|
|||||||
@ -1,19 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
#include <AsyncJson.h>
|
|
||||||
|
|
||||||
class WebApiHuaweiClass {
|
|
||||||
public:
|
|
||||||
void init(AsyncWebServer& server, Scheduler& scheduler);
|
|
||||||
void getJsonData(JsonVariant& root);
|
|
||||||
private:
|
|
||||||
void onStatus(AsyncWebServerRequest* request);
|
|
||||||
void onAdminGet(AsyncWebServerRequest* request);
|
|
||||||
void onAdminPost(AsyncWebServerRequest* request);
|
|
||||||
void onPost(AsyncWebServerRequest* request);
|
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
|
|
||||||
class WebApiBatteryClass {
|
|
||||||
public:
|
|
||||||
void init(AsyncWebServer& server, Scheduler& scheduler);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void onStatus(AsyncWebServerRequest* request);
|
|
||||||
void onAdminGet(AsyncWebServerRequest* request);
|
|
||||||
void onAdminPost(AsyncWebServerRequest* request);
|
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
|
||||||
#include <TaskSchedulerDeclarations.h>
|
|
||||||
|
|
||||||
class WebApiConfigClass {
|
|
||||||
public:
|
|
||||||
void init(AsyncWebServer& server, Scheduler& scheduler);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void onConfigGet(AsyncWebServerRequest* request);
|
|
||||||
void onConfigDelete(AsyncWebServerRequest* request);
|
|
||||||
void onConfigListGet(AsyncWebServerRequest* request);
|
|
||||||
void onConfigUploadFinish(AsyncWebServerRequest* request);
|
|
||||||
void onConfigUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final);
|
|
||||||
};
|
|
||||||