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:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### ⚠️ 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.
|
||||
value: >
|
||||
### ⚠️ Please remember: issues are for *bugs*
|
||||
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
|
||||
attributes:
|
||||
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:
|
||||
|
||||
- [Documentation](https://opendtu-onbattery.net)
|
||||
- [The FAQs](https://opendtu-onbattery.net/firmware/faq/)
|
||||
- [Existing issues and discussions](https://github.com/helgeerbe/OpenDTU-OnBattery/search?q=&type=issues)
|
||||
- [Documentation](https://www.opendtu.solar).
|
||||
- [The FAQs](https://www.opendtu.solar/firmware/faq/).
|
||||
- [Existing issues and discussions](https://github.com/tbnobody/OpenDTU/search?q=&type=issues).
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
@ -45,7 +45,7 @@ body:
|
||||
id: install_format
|
||||
attributes:
|
||||
label: Install Method
|
||||
description: How did you install OpenDTU-OnBattery?
|
||||
description: How did you install OpenDTU?
|
||||
options:
|
||||
- Pre-Compiled binary from GitHub releases
|
||||
- Pre-Compiled binary from GitHub actions/pull-request
|
||||
@ -55,25 +55,17 @@ body:
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: What git-hash/version of OpenDTU-OnBattery?
|
||||
description: You can find this in the Web UI at Info -> System.
|
||||
label: What git-hash/version of OpenDTU?
|
||||
description: You can find this in by going to Info -> System
|
||||
placeholder: "e.g. 359d513"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
- type: input
|
||||
id: environment
|
||||
attributes:
|
||||
label: What firmware variant (PIO Environment)?
|
||||
description: You can find this in the Web UI at Info -> System.
|
||||
options:
|
||||
- "generic_esp32s3_usb"
|
||||
- "generic_esp32s3"
|
||||
- "generic_esp32_8mb"
|
||||
- "generic_esp32_4mb_no_ota"
|
||||
- "generic_esp32"
|
||||
- "generic"
|
||||
- "opendtufusionv2"
|
||||
- "other (tell us in 'Anything else?')"
|
||||
label: What firmware variant (PIO Environment) are you using?
|
||||
description: You can find this in by going to Info -> System
|
||||
placeholder: "generic_esp32s3_usb"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
@ -95,7 +87,7 @@ body:
|
||||
attributes:
|
||||
label: Please confirm the following
|
||||
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
|
||||
- label: I have already searched for relevant existing issues and discussions before opening this report.
|
||||
required: true
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -4,5 +4,5 @@ contact_links:
|
||||
url: https://discord.gg/WzhxEY62mB
|
||||
about: Discuss with us on Discord
|
||||
- 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
|
||||
4
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@ -1,12 +1,12 @@
|
||||
name: ✨ Request a feature
|
||||
description: Suggest an improvement idea for OpenDTU-OnBattery!
|
||||
description: Suggest an improvement idea for OpenDTU!
|
||||
title: "[Request]"
|
||||
labels: ["enhancement"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
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
|
||||
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:
|
||||
push:
|
||||
paths-ignore:
|
||||
- docs/**
|
||||
- '**/*.md'
|
||||
branches:
|
||||
- master
|
||||
- development
|
||||
tags-ignore:
|
||||
- 'v**'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- docs/**
|
||||
@ -60,15 +55,6 @@ jobs:
|
||||
- name: Get tags
|
||||
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
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
@ -119,48 +105,27 @@ jobs:
|
||||
run: pio run -e ${{ matrix.environment }}
|
||||
|
||||
- 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
|
||||
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
|
||||
with:
|
||||
name: opendtu-onbattery-${{ matrix.environment }}
|
||||
name: opendtu-${{ matrix.environment }}
|
||||
path: |
|
||||
.pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.bin
|
||||
!.pio/build/generic_esp32_4mb_no_ota/opendtu-onbattery-generic_esp32_4mb_no_ota.bin
|
||||
.pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.factory.bin
|
||||
.pio/build/${{ matrix.environment }}/opendtu-${{ matrix.environment }}.bin
|
||||
.pio/build/${{ matrix.environment }}/opendtu-${{ matrix.environment }}.factory.bin
|
||||
|
||||
release:
|
||||
name: Create Release
|
||||
runs-on: ubuntu-latest
|
||||
needs: [get_default_envs, build]
|
||||
if: startsWith(github.ref, 'refs/tags/2')
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
steps:
|
||||
- name: Checkout
|
||||
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
|
||||
id: github_release
|
||||
uses: mikepenz/release-changelog-builder-action@v4
|
||||
@ -179,7 +144,7 @@ jobs:
|
||||
run: |
|
||||
ls -R
|
||||
cd artifacts
|
||||
for i in */; do cp ${i}opendtu-onbattery-*.bin ./; done
|
||||
for i in */; do cp ${i}opendtu-*.bin ./; done
|
||||
|
||||
- name: Create release
|
||||
uses: softprops/action-gh-release@v2
|
||||
@ -189,4 +154,4 @@ jobs:
|
||||
files: |
|
||||
artifacts/*.zip, artifacts/*.bin
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
}
|
||||
],
|
||||
"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",
|
||||
"label_extractor": [
|
||||
{
|
||||
|
||||
10
.github/workflows/cpplint.yml
vendored
@ -6,10 +6,6 @@ jobs:
|
||||
build:
|
||||
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:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python
|
||||
@ -19,9 +15,7 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install cpplint==1.6.1
|
||||
pip install cpplint
|
||||
- name: Linting
|
||||
run: |
|
||||
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
|
||||
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
|
||||
|
||||
4
.github/workflows/yarnlint.yml
vendored
@ -6,10 +6,6 @@ jobs:
|
||||
build:
|
||||
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:
|
||||
run:
|
||||
working-directory: webapp
|
||||
|
||||
4
.github/workflows/yarnprettier.yml
vendored
@ -6,10 +6,6 @@ jobs:
|
||||
build:
|
||||
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:
|
||||
run:
|
||||
working-directory: webapp
|
||||
|
||||
1
.gitignore
vendored
@ -7,5 +7,4 @@
|
||||
platformio-device-monitor*.log
|
||||
logs/device-monitor*.log
|
||||
platformio_override.ini
|
||||
webapp_dist/
|
||||
.DS_Store
|
||||
|
||||
62
.vscode/settings.json
vendored
@ -1,63 +1,3 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
"C_Cpp.clang_format_style": "WebKit"
|
||||
}
|
||||
88
README.md
@ -1,71 +1,43 @@
|
||||
- [OpenDTU-OnBattery](#opendtu-onbattery)
|
||||
- [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
|
||||
|
||||
# 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 !!
|
||||
|
||||
<!---
|
||||
disabled while "create release badge" action is broken, see .github/build.yml
|
||||

|
||||
--->
|
||||
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!
|
||||
|
||||
[](https://github.com/hoylabs/OpenDTU-OnBattery/actions/workflows/build.yml)
|
||||
[](https://github.com/hoylabs/OpenDTU-OnBattery/actions/workflows/cpplint.yml)
|
||||
[](https://github.com/hoylabs/OpenDTU-OnBattery/actions/workflows/yarnlint.yml)
|
||||
## Background
|
||||
|
||||
## What is OpenDTU-OnBattery
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
## Documentation
|
||||
|
||||
The canonical documentation of OpenDTU-OnBattery is hosted at
|
||||
[https://opendtu-onbattery.net](https://opendtu-onbattery.net).
|
||||
The documentation can be found [here](https://tbnobody.github.io/OpenDTU-docs/).
|
||||
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
|
||||
community-maintained [Github
|
||||
Wiki](https://github.com/hoylabs/OpenDTU-OnBattery/wiki).
|
||||
## Breaking changes
|
||||
|
||||
To find out what's new or improved have a look at the changelog of the
|
||||
[releases](https://github.com/hoylabs/OpenDTU-OnBattery/releases).
|
||||
Generated using: `git log --date=short --pretty=format:"* %h%x09%ad%x09%s" | grep BREAKING`
|
||||
|
||||
## 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
|
||||
may change significantly during its development. Bug reports, comments, feature
|
||||
requests and pull requests are welcome!
|
||||
## Currently supported Inverters
|
||||
|
||||
## History of the project
|
||||
|
||||
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.
|
||||
A list of all currently supported inverters can be found [here](https://www.opendtu.solar/hardware/inverter_overview/)
|
||||
|
||||
@ -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 <cstdint>
|
||||
#include <ArduinoJson.h>
|
||||
#include <TaskSchedulerDeclarations.h>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
#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_PASSWORD_STRLEN 64
|
||||
#define WIFI_MAX_HOSTNAME_STRLEN 31
|
||||
|
||||
#define SYSLOG_MAX_HOSTNAME_STRLEN 128
|
||||
|
||||
#define NTP_MAX_SERVER_STRLEN 31
|
||||
#define NTP_MAX_TIMEZONE_STRLEN 50
|
||||
#define NTP_MAX_TIMEZONEDESCR_STRLEN 50
|
||||
@ -22,7 +22,7 @@
|
||||
#define MQTT_MAX_CLIENTID_STRLEN 64
|
||||
#define MQTT_MAX_USERNAME_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_CERT_STRLEN 2560
|
||||
|
||||
@ -33,17 +33,7 @@
|
||||
#define CHAN_MAX_NAME_STRLEN 31
|
||||
|
||||
#define DEV_MAX_MAPPING_NAME_STRLEN 63
|
||||
|
||||
#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
|
||||
#define LOCALE_STRLEN 2
|
||||
|
||||
struct CHANNEL_CONFIG_T {
|
||||
uint16_t MaxChannelPower;
|
||||
@ -67,91 +57,6 @@ struct INVERTER_CONFIG_T {
|
||||
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 {
|
||||
uint32_t Version;
|
||||
@ -175,12 +80,6 @@ struct CONFIG_T {
|
||||
bool Enabled;
|
||||
} Mdns;
|
||||
|
||||
struct {
|
||||
bool Enabled;
|
||||
char Hostname[SYSLOG_MAX_HOSTNAME_STRLEN + 1];
|
||||
uint16_t Port;
|
||||
} Syslog;
|
||||
|
||||
struct {
|
||||
char Server[NTP_MAX_SERVER_STRLEN + 1];
|
||||
char Timezone[NTP_MAX_TIMEZONE_STRLEN + 1];
|
||||
@ -193,7 +92,6 @@ struct CONFIG_T {
|
||||
struct {
|
||||
bool Enabled;
|
||||
char Hostname[MQTT_MAX_HOSTNAME_STRLEN + 1];
|
||||
bool VerboseLogging;
|
||||
uint32_t Port;
|
||||
char ClientId[MQTT_MAX_CLIENTID_STRLEN + 1];
|
||||
char Username[MQTT_MAX_USERNAME_STRLEN + 1];
|
||||
@ -238,7 +136,6 @@ struct CONFIG_T {
|
||||
uint32_t Frequency;
|
||||
uint8_t CountryMode;
|
||||
} Cmt;
|
||||
bool VerboseLogging;
|
||||
} Dtu;
|
||||
|
||||
struct {
|
||||
@ -251,7 +148,7 @@ struct CONFIG_T {
|
||||
bool ScreenSaver;
|
||||
uint8_t Rotation;
|
||||
uint8_t Contrast;
|
||||
uint8_t Language;
|
||||
char Locale[LOCALE_STRLEN + 1];
|
||||
struct {
|
||||
uint32_t Duration;
|
||||
uint8_t Mode;
|
||||
@ -262,98 +159,38 @@ struct CONFIG_T {
|
||||
uint8_t Brightness;
|
||||
} 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];
|
||||
char Dev_PinMapping[DEV_MAX_MAPPING_NAME_STRLEN + 1];
|
||||
};
|
||||
|
||||
class ConfigurationClass {
|
||||
public:
|
||||
void init();
|
||||
void init(Scheduler& scheduler);
|
||||
bool read();
|
||||
bool write();
|
||||
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* getInverterConfig(const uint64_t serial);
|
||||
void deleteInverterById(const uint8_t id);
|
||||
|
||||
static void serializeHttpRequestConfig(HttpRequestConfig const& source, JsonObject& target);
|
||||
static void serializePowerMeterMqttConfig(PowerMeterMqttConfig const& source, JsonObject& target);
|
||||
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);
|
||||
private:
|
||||
void loop();
|
||||
|
||||
static void deserializeHttpRequestConfig(JsonObject const& source, HttpRequestConfig& target);
|
||||
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);
|
||||
Task _loopTask;
|
||||
};
|
||||
|
||||
extern ConfigurationClass Configuration;
|
||||
|
||||
@ -40,7 +40,7 @@ public:
|
||||
void setContrast(const uint8_t contrast);
|
||||
void setStatus(const bool turnOn);
|
||||
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 setStartupDisplay();
|
||||
|
||||
@ -65,7 +65,7 @@ private:
|
||||
|
||||
DisplayType_t _display_type = DisplayType_t::None;
|
||||
DiagramMode_t _diagram_mode = DiagramMode_t::Off;
|
||||
uint8_t _display_language = DISPLAY_LANGUAGE;
|
||||
String _display_language = DISPLAY_LOCALE;
|
||||
uint8_t _mExtra;
|
||||
const uint16_t _period = 1000;
|
||||
const uint16_t _interval = 60000; // interval at which to power save (milliseconds)
|
||||
@ -73,6 +73,15 @@ private:
|
||||
char _fmtText[32];
|
||||
bool _isLarge = false;
|
||||
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;
|
||||
|
||||
@ -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
|
||||
|
||||
#include <AsyncWebSocket.h>
|
||||
#include <HardwareSerial.h>
|
||||
#include <Stream.h>
|
||||
#include <TaskSchedulerDeclarations.h>
|
||||
#include <Print.h>
|
||||
#include <freertos/task.h>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <queue>
|
||||
|
||||
#define BUFFER_SIZE 500
|
||||
|
||||
class MessageOutputClass : public Print {
|
||||
public:
|
||||
@ -23,19 +22,13 @@ private:
|
||||
|
||||
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;
|
||||
char _buffer[BUFFER_SIZE];
|
||||
uint16_t _buff_pos = 0;
|
||||
uint32_t _lastSend = 0;
|
||||
bool _forceSend = false;
|
||||
|
||||
std::mutex _msgLock;
|
||||
|
||||
void serialWrite(message_t const& m);
|
||||
};
|
||||
|
||||
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 forceUpdate();
|
||||
|
||||
static String getDtuUniqueId();
|
||||
static String getDtuUrl();
|
||||
|
||||
private:
|
||||
void loop();
|
||||
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 String getDtuUniqueId();
|
||||
static String getDtuUrl();
|
||||
|
||||
Task _loopTask;
|
||||
|
||||
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:
|
||||
MqttSettingsClass();
|
||||
void init();
|
||||
void loop();
|
||||
void performReconnect();
|
||||
bool getConnected();
|
||||
void publish(const String& subtopic, const String& payload);
|
||||
@ -21,7 +20,7 @@ public:
|
||||
void unsubscribe(const String& topic);
|
||||
|
||||
String getPrefix() const;
|
||||
String getClientId();
|
||||
String getClientId() const;
|
||||
|
||||
private:
|
||||
void NetworkEvent(network_event event);
|
||||
@ -39,7 +38,6 @@ private:
|
||||
Ticker _mqttReconnectTimer;
|
||||
MqttSubscribeParser _mqttSubscribeParser;
|
||||
std::mutex _clientLock;
|
||||
bool _verboseLogging = true;
|
||||
};
|
||||
|
||||
extern MqttSettingsClass MqttSettings;
|
||||
|
||||
@ -51,29 +51,6 @@ struct PinMapping_t {
|
||||
uint8_t display_reset;
|
||||
|
||||
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 {
|
||||
@ -82,16 +59,19 @@ public:
|
||||
bool init(const String& deviceMapping);
|
||||
PinMapping_t& get();
|
||||
|
||||
bool isMappingSelected() const { return _mappingSelected; }
|
||||
|
||||
bool isValidNrf24Config() const;
|
||||
bool isValidCmt2300Config() const;
|
||||
bool isValidW5500Config() const;
|
||||
#if CONFIG_ETH_USE_ESP32_EMAC
|
||||
bool isValidEthConfig() const;
|
||||
#endif
|
||||
bool isValidHuaweiConfig() const;
|
||||
|
||||
private:
|
||||
PinMapping_t _pinMapping;
|
||||
|
||||
bool _mappingSelected = false;
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <LittleFS.h>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
class Utils {
|
||||
public:
|
||||
@ -12,12 +12,6 @@ public:
|
||||
static int getTimezoneOffset();
|
||||
static bool checkJsonAlloc(const JsonDocument& doc, const char* function, const uint16_t line);
|
||||
static void removeAllFiles();
|
||||
|
||||
/* OpenDTU-OnBatter-specific utils go here: */
|
||||
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);
|
||||
static String generateMd5FromFile(String file);
|
||||
static void skipBom(File& f);
|
||||
};
|
||||
|
||||
@ -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
|
||||
#pragma once
|
||||
|
||||
#include "WebApi_battery.h"
|
||||
#include "WebApi_config.h"
|
||||
#include "WebApi_device.h"
|
||||
#include "WebApi_devinfo.h"
|
||||
#include "WebApi_dtu.h"
|
||||
#include "WebApi_errors.h"
|
||||
#include "WebApi_eventlog.h"
|
||||
#include "WebApi_file.h"
|
||||
#include "WebApi_firmware.h"
|
||||
#include "WebApi_gridprofile.h"
|
||||
#include "WebApi_i18n.h"
|
||||
#include "WebApi_inverter.h"
|
||||
#include "WebApi_limit.h"
|
||||
#include "WebApi_maintenance.h"
|
||||
@ -17,8 +17,6 @@
|
||||
#include "WebApi_network.h"
|
||||
#include "WebApi_ntp.h"
|
||||
#include "WebApi_power.h"
|
||||
#include "WebApi_powermeter.h"
|
||||
#include "WebApi_powerlimiter.h"
|
||||
#include "WebApi_prometheus.h"
|
||||
#include "WebApi_security.h"
|
||||
#include "WebApi_sysstatus.h"
|
||||
@ -26,11 +24,6 @@
|
||||
#include "WebApi_ws_console.h"
|
||||
#include "WebApi_ws_live.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 <TaskSchedulerDeclarations.h>
|
||||
|
||||
@ -54,14 +47,14 @@ public:
|
||||
private:
|
||||
AsyncWebServer _server;
|
||||
|
||||
WebApiBatteryClass _webApiBattery;
|
||||
WebApiConfigClass _webApiConfig;
|
||||
WebApiDeviceClass _webApiDevice;
|
||||
WebApiDevInfoClass _webApiDevInfo;
|
||||
WebApiDtuClass _webApiDtu;
|
||||
WebApiEventlogClass _webApiEventlog;
|
||||
WebApiFileClass _webApiFile;
|
||||
WebApiFirmwareClass _webApiFirmware;
|
||||
WebApiGridProfileClass _webApiGridprofile;
|
||||
WebApiI18nClass _webApiI18n;
|
||||
WebApiInverterClass _webApiInverter;
|
||||
WebApiLimitClass _webApiLimit;
|
||||
WebApiMaintenanceClass _webApiMaintenance;
|
||||
@ -69,19 +62,12 @@ private:
|
||||
WebApiNetworkClass _webApiNetwork;
|
||||
WebApiNtpClass _webApiNtp;
|
||||
WebApiPowerClass _webApiPower;
|
||||
WebApiPowerMeterClass _webApiPowerMeter;
|
||||
WebApiPowerLimiterClass _webApiPowerLimiter;
|
||||
WebApiPrometheusClass _webApiPrometheus;
|
||||
WebApiSecurityClass _webApiSecurity;
|
||||
WebApiSysstatusClass _webApiSysstatus;
|
||||
WebApiWebappClass _webApiWebapp;
|
||||
WebApiWsConsoleClass _webApiWsConsole;
|
||||
WebApiWsLiveClass _webApiWsLive;
|
||||
WebApiWsVedirectLiveClass _webApiWsVedirectLive;
|
||||
WebApiVedirectClass _webApiVedirect;
|
||||
WebApiHuaweiClass _webApiHuaweiClass;
|
||||
WebApiWsHuaweiLiveClass _webApiWsHuaweiLive;
|
||||
WebApiWsBatteryLiveClass _webApiWsBatteryLive;
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||