Merge branch 'master' into patch-1
This commit is contained in:
commit
60e0950273
9
.editorconfig
Normal file
9
.editorconfig
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# http://editorconfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
trim_trailing_whitespace = true
|
||||||
37
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
37
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -5,11 +5,18 @@ body:
|
|||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: >
|
value: >
|
||||||
### ✋ **This is bug tracker, not a support forum**
|
### ⚠️ 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/tbnobody/OpenDTU/discussions/new) or [ask in chat](https://discord.gg/WzhxEY62mB).
|
||||||
|
|
||||||
If something isn't working right, you have questions or need help, [**get in touch on the Discussions**](https://github.com/tbnobody/OpenDTU/discussions).
|
#### Before opening an issue, please double check:
|
||||||
|
|
||||||
Please quickly search existing issues first before submitting a bug.
|
- [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
|
- type: textarea
|
||||||
id: what-happened
|
id: what-happened
|
||||||
attributes:
|
attributes:
|
||||||
@ -40,7 +47,8 @@ body:
|
|||||||
label: Install Method
|
label: Install Method
|
||||||
description: How did you install OpenDTU?
|
description: How did you install OpenDTU?
|
||||||
options:
|
options:
|
||||||
- Pre-Compiled binary from GitHub
|
- Pre-Compiled binary from GitHub releases
|
||||||
|
- Pre-Compiled binary from GitHub actions/pull-request
|
||||||
- Self-Compiled
|
- Self-Compiled
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
@ -52,6 +60,14 @@ body:
|
|||||||
placeholder: "e.g. 359d513"
|
placeholder: "e.g. 359d513"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: environment
|
||||||
|
attributes:
|
||||||
|
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
|
- type: textarea
|
||||||
id: logs
|
id: logs
|
||||||
attributes:
|
attributes:
|
||||||
@ -66,3 +82,16 @@ body:
|
|||||||
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
|
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
|
- type: checkboxes
|
||||||
|
id: required-checks
|
||||||
|
attributes:
|
||||||
|
label: Please confirm the following
|
||||||
|
options:
|
||||||
|
- 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
|
||||||
|
- label: I have updated the title field above with a concise description.
|
||||||
|
required: true
|
||||||
|
- label: I have double checked that my inverter does not contain a W in the model name (like HMS-xxxW) as they are not supported.
|
||||||
|
required: true
|
||||||
|
|||||||
45
.github/workflows/build.yml
vendored
45
.github/workflows/build.yml
vendored
@ -15,17 +15,17 @@ jobs:
|
|||||||
name: Gather Environments
|
name: Gather Environments
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Cache pip
|
- name: Cache pip
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/pip
|
path: ~/.cache/pip
|
||||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-pip-
|
${{ runner.os }}-pip-
|
||||||
|
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
|
|
||||||
@ -43,20 +43,20 @@ jobs:
|
|||||||
environments: ${{ steps.envs.outputs.environments }}
|
environments: ${{ steps.envs.outputs.environments }}
|
||||||
|
|
||||||
build:
|
build:
|
||||||
name: Build Enviornments
|
name: Build Environments
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: get_default_envs
|
needs: get_default_envs
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }}
|
environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Get tags
|
- name: Get tags
|
||||||
run: git fetch --force --tags origin
|
run: git fetch --force --tags origin
|
||||||
|
|
||||||
- name: Cache pip
|
- name: Cache pip
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/pip
|
path: ~/.cache/pip
|
||||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||||
@ -64,33 +64,42 @@ jobs:
|
|||||||
${{ runner.os }}-pip-
|
${{ runner.os }}-pip-
|
||||||
|
|
||||||
- name: Cache PlatformIO
|
- name: Cache PlatformIO
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ~/.platformio
|
path: ~/.platformio
|
||||||
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
|
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
|
|
||||||
- name: Install PlatformIO
|
- name: Install PlatformIO
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install --upgrade platformio
|
pip install --upgrade platformio setuptools
|
||||||
|
|
||||||
|
- name: Enable Corepack
|
||||||
|
run: |
|
||||||
|
cd webapp
|
||||||
|
corepack enable
|
||||||
|
|
||||||
- name: Setup Node.js and yarn
|
- name: Setup Node.js and yarn
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: "18"
|
node-version: "22"
|
||||||
cache: "yarn"
|
cache: "yarn"
|
||||||
cache-dependency-path: "webapp/yarn.lock"
|
cache-dependency-path: "webapp/yarn.lock"
|
||||||
|
|
||||||
- name: Install WebApp dependencies
|
- name: Install WebApp dependencies
|
||||||
run: yarn --cwd webapp install --frozen-lockfile
|
run: |
|
||||||
|
cd webapp
|
||||||
|
yarn install --frozen-lockfile
|
||||||
|
|
||||||
- name: Build WebApp
|
- name: Build WebApp
|
||||||
run: yarn --cwd webapp build
|
run: |
|
||||||
|
cd webapp
|
||||||
|
yarn build
|
||||||
|
|
||||||
- name: Build firmware
|
- name: Build firmware
|
||||||
run: pio run -e ${{ matrix.environment }}
|
run: pio run -e ${{ matrix.environment }}
|
||||||
@ -101,7 +110,7 @@ jobs:
|
|||||||
- name: Rename Factory Firmware
|
- name: Rename Factory Firmware
|
||||||
run: mv .pio/build/${{ matrix.environment }}/firmware.factory.bin .pio/build/${{ matrix.environment }}/opendtu-${{ 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@v3
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: opendtu-${{ matrix.environment }}
|
name: opendtu-${{ matrix.environment }}
|
||||||
path: |
|
path: |
|
||||||
@ -115,11 +124,11 @@ jobs:
|
|||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Build Changelog
|
- name: Build Changelog
|
||||||
id: github_release
|
id: github_release
|
||||||
uses: mikepenz/release-changelog-builder-action@v3
|
uses: mikepenz/release-changelog-builder-action@v4
|
||||||
with:
|
with:
|
||||||
failOnError: true
|
failOnError: true
|
||||||
commitMode: true
|
commitMode: true
|
||||||
@ -127,7 +136,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
path: artifacts/
|
path: artifacts/
|
||||||
|
|
||||||
@ -138,7 +147,7 @@ jobs:
|
|||||||
for i in */; do cp ${i}opendtu-*.bin ./; done
|
for i in */; do cp ${i}opendtu-*.bin ./; done
|
||||||
|
|
||||||
- name: Create release
|
- name: Create release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
body: ${{steps.github_release.outputs.changelog}}
|
body: ${{steps.github_release.outputs.changelog}}
|
||||||
draft: False
|
draft: False
|
||||||
|
|||||||
@ -18,6 +18,12 @@
|
|||||||
"fix"
|
"fix"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "## 🌎 Web Application",
|
||||||
|
"labels": [
|
||||||
|
"webapp"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "## 📚 Documentation",
|
"title": "## 📚 Documentation",
|
||||||
"labels": [
|
"labels": [
|
||||||
|
|||||||
4
.github/workflows/cpplint.yml
vendored
4
.github/workflows/cpplint.yml
vendored
@ -7,9 +7,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
|
|||||||
54
.github/workflows/repo-maintenance.yml
vendored
Normal file
54
.github/workflows/repo-maintenance.yml
vendored
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
name: 'Repository Maintenance'
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 4 * * *'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
discussions: write
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: lock
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
stale:
|
||||||
|
name: 'Stale'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/stale@v9
|
||||||
|
with:
|
||||||
|
days-before-stale: 14
|
||||||
|
days-before-close: 60
|
||||||
|
any-of-labels: 'cant-reproduce,not a bug'
|
||||||
|
stale-issue-label: stale
|
||||||
|
stale-pr-label: stale
|
||||||
|
stale-issue-message: >
|
||||||
|
This issue has been automatically marked as stale because it has not had
|
||||||
|
recent activity. It will be closed if no further activity occurs. Thank you
|
||||||
|
for your contributions.
|
||||||
|
|
||||||
|
lock-threads:
|
||||||
|
name: 'Lock Old Threads'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: dessant/lock-threads@v5
|
||||||
|
with:
|
||||||
|
issue-inactive-days: '30'
|
||||||
|
pr-inactive-days: '30'
|
||||||
|
discussion-inactive-days: '30'
|
||||||
|
log-output: true
|
||||||
|
issue-comment: >
|
||||||
|
This issue has been automatically locked since there
|
||||||
|
has not been any recent activity after it was closed.
|
||||||
|
Please open a new discussion or issue for related concerns.
|
||||||
|
pr-comment: >
|
||||||
|
This pull request has been automatically locked since there
|
||||||
|
has not been any recent activity after it was closed.
|
||||||
|
Please open a new discussion or issue for related concerns.
|
||||||
|
discussion-comment: >
|
||||||
|
This discussion has been automatically locked since there
|
||||||
|
has not been any recent activity after it was closed.
|
||||||
|
Please open a new discussion for related concerns.
|
||||||
16
.github/workflows/yarnlint.yml
vendored
16
.github/workflows/yarnlint.yml
vendored
@ -6,17 +6,23 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: webapp
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
- name: Enable Corepack
|
||||||
|
run: corepack enable
|
||||||
- name: Setup Node.js and yarn
|
- name: Setup Node.js and yarn
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: "18"
|
node-version: "22"
|
||||||
cache: "yarn"
|
cache: "yarn"
|
||||||
cache-dependency-path: "webapp/yarn.lock"
|
cache-dependency-path: "webapp/yarn.lock"
|
||||||
|
|
||||||
- name: Install WebApp dependencies
|
- name: Install WebApp dependencies
|
||||||
run: yarn --cwd webapp install --frozen-lockfile
|
run: yarn install --frozen-lockfile
|
||||||
|
|
||||||
- name: Linting
|
- name: Linting
|
||||||
run: yarn --cwd webapp lint
|
run: yarn lint
|
||||||
|
|||||||
28
.github/workflows/yarnprettier.yml
vendored
Normal file
28
.github/workflows/yarnprettier.yml
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
name: Yarn Prettier
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: webapp
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Enable Corepack
|
||||||
|
run: corepack enable
|
||||||
|
- name: Setup Node.js and yarn
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "22"
|
||||||
|
cache: "yarn"
|
||||||
|
cache-dependency-path: "webapp/yarn.lock"
|
||||||
|
|
||||||
|
- name: Install WebApp dependencies
|
||||||
|
run: yarn install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Check Formatting
|
||||||
|
run: yarn prettier --check src/
|
||||||
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@ -3,8 +3,8 @@
|
|||||||
// for the documentation about the extensions.json format
|
// for the documentation about the extensions.json format
|
||||||
"recommendations": [
|
"recommendations": [
|
||||||
"DavidAnson.vscode-markdownlint",
|
"DavidAnson.vscode-markdownlint",
|
||||||
|
"EditorConfig.EditorConfig",
|
||||||
"Vue.volar",
|
"Vue.volar",
|
||||||
"Vue.vscode-typescript-vue-plugin",
|
|
||||||
"platformio.platformio-ide"
|
"platformio.platformio-ide"
|
||||||
],
|
],
|
||||||
"unwantedRecommendations": [
|
"unwantedRecommendations": [
|
||||||
|
|||||||
313
README.md
313
README.md
@ -13,91 +13,22 @@ If you are upgrading from a version before 15.03.2023 you have to upgrade the pa
|
|||||||
This project was started from [this](https://www.mikrocontroller.net/topic/525778) discussion (Mikrocontroller.net).
|
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.
|
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.
|
||||||
|
|
||||||
## Screenshots
|
## Documentation
|
||||||
|
|
||||||
Several screenshots of the frontend can be found here: [Screenshots](docs/screenshots/README.md)
|
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.
|
||||||
## Builds
|
|
||||||
|
|
||||||
Different builds from existing installations can be found here [Builds](docs/builds/README.md)
|
|
||||||
Like to show your own build? Just send me a Pull Request.
|
|
||||||
|
|
||||||
## Currently supported Inverters
|
|
||||||
|
|
||||||
| Model | Required RF Module | DC Inputs | MPP-Tracker | AC Phases |
|
|
||||||
| --------------------| ------------------ | --------- | ----------- | --------- |
|
|
||||||
| Hoymiles HM-300 | NRF24L01+ | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HM-350 | NRF24L01+ | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HM-400 | NRF24L01+ | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HM-600 | NRF24L01+ | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HM-700 | NRF24L01+ | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HM-800 | NRF24L01+ | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HM-1000 | NRF24L01+ | 4 | 2 | 1 |
|
|
||||||
| Hoymiles HM-1200 | NRF24L01+ | 4 | 2 | 1 |
|
|
||||||
| Hoymiles HM-1500 | NRF24L01+ | 4 | 2 | 1 |
|
|
||||||
| Hoymiles HMS-300 | CMT2300A | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HMS-350 | CMT2300A | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HMS-400 | CMT2300A | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HMS-450 | CMT2300A | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HMS-500 | CMT2300A | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HMS-600 | CMT2300A | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HMS-700 | CMT2300A | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HMS-800 | CMT2300A | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HMS-900 | CMT2300A | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HMS-1000 | CMT2300A | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HMS-1600 | CMT2300A | 4 | 4 | 1 |
|
|
||||||
| Hoymiles HMS-1800 | CMT2300A | 4 | 4 | 1 |
|
|
||||||
| Hoymiles HMS-2000 | CMT2300A | 4 | 4 | 1 |
|
|
||||||
| Hoymiles HMT-1800 | CMT2300A | 6 | 3 | 3 |
|
|
||||||
| Hoymiles HMT-2250 | CMT2300A | 6 | 3 | 3 |
|
|
||||||
| Solenso SOL-H350 | NRF24L01+ | 1 | 1 | 1 |
|
|
||||||
| Solenso SOL-H400 | NRF24L01+ | 1 | 1 | 1 |
|
|
||||||
| Solenso SOL-H800 | NRF24L01+ | 2 | 2 | 1 |
|
|
||||||
| TSUN TSOL-M350 | NRF24L01+ | 1 | 1 | 1 |
|
|
||||||
| TSUN TSOL-M800 | NRF24L01+ | 2 | 2 | 1 |
|
|
||||||
| TSUN TSOL-M1600 | NRF24L01+ | 4 | 2 | 1 |
|
|
||||||
|
|
||||||
**TSUN compatibility remark:**
|
|
||||||
Compatibility with OpenDTU is most likly related to the serial number of the inverter. Current findings indicate that TSUN inverters with a serial number starting with "11" are supported, whereby inverters with a serial number starting with "10" are not.
|
|
||||||
|
|
||||||
## Features for end users
|
|
||||||
|
|
||||||
* Read live data from inverter
|
|
||||||
* Show inverters internal event log
|
|
||||||
* Show inverter information like firmware version, firmware build date, hardware revision and hardware version
|
|
||||||
* Show and set the current inverter limit
|
|
||||||
* Function to turn the inverter off and on
|
|
||||||
* Uses ESP32 microcontroller and NRF24L01+
|
|
||||||
* Multi-Inverter support
|
|
||||||
* MQTT support (with TLS)
|
|
||||||
* Home Assistant MQTT Auto Discovery support
|
|
||||||
* Nice and fancy WebApp with visualization of current data
|
|
||||||
* Firmware upgrade using the web UI
|
|
||||||
* Default source supports up to 10 inverters
|
|
||||||
* Time zone support
|
|
||||||
* Ethernet support
|
|
||||||
* Prometheus API endpoint (/api/prometheus/metrics)
|
|
||||||
* English, german and french web interface
|
|
||||||
* Displays (SSD1306, SH1106, PCD8544)
|
|
||||||
* Status LEDs
|
|
||||||
* Configuration management (export / import configurations)
|
|
||||||
* Dark Theme
|
|
||||||
|
|
||||||
## Features for developers
|
|
||||||
|
|
||||||
* The microcontroller part
|
|
||||||
* Build with Arduino PlatformIO Framework for the ESP32
|
|
||||||
* Uses a fork of [ESPAsyncWebserver](https://github.com/yubox-node-org/ESPAsyncWebServer) and [espMqttClient](https://github.com/bertmelis/espMqttClient)
|
|
||||||
|
|
||||||
* The WebApp part
|
|
||||||
* Build with [Vue.js](https://vuejs.org)
|
|
||||||
* Source is written in TypeScript
|
|
||||||
|
|
||||||
## Breaking changes
|
## Breaking changes
|
||||||
|
|
||||||
Generated using: `git log --date=short --pretty=format:"* %h%x09%ad%x09%s" | grep BREAKING`
|
Generated using: `git log --date=short --pretty=format:"* %h%x09%ad%x09%s" | grep BREAKING`
|
||||||
|
|
||||||
```code
|
```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=
|
* 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!
|
* 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!
|
* 3b7aef6 2023-02-13 BREAKING CHANGE: Web API!
|
||||||
@ -107,228 +38,6 @@ Generated using: `git log --date=short --pretty=format:"* %h%x09%ad%x09%s" | gre
|
|||||||
* 27ed4e3 2022-10-31 BREAKING: Change power factor from percent value to value between 0 and 1
|
* 27ed4e3 2022-10-31 BREAKING: Change power factor from percent value to value between 0 and 1
|
||||||
```
|
```
|
||||||
|
|
||||||
## Hardware you need
|
## Currently supported Inverters
|
||||||
|
|
||||||
### ESP32 board
|
A list of all currently supported inverters can be found [here](https://www.opendtu.solar/hardware/inverter_overview/)
|
||||||
|
|
||||||
For ease of use, buy a "ESP32 DEVKIT DOIT" or "ESP32 NodeMCU Development Board" with an ESP32-S3 or ESP-WROOM-32 chipset on it.
|
|
||||||
|
|
||||||
Sample Picture:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Also supported: Board with Ethernet-Connector and Power-over-Ethernet [Olimex ESP32-POE](https://www.olimex.com/Products/IoT/ESP32/ESP32-POE/open-source-hardware)
|
|
||||||
|
|
||||||
### NRF24L01+ radio board (See inverter table above for supported inverters)
|
|
||||||
|
|
||||||
The PLUS sign is IMPORTANT! There are different variants available, with antenna on the printed circuit board or external antenna.
|
|
||||||
|
|
||||||
Sample picture:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Buy your hardware from a trusted source, at best from a dealer/online shop in your country where you have support and the right to return non-functional hardware.
|
|
||||||
When you want to buy from Amazon, AliExpress, eBay etc., take note that there is a lot of low-quality or fake hardware offered. Read customer comments and ratings carefully!
|
|
||||||
|
|
||||||
A heavily incomplete list of trusted hardware shops in germany is:
|
|
||||||
|
|
||||||
* [AZ-Delivery](https://www.az-delivery.de/)
|
|
||||||
* [Makershop](https://www.makershop.de/)
|
|
||||||
* [Berrybase](https://www.berrybase.de/)
|
|
||||||
|
|
||||||
This list is for your convenience only, the project is not related to any of these shops.
|
|
||||||
|
|
||||||
### CMT2300A radio board (See inverter table above for supported inverters)
|
|
||||||
|
|
||||||
It is important to get a module which supports SPI communicatiton. The following modules are currently supported:
|
|
||||||
|
|
||||||
* EBYTE E49-900M20S
|
|
||||||
|
|
||||||
The CMT2300A uses 3-Wire half duplex SPI communication. Due to this fact it currently requires a separate SPI bus. If you want to run the CMT2300A module on the same ESP32 as a NRF24L01+ module or a PCD8544 display make sure you get a ESP which supports 2 SPI busses. Currently the SPI bus host is hardcoded to number 2. This may change in future.
|
|
||||||
|
|
||||||
### Power supply
|
|
||||||
|
|
||||||
Use a power suppy with 5 V and 1 A. The USB cable connected to your PC/Notebook may be powerful enough or may be not.
|
|
||||||
|
|
||||||
## Wiring up the NRF24L01+ module
|
|
||||||
|
|
||||||
### Schematic
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Symbolic view
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Change pin assignment
|
|
||||||
|
|
||||||
Its possible to change all the pins of the NRF24L01+ module, the Display, the LED etc.
|
|
||||||
The recommend way to change the pin assignment is by creating a custom [device profile](docs/DeviceProfiles.md).
|
|
||||||
It is also possible to create a custom environment and compile the source yourself. This can be achieved by copying one of the [env:....] sections from 'platformio.ini' to 'platformio_override.ini' and editing the 'platformio_override.ini' file and add/change one or more of the following lines to the 'build_flags' parameter:
|
|
||||||
|
|
||||||
```makefile
|
|
||||||
-DHOYMILES_PIN_MISO=19
|
|
||||||
-DHOYMILES_PIN_MOSI=23
|
|
||||||
-DHOYMILES_PIN_SCLK=18
|
|
||||||
-DHOYMILES_PIN_IRQ=16
|
|
||||||
-DHOYMILES_PIN_CE=4
|
|
||||||
-DHOYMILES_PIN_CS=5
|
|
||||||
```
|
|
||||||
|
|
||||||
It is recommended to make all changes only in the 'platformio_override.ini', this is your personal copy.
|
|
||||||
|
|
||||||
## Flashing and starting up
|
|
||||||
|
|
||||||
### with Visual Studio Code
|
|
||||||
|
|
||||||
* Install [Visual Studio Code](https://code.visualstudio.com/download) (from now named "vscode")
|
|
||||||
* In Visual Studio Code, install the [PlatformIO Extension](https://marketplace.visualstudio.com/items?itemName=platformio.platformio-ide)
|
|
||||||
* Install git and enable git in vscode - [git download](https://git-scm.com/downloads/) - [Instructions](https://www.jcchouinard.com/install-git-in-vscode/)
|
|
||||||
* Clone this repository (you really have to clone it, don't just download the ZIP file. During the build process the git hash gets embedded into the firmware. If you download the ZIP file a build error will occur): Inside vscode open the command palette by pressing `CTRL` + `SHIFT` + `P`. Enter `git clone`, add the repository-URL `https://github.com/tbnobody/OpenDTU`. Next you have to choose (or create) a target directory.
|
|
||||||
* In vscode, choose File --> Open Folder and select the previously downloaded source code. (You have to select the folder which contains the "platformio.ini" and "platformio_override.ini" file)
|
|
||||||
* Adjust the COM port in the file "platformio_override.ini" for your USB-to-serial-converter. It occurs twice:
|
|
||||||
* upload_port
|
|
||||||
* monitor_port
|
|
||||||
* Select the arrow button in the blue bottom status bar (PlatformIO: Upload) to compile and upload the firmware. During the compilation, all required libraries are downloaded automatically.
|
|
||||||
* Under Linux, if the upload fails with error messages "Could not open /dev/ttyUSB0, the port doesn't exist", you can check via ```ls -la /dev/tty*``` to which group your port belongs to, and then add your user this group via ```sudo adduser <yourusername> dialout``` (if you are using ```arch-linux``` use: ```sudo gpasswd -a <yourusername> uucp```, this method requires a logout/login of the affected user).
|
|
||||||
* There are two videos showing these steps:
|
|
||||||
* [Git Clone and compilation](https://youtu.be/9cA_esv3zeA)
|
|
||||||
* [Full installation and compilation](https://youtu.be/xs6TqHn7QWM)
|
|
||||||
|
|
||||||
### on the commandline with PlatformIO Core
|
|
||||||
|
|
||||||
* Install [PlatformIO Core](https://platformio.org/install/cli)
|
|
||||||
* Clone this repository (you really have to clone it, don't just download the ZIP file. During the build process the git hash gets embedded into the firmware. If you download the ZIP file a build error will occur)
|
|
||||||
* Adjust the COM port in the file "platformio_override.ini". It occurs twice:
|
|
||||||
* upload_port
|
|
||||||
* monitor_port
|
|
||||||
* build: `platformio run -e generic`
|
|
||||||
* upload to esp module: `platformio run -e generic -t upload`
|
|
||||||
* other options:
|
|
||||||
* clean the sources: `platformio run -e generic -t clean`
|
|
||||||
* erase flash: `platformio run -e generic -t erase`
|
|
||||||
|
|
||||||
### using the pre-compiled .bin files
|
|
||||||
|
|
||||||
The pre-compiled binary files can be found here on the [github page behind "Releases"](https://github.com/tbnobody/OpenDTU/releases) (look at the right column). For a first installation on an ESP32, download `opendtu-generic.factory.bin` and use a ESP32 flash tool of your choice to flash the `.bin` file to the address `0x0`. (The previous method with different .bin files is no more necessary.)
|
|
||||||
|
|
||||||
For further updates download `opendtu-generic.bin` and use the over-the-air firmware update in OpenDTU's web interface.
|
|
||||||
|
|
||||||
#### Flash with esptool.py (Linux)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
esptool.py --port /dev/ttyUSB0 --chip esp32 --before default_reset --after hard_reset \
|
|
||||||
write_flash --flash_mode dout --flash_freq 40m --flash_size detect \
|
|
||||||
0x0 opendtu-generic.factory.bin
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Flash with Espressif Flash Download Tool (Windows)
|
|
||||||
|
|
||||||
[Download link](https://www.espressif.com/en/support/download/other-tools)
|
|
||||||
|
|
||||||
* On startup, select Chip Type -> "ESP32" / WorkMode -> "Develop"
|
|
||||||
* Prepare all settings (see picture). Make sure to uncheck the `DoNotChgBin` option. Otherwise you may get errors like "invalid header".
|
|
||||||
* 
|
|
||||||
* Press "Erase" button on screen. Look into the terminal window, you should see dots appear. Then press the "Boot" button on the ESP32 board. Wait for "FINISH" to see if flashing/erasing is done.
|
|
||||||
* To program, press "Start" on screen, then the "Boot" button.
|
|
||||||
* When flashing is complete (FINISH appears) then press the Reset button on the ESP32 board (or powercycle ) to start the OpenDTU application.
|
|
||||||
|
|
||||||
#### Flash with ESP_Flasher (Windows)
|
|
||||||
|
|
||||||
Users report that [ESP_Flasher](https://github.com/Jason2866/ESP_Flasher/releases/) is suitable for flashing OpenDTU on Windows.
|
|
||||||
|
|
||||||
#### Flash with [ESP_Flasher](https://espressif.github.io/esptool-js/) - web version
|
|
||||||
|
|
||||||
It is also possible to flash it via the web tools which might be more convenient and is platform independent.
|
|
||||||
|
|
||||||
## First configuration
|
|
||||||
|
|
||||||
* After the initial flashing of the microcontroller, an Access Point called "OpenDTU-*" is opened. The default password is "openDTU42".
|
|
||||||
* Use a web browser to open the address [http://192.168.4.1](http://192.168.4.1)
|
|
||||||
* Navigate to Settings --> Network Settings and enter your WiFi credentials. The username to access the config menu is "admin" and the password the same as for accessing the Access Point (default: "openDTU42").
|
|
||||||
* OpenDTU then simultaneously connects to your WiFi AP with these credentials. Navigate to Info --> Network and look into section "Network Interface (Station)" for the IP address received via DHCP.
|
|
||||||
* If your WiFi AP uses an allow-list for MAC-addresses, please be aware that the ESP32 has two different MAC addresses for its AP and client modes, they are also listed at Info --> Network.
|
|
||||||
* When OpenDTU is connected to a configured WiFI AP, the "OpenDTU-*" Access Point is closed after 3 minutes.
|
|
||||||
* OpenDTU needs access to a working NTP server to get the current date & time. Both are sent to the inverter with each request. Default NTP server is pool.ntp.org. If your network has different requirements please change accordingly (Settings --> NTP Settings).
|
|
||||||
* Add your inverter in the inverter settings (Settings --> Inverter Settings)
|
|
||||||
|
|
||||||
## Flashing an Update using "Over The Air" OTA Update
|
|
||||||
|
|
||||||
Once you have your OpenDTU running and connected to WLAN, you can do further updates through the web interface.
|
|
||||||
Navigate to Settings --> Firmware upgrade and press the browse button. Select the firmware file from your local computer.
|
|
||||||
|
|
||||||
You'll find the firmware file (after a successful build process) under `.pio/build/generic/firmware.bin`.
|
|
||||||
|
|
||||||
If you downloaded a precompiled zip archive, unpack it and choose `opendtu-generic.bin`.
|
|
||||||
|
|
||||||
After the successful upload, the OpenDTU immediately restarts into the new firmware.
|
|
||||||
|
|
||||||
## MQTT Topic Documentation
|
|
||||||
|
|
||||||
A documentation of all available MQTT Topics can be found here: [MQTT Documentation](docs/MQTT_Topics.md)
|
|
||||||
|
|
||||||
## Web API Documentation
|
|
||||||
|
|
||||||
A documentation of the Web API can be found here: [Web-API Documentation](docs/Web-API.md)
|
|
||||||
|
|
||||||
## OpenDTU Breakoutboard
|
|
||||||
We sat down together and designed a PCB. This is 100% compatible with openDTU and has space for all extensions such as display and LEDs. You can find the PCB design here: https://github.com/marove2000/openDTU_BreakoutBoard
|
|
||||||
|
|
||||||
A ready to solder kit can be found here: https://shop.blinkyparts.com/en/OpenDTU-Your-evaluation-for-your-balcony-solar-system/blink237542
|
|
||||||
|
|
||||||
<img src="docs/builds/opendtu_breakoutboard.jpg" alt='OpenDTU Breakout Board with Case' height='300px'><img src="docs/builds/thumbnail.jpg" alt='OpenDTU Breakout Board with Case' height='300px'>
|
|
||||||
|
|
||||||
## Available cases
|
|
||||||
|
|
||||||
* <https://www.thingiverse.com/thing:5435911>
|
|
||||||
* <https://www.printables.com/model/293003-sol-opendtu-esp32-nrf24l01-case>
|
|
||||||
* <https://www.thingiverse.com/thing:5661780>
|
|
||||||
* <https://www.thingiverse.com/thing:5632374>
|
|
||||||
* <https://www.thingiverse.com/thing:5852233>
|
|
||||||
* <https://www.printables.com/model/377994-opendtu-pcb-box-for-the-wider-board>
|
|
||||||
* <https://www.printables.com/model/376840-esp32-ahoy-opendtu-pcb-housing>
|
|
||||||
|
|
||||||
## Available layouts for printed circuit boards
|
|
||||||
|
|
||||||
* [BreakoutBoard - sample printed circuit board for OpenDTU and Ahoy](https://github.com/dokuhn/openDTU-BreakoutBoard)
|
|
||||||
* [Board for OpenDTU with Display](https://github.com/SteffMUC/openDTU_wDisplay2)
|
|
||||||
* [OpenDTU PCB mit Display](https://github.com/turrican944/OpenDTU-PCB)
|
|
||||||
* [PCB for OpenDTU in Cable Branchbox](https://github.com/plewka/ESP-Solar_OpenDTU)
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
* Building the WebApp
|
|
||||||
* The WebApp can be build using yarn
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd webapp
|
|
||||||
yarn install
|
|
||||||
yarn build
|
|
||||||
```
|
|
||||||
|
|
||||||
* The updated output is placed in the 'webapp_dist' directory
|
|
||||||
* It is only necessary to build the webapp when you made changes to it
|
|
||||||
* Building the microcontroller firmware
|
|
||||||
* Visual Studio Code with the PlatformIO Extension is required for building
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
* First: When there is no light on the solar panels, the inverter completely turns off and does not answer to OpenDTU! So if you assembled your OpenDTU in the evening, wait until tomorrow.
|
|
||||||
* When there is no data received from the inverter(s) - try to reduce the distance between the openDTU and the inverter (e.g. move it to the window towards the roof)
|
|
||||||
* Under Settings -> DTU Settings you can increase the transmit power "PA level". Default is "minimum".
|
|
||||||
* The NRF24L01+ needs relatively much current. With bad power supply (and especially bad cables!) a 10 µF capacitor soldered directly to the NRF24L01+ board connector brings more stability (pin 1+2 are the power supply). Note the polarity of the capacitor…
|
|
||||||
* You can try to use an USB power supply with 1 A or more instead of connecting the ESP32 to the computer.
|
|
||||||
* Try a different USB cable. Once again, a stable power source is important. Some USB cables are made of much plastic and very little copper inside.
|
|
||||||
* Double check that you have a radio module NRF24L01+ with a plus sign at the end. NRF24L01 module without the plus are not compatible with this project.
|
|
||||||
* There is no possibility of auto-discovering the inverters. Double check you have entered the serial numbers of the inverters correctly.
|
|
||||||
* OpenDTU needs access to a working NTP server to get the current date & time.
|
|
||||||
* If your problem persists, check the [Issues on Github](https://github.com/tbnobody/OpenDTU/issues). Please inspect not only the open issues, also the closed issues contain useful information.
|
|
||||||
* Another source of information are the [Discussions](https://github.com/tbnobody/OpenDTU/discussions/)
|
|
||||||
* When flashing with VSCode Plattform.IO fails and also with ESPRESSIF tool a demo bin file cannot be flashed to the ESP32 with error message "A fatal error occurred: MD5 of file does not match data in flash!" than un-wire/unconnect ESP32 from the NRF24L01+ board. Try to flash again and rewire afterwards.
|
|
||||||
* Make sure to connect one inverter only to one DTU (Original, Ahoy, OpenDTU doesn't make a difference). If you query a inverter by multiple DTUs you will get strange peaks in your values.
|
|
||||||
|
|
||||||
## Related Projects
|
|
||||||
|
|
||||||
* [Ahoy](https://github.com/grindylow/ahoy)
|
|
||||||
* [DTU Simulator](https://github.com/Ziyatoe/DTUsimMI1x00-Hoymiles)
|
|
||||||
* [OpenDTU extended to talk to Victrons MPPT battery chargers (Ve.Direct)](https://github.com/helgeerbe/OpenDTU_VeDirect)
|
|
||||||
|
|||||||
@ -1,113 +1,3 @@
|
|||||||
# Device Profiles
|
# Device Profiles
|
||||||
|
|
||||||
It is possible to change hardware settings like pin assignments or ethernet support using a json file. The json file can be uploaded using the configuration management in the web interface. Just select "Pin Mapping (pin_mapping.json)" in the recovery section.
|
This documentation will has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/firmware/device_profiles/>
|
||||||
|
|
||||||
When the file is uploaded the ESP performs a reboot. This is required as the pin settings could have changed within the file. By default all the pin assignments are used as compiled into the firmware.
|
|
||||||
|
|
||||||
To change the device profile, navigate to the "Device Manager" and selected the appropriate profile. You can see the current (Active) and the new (Selected) in assignment in the table below the combobox.
|
|
||||||
|
|
||||||
## Structure of the json file
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"name": "Generic NodeMCU 38 pin",
|
|
||||||
"nrf24": {
|
|
||||||
"miso": 19,
|
|
||||||
"mosi": 23,
|
|
||||||
"clk": 18,
|
|
||||||
"irq": 16,
|
|
||||||
"en": 4,
|
|
||||||
"cs": 5
|
|
||||||
},
|
|
||||||
"eth": {
|
|
||||||
"enabled": false,
|
|
||||||
"phy_addr": -1,
|
|
||||||
"power": -1,
|
|
||||||
"mdc": -1,
|
|
||||||
"mdio": -1,
|
|
||||||
"type": -1,
|
|
||||||
"clk_mode": -1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Generic NodeMCU 38 pin with SSD1306",
|
|
||||||
"nrf24": {
|
|
||||||
"miso": 19,
|
|
||||||
"mosi": 23,
|
|
||||||
"clk": 18,
|
|
||||||
"irq": 16,
|
|
||||||
"en": 4,
|
|
||||||
"cs": 5
|
|
||||||
},
|
|
||||||
"eth": {
|
|
||||||
"enabled": false,
|
|
||||||
"phy_addr": -1,
|
|
||||||
"power": -1,
|
|
||||||
"mdc": -1,
|
|
||||||
"mdio": -1,
|
|
||||||
"type": -1,
|
|
||||||
"clk_mode": -1
|
|
||||||
},
|
|
||||||
"display": {
|
|
||||||
"type": 2,
|
|
||||||
"data": 21,
|
|
||||||
"clk": 22
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Olimex ESP32-POE",
|
|
||||||
"nrf24": {
|
|
||||||
"miso": 15,
|
|
||||||
"mosi": 2,
|
|
||||||
"clk": 14,
|
|
||||||
"irq": 13,
|
|
||||||
"en": 16,
|
|
||||||
"cs": 5
|
|
||||||
},
|
|
||||||
"eth": {
|
|
||||||
"enabled": true,
|
|
||||||
"phy_addr": 0,
|
|
||||||
"power": 12,
|
|
||||||
"mdc": 23,
|
|
||||||
"mdio": 18,
|
|
||||||
"type": 0,
|
|
||||||
"clk_mode": 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
The json file can contain multiple profiles. Each profile requires a name and different parameters. If one parameter is not set, the default value, as compiled into the firmware is used. The example above shows all the currently supported values. Others may follow. Sample files for some boards can be found [here](DeviceProfiles/). This means you can just flash the generic bin file and upload the json file. Then you select your board and everything works hopyfully as expected.
|
|
||||||
|
|
||||||
## Implemented configuration values
|
|
||||||
|
|
||||||
| Parameter | Data Type | Description |
|
|
||||||
| ------------- | --------- | ----------- |
|
|
||||||
| name | string | Unique name of the profile (max 63 characters) |
|
|
||||||
| nrf24.miso | number | MISO Pin |
|
|
||||||
| nrf24.mosi | number | MOSI Pin |
|
|
||||||
| nrf24.clk | number | Clock Pin |
|
|
||||||
| nrf24.irq | number | Interrupt Pin |
|
|
||||||
| nrf24.en | number | Enable Pin |
|
|
||||||
| nrf24.cs | number | Chip Select Pin |
|
|
||||||
| cmt.sdio | number | SDIO Pin |
|
|
||||||
| cmt.clk | number | CLK Pin |
|
|
||||||
| cmt.cs | number | CS Pin |
|
|
||||||
| cmt.fcs | number | FCS Pin |
|
|
||||||
| cmt.gpio2 | number | GPIO2 Pin (optional) |
|
|
||||||
| cmt.gpio3 | number | GPIO3 Pin (optional) |
|
|
||||||
| eth.enabled | boolean | Enable/Disable the ethernet stack |
|
|
||||||
| eth.phy_addr | number | Unique PHY addr |
|
|
||||||
| eth.power | number | Power Pin (if available). Use -1 for not assigned pins. |
|
|
||||||
| eth.mdc | number | Serial Management Interface MDC Pin. Use -1 for not assigned pins. |
|
|
||||||
| eth.mdio | number | Serial Management Interface MDIO Pin. Use -1 for not assigned pins. |
|
|
||||||
| eth.type | number | Possible values:<br>* 0 = ETH_PHY_LAN8720<br>* 1 = ETH_PHY_TLK110<br>* 2 = ETH_PHY_RTL8201<br>* 3 = ETH_PHY_DP83848<br>* 4 = ETH_PHY_DM9051<br>* 5 = ETH_PHY_KSZ8041<br>* 6 = ETH_PHY_KSZ8081 |
|
|
||||||
| eth.clk_mode | number | Possible values:<br>* 0 = ETH_CLOCK_GPIO0_IN<br>* 1 = ETH_CLOCK_GPIO0_OUT<br>* 2 = ETH_CLOCK_GPIO16_OUT<br>* 3 = ETH_CLOCK_GPIO17_OUT |
|
|
||||||
| display.type | number | Specify type of display. Possible values:<br>* 0 = None (default)<br>* 1 = PCD8544 <br>* 2 = SSD1306 <br>* 3 = SH1106 |
|
|
||||||
| display.data | number | Data Pin (e.g. SDA for i2c displays) required for all displays. Use 255 for not assigned pins. |
|
|
||||||
| display.clk | number | Clock Pin (e.g. SCL for i2c displays) required for SSD1306 and SH1106. Use 255 for not assigned pins. |
|
|
||||||
| display.cs | number | Chip Select Pin required for PCD8544. Use 255 for not assigned pins. |
|
|
||||||
| display.reset | number | Reset Pin required for PCD8544, optional for all other displays. Use 255 for not assigned pins. |
|
|
||||||
| led.led0 | number | LED pin for network indication. Blinking = WLAN connected but NTP & MQTT (if enabled) disconnected. On = WLAN, NTP, MQTT connected. Off = Network not connected |
|
|
||||||
| led.led1 | number | LED pin for inverter indication. On = All inverters reachable & producing. Blinking = All inverters reachable but not producing. Off = At least one inverter is not reachable. Only inverters with polling enabled are considered. |
|
|
||||||
|
|||||||
76
docs/DeviceProfiles/AhoyDTU-ESP32.json
Normal file
76
docs/DeviceProfiles/AhoyDTU-ESP32.json
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "AhoyDTU ESP32 Display LED",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://ahoydtu.de/getting_started/"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 19,
|
||||||
|
"mosi": 23,
|
||||||
|
"clk": 18,
|
||||||
|
"irq": 16,
|
||||||
|
"en": 4,
|
||||||
|
"cs": 5
|
||||||
|
},
|
||||||
|
"led": {
|
||||||
|
"led0": 25,
|
||||||
|
"led1": 26
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": 2,
|
||||||
|
"data": 21,
|
||||||
|
"clk": 22
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AhoyDTU ESP32 Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://ahoydtu.de/getting_started/"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 19,
|
||||||
|
"mosi": 23,
|
||||||
|
"clk": 18,
|
||||||
|
"irq": 16,
|
||||||
|
"en": 4,
|
||||||
|
"cs": 5
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": 2,
|
||||||
|
"data": 21,
|
||||||
|
"clk": 22
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AhoyDTU ESP32 LED",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://ahoydtu.de/getting_started/"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 19,
|
||||||
|
"mosi": 23,
|
||||||
|
"clk": 18,
|
||||||
|
"irq": 16,
|
||||||
|
"en": 4,
|
||||||
|
"cs": 5
|
||||||
|
},
|
||||||
|
"led": {
|
||||||
|
"led0": 25,
|
||||||
|
"led1": 26
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AhoyDTU ESP32",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://ahoydtu.de/getting_started/"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 19,
|
||||||
|
"mosi": 23,
|
||||||
|
"clk": 18,
|
||||||
|
"irq": 16,
|
||||||
|
"en": 4,
|
||||||
|
"cs": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
20
docs/DeviceProfiles/CASmo-DTU.json
Normal file
20
docs/DeviceProfiles/CASmo-DTU.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "CASmo-DTU",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://casmo.info/product-details/?product=2"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 19,
|
||||||
|
"mosi": 23,
|
||||||
|
"clk": 18,
|
||||||
|
"irq": 16,
|
||||||
|
"en": 4,
|
||||||
|
"cs": 5
|
||||||
|
},
|
||||||
|
"led": {
|
||||||
|
"led0": 25,
|
||||||
|
"led1": 26
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@ -1,6 +1,12 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"name": "NRF, LEDs, Display",
|
"name": "NRF, LEDs, Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://shop.blinkyparts.com/de/OpenDTU-NRF-Deine-Auswertung-fuer-deine-Balkonsolaranlage-kompatibel-zu-Hoymiles-HM-Serie-NRF-Modul/blink237542"},
|
||||||
|
{"name": "Manual DE", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_de.pdf"},
|
||||||
|
{"name": "Manual EN", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_en.pdf"},
|
||||||
|
{"name": "Schematic", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/ibom.html"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 19,
|
"miso": 19,
|
||||||
"mosi": 23,
|
"mosi": 23,
|
||||||
@ -21,6 +27,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "CMT, LEDs, Display",
|
"name": "CMT, LEDs, Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://shop.blinkyparts.com/de/OpenDTU-CMT-Deine-Auswertung-fuer-deine-Balkonsolaranlage-kompatibel-zu-Hoymiles-HMS-und-HMT-Serie-CMT-Modul/blink238342"},
|
||||||
|
{"name": "Manual DE", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_de.pdf"},
|
||||||
|
{"name": "Manual EN", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_en.pdf"},
|
||||||
|
{"name": "Schematic", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/ibom.html"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": -1,
|
"miso": -1,
|
||||||
"mosi": -1,
|
"mosi": -1,
|
||||||
@ -49,6 +61,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "NRF, Display",
|
"name": "NRF, Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://shop.blinkyparts.com/de/OpenDTU-NRF-Deine-Auswertung-fuer-deine-Balkonsolaranlage-kompatibel-zu-Hoymiles-HM-Serie-NRF-Modul/blink237542"},
|
||||||
|
{"name": "Manual DE", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_de.pdf"},
|
||||||
|
{"name": "Manual EN", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_en.pdf"},
|
||||||
|
{"name": "Schematic", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/ibom.html"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 19,
|
"miso": 19,
|
||||||
"mosi": 23,
|
"mosi": 23,
|
||||||
@ -65,6 +83,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "CMT, Display",
|
"name": "CMT, Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://shop.blinkyparts.com/de/OpenDTU-CMT-Deine-Auswertung-fuer-deine-Balkonsolaranlage-kompatibel-zu-Hoymiles-HMS-und-HMT-Serie-CMT-Modul/blink238342"},
|
||||||
|
{"name": "Manual DE", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_de.pdf"},
|
||||||
|
{"name": "Manual EN", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_en.pdf"},
|
||||||
|
{"name": "Schematic", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/ibom.html"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": -1,
|
"miso": -1,
|
||||||
"mosi": -1,
|
"mosi": -1,
|
||||||
@ -89,6 +113,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "NRF, LEDs",
|
"name": "NRF, LEDs",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://shop.blinkyparts.com/de/OpenDTU-NRF-Deine-Auswertung-fuer-deine-Balkonsolaranlage-kompatibel-zu-Hoymiles-HM-Serie-NRF-Modul/blink237542"},
|
||||||
|
{"name": "Manual DE", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_de.pdf"},
|
||||||
|
{"name": "Manual EN", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_en.pdf"},
|
||||||
|
{"name": "Schematic", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/ibom.html"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 19,
|
"miso": 19,
|
||||||
"mosi": 23,
|
"mosi": 23,
|
||||||
@ -104,6 +134,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "CMT, LEDs",
|
"name": "CMT, LEDs",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://shop.blinkyparts.com/de/OpenDTU-CMT-Deine-Auswertung-fuer-deine-Balkonsolaranlage-kompatibel-zu-Hoymiles-HMS-und-HMT-Serie-CMT-Modul/blink238342"},
|
||||||
|
{"name": "Manual DE", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_de.pdf"},
|
||||||
|
{"name": "Manual EN", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_en.pdf"},
|
||||||
|
{"name": "Schematic", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/ibom.html"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": -1,
|
"miso": -1,
|
||||||
"mosi": -1,
|
"mosi": -1,
|
||||||
@ -127,6 +163,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "NRF",
|
"name": "NRF",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://shop.blinkyparts.com/de/OpenDTU-NRF-Deine-Auswertung-fuer-deine-Balkonsolaranlage-kompatibel-zu-Hoymiles-HM-Serie-NRF-Modul/blink237542"},
|
||||||
|
{"name": "Manual DE", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_de.pdf"},
|
||||||
|
{"name": "Manual EN", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_en.pdf"},
|
||||||
|
{"name": "Schematic", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/ibom.html"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 19,
|
"miso": 19,
|
||||||
"mosi": 23,
|
"mosi": 23,
|
||||||
@ -138,6 +180,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "CMT",
|
"name": "CMT",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://shop.blinkyparts.com/de/OpenDTU-CMT-Deine-Auswertung-fuer-deine-Balkonsolaranlage-kompatibel-zu-Hoymiles-HMS-und-HMT-Serie-CMT-Modul/blink238342"},
|
||||||
|
{"name": "Manual DE", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_de.pdf"},
|
||||||
|
{"name": "Manual EN", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/manual/OpenDTU_Breakout_en.pdf"},
|
||||||
|
{"name": "Schematic", "url": "https://binary-kitchen.github.io/SolderingTutorial/OpenDTU_Breakout/ibom.html"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": -1,
|
"miso": -1,
|
||||||
"mosi": -1,
|
"mosi": -1,
|
||||||
|
|||||||
25
docs/DeviceProfiles/esp32_stick_poe_a.json
Normal file
25
docs/DeviceProfiles/esp32_stick_poe_a.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Esp32-Stick-PoE-A",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/allexoK/Esp32-Stick-Boards-Docs"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 2,
|
||||||
|
"mosi": 15,
|
||||||
|
"clk": 14,
|
||||||
|
"irq": 34,
|
||||||
|
"en": 12,
|
||||||
|
"cs": 4
|
||||||
|
},
|
||||||
|
"eth": {
|
||||||
|
"enabled": true,
|
||||||
|
"phy_addr": 1,
|
||||||
|
"power": -1,
|
||||||
|
"mdc": 23,
|
||||||
|
"mdio": 18,
|
||||||
|
"type": 0,
|
||||||
|
"clk_mode": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
74
docs/DeviceProfiles/liligo_t-eth-lite_poe.json
Normal file
74
docs/DeviceProfiles/liligo_t-eth-lite_poe.json
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "LILYGO T-ETH-Lite-POE CMT",
|
||||||
|
"links": [
|
||||||
|
{"name": "Datasheet", "url": "https://www.lilygo.cc/products/t-eth-lite"}
|
||||||
|
],
|
||||||
|
"eth": {
|
||||||
|
"enabled": true,
|
||||||
|
"phy_addr": 0,
|
||||||
|
"power": 12,
|
||||||
|
"mdc": 23,
|
||||||
|
"mdio": 18,
|
||||||
|
"type": 2,
|
||||||
|
"clk_mode": 0
|
||||||
|
},
|
||||||
|
"cmt": {
|
||||||
|
"clk": 15,
|
||||||
|
"cs": 32,
|
||||||
|
"fcs": 33,
|
||||||
|
"sdio": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LILYGO T-ETH-Lite-POE NRF24",
|
||||||
|
"links": [
|
||||||
|
{"name": "Datasheet", "url": "https://www.lilygo.cc/products/t-eth-lite"}
|
||||||
|
],
|
||||||
|
"eth": {
|
||||||
|
"enabled": true,
|
||||||
|
"phy_addr": 0,
|
||||||
|
"power": 12,
|
||||||
|
"mdc": 23,
|
||||||
|
"mdio": 18,
|
||||||
|
"type": 2,
|
||||||
|
"clk_mode": 0
|
||||||
|
},
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 34,
|
||||||
|
"mosi": 13,
|
||||||
|
"clk": 14,
|
||||||
|
"irq": 35,
|
||||||
|
"en": 4,
|
||||||
|
"cs": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LILYGO T-ETH-Lite-POE NRF24 + Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Datasheet", "url": "https://www.lilygo.cc/products/t-eth-lite"}
|
||||||
|
],
|
||||||
|
"eth": {
|
||||||
|
"enabled": true,
|
||||||
|
"phy_addr": 0,
|
||||||
|
"power": 12,
|
||||||
|
"mdc": 23,
|
||||||
|
"mdio": 18,
|
||||||
|
"type": 2,
|
||||||
|
"clk_mode": 0
|
||||||
|
},
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 34,
|
||||||
|
"mosi": 13,
|
||||||
|
"clk": 14,
|
||||||
|
"irq": 35,
|
||||||
|
"en": 4,
|
||||||
|
"cs": 2
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": 3,
|
||||||
|
"data": 32,
|
||||||
|
"clk": 33
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@ -1,6 +1,9 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"name": "LILYGO TTGO T-Internet-POE",
|
"name": "LILYGO TTGO T-Internet-POE",
|
||||||
|
"links": [
|
||||||
|
{"name": "Datasheet", "url": "https://www.lilygo.cc/products/t-internet-poe"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 2,
|
"miso": 2,
|
||||||
"mosi": 15,
|
"mosi": 15,
|
||||||
@ -21,6 +24,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "LILYGO TTGO T-Internet-POE, nrf24 direct solder",
|
"name": "LILYGO TTGO T-Internet-POE, nrf24 direct solder",
|
||||||
|
"links": [
|
||||||
|
{"name": "Datasheet", "url": "https://www.lilygo.cc/products/t-internet-poe"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 12,
|
"miso": 12,
|
||||||
"mosi": 4,
|
"mosi": 4,
|
||||||
@ -41,6 +47,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "LILYGO TTGO T-Internet-POE, nrf24 direct solder, SSD1306",
|
"name": "LILYGO TTGO T-Internet-POE, nrf24 direct solder, SSD1306",
|
||||||
|
"links": [
|
||||||
|
{"name": "Datasheet", "url": "https://www.lilygo.cc/products/t-internet-poe"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 12,
|
"miso": 12,
|
||||||
"mosi": 4,
|
"mosi": 4,
|
||||||
|
|||||||
@ -73,6 +73,25 @@
|
|||||||
"clk": 22
|
"clk": 22
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "NRF24 with SSD1309",
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 19,
|
||||||
|
"mosi": 23,
|
||||||
|
"clk": 18,
|
||||||
|
"irq": 16,
|
||||||
|
"en": 4,
|
||||||
|
"cs": 5
|
||||||
|
},
|
||||||
|
"eth": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": 4,
|
||||||
|
"data": 21,
|
||||||
|
"clk": 22
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "CMT2300A with SSD1306",
|
"name": "CMT2300A with SSD1306",
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
@ -127,6 +146,33 @@
|
|||||||
"clk": 22
|
"clk": 22
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "CMT2300A with SSD1309",
|
||||||
|
"nrf24": {
|
||||||
|
"miso": -1,
|
||||||
|
"mosi": -1,
|
||||||
|
"clk": -1,
|
||||||
|
"irq": -1,
|
||||||
|
"en": -1,
|
||||||
|
"cs": -1
|
||||||
|
},
|
||||||
|
"cmt": {
|
||||||
|
"clk": 18,
|
||||||
|
"cs": 4,
|
||||||
|
"fcs": 5,
|
||||||
|
"sdio": 23,
|
||||||
|
"gpio2": 19,
|
||||||
|
"gpio3": 16
|
||||||
|
},
|
||||||
|
"eth": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": 4,
|
||||||
|
"data": 21,
|
||||||
|
"clk": 22
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "NRF24 + CMT2300A",
|
"name": "NRF24 + CMT2300A",
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"name": "Olimex ESP32-EVB",
|
"name": "Olimex ESP32-EVB",
|
||||||
|
"links": [
|
||||||
|
{ "name": "Datasheet", "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-EVB/open-source-hardware" }
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 15,
|
"miso": 15,
|
||||||
"mosi": 2,
|
"mosi": 2,
|
||||||
|
|||||||
47
docs/DeviceProfiles/olimex_esp32_gateway.json
Normal file
47
docs/DeviceProfiles/olimex_esp32_gateway.json
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Olimex ESP32-Gateway",
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 14,
|
||||||
|
"mosi": 13,
|
||||||
|
"clk": 12,
|
||||||
|
"irq": 15,
|
||||||
|
"en": 2,
|
||||||
|
"cs": 4
|
||||||
|
},
|
||||||
|
"eth": {
|
||||||
|
"enabled": true,
|
||||||
|
"phy_addr": 0,
|
||||||
|
"power": 12,
|
||||||
|
"mdc": 23,
|
||||||
|
"mdio": 18,
|
||||||
|
"type": 0,
|
||||||
|
"clk_mode": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Olimex ESP32-Gateway with SSH1106",
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 14,
|
||||||
|
"mosi": 13,
|
||||||
|
"clk": 12,
|
||||||
|
"irq": 15,
|
||||||
|
"en": 2,
|
||||||
|
"cs": 4
|
||||||
|
},
|
||||||
|
"eth": {
|
||||||
|
"enabled": true,
|
||||||
|
"phy_addr": 0,
|
||||||
|
"power": 12,
|
||||||
|
"mdc": 23,
|
||||||
|
"mdio": 18,
|
||||||
|
"type": 0,
|
||||||
|
"clk_mode": 3
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": 3,
|
||||||
|
"data": 32,
|
||||||
|
"clk": 16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@ -1,6 +1,9 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"name": "Olimex ESP32-POE",
|
"name": "Olimex ESP32-POE",
|
||||||
|
"links": [
|
||||||
|
{"name": "Datasheet", "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-POE/open-source-hardware"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 15,
|
"miso": 15,
|
||||||
"mosi": 2,
|
"mosi": 2,
|
||||||
@ -21,6 +24,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Olimex ESP32-POE with SSD1306",
|
"name": "Olimex ESP32-POE with SSD1306",
|
||||||
|
"links": [
|
||||||
|
{"name": "Datasheet", "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-POE/open-source-hardware"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 15,
|
"miso": 15,
|
||||||
"mosi": 2,
|
"mosi": 2,
|
||||||
@ -46,6 +52,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Olimex ESP32-POE with SH1106",
|
"name": "Olimex ESP32-POE with SH1106",
|
||||||
|
"links": [
|
||||||
|
{"name": "Datasheet", "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-POE/open-source-hardware"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 15,
|
"miso": 15,
|
||||||
"mosi": 2,
|
"mosi": 2,
|
||||||
@ -68,5 +77,33 @@
|
|||||||
"data": 33,
|
"data": 33,
|
||||||
"clk": 32
|
"clk": 32
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Olimex ESP32-POE with SSD1309",
|
||||||
|
"links": [
|
||||||
|
{"name": "Datasheet", "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-POE/open-source-hardware"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 15,
|
||||||
|
"mosi": 2,
|
||||||
|
"clk": 14,
|
||||||
|
"irq": 13,
|
||||||
|
"en": 16,
|
||||||
|
"cs": 5
|
||||||
|
},
|
||||||
|
"eth": {
|
||||||
|
"enabled": true,
|
||||||
|
"phy_addr": 0,
|
||||||
|
"power": 12,
|
||||||
|
"mdc": 23,
|
||||||
|
"mdio": 18,
|
||||||
|
"type": 0,
|
||||||
|
"clk_mode": 3
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": 4,
|
||||||
|
"data": 33,
|
||||||
|
"clk": 32
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -1,6 +1,9 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"name": "OpenDTU Fusion v1",
|
"name": "OpenDTU Fusion v1",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 48,
|
"miso": 48,
|
||||||
"mosi": 35,
|
"mosi": 35,
|
||||||
@ -25,6 +28,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "OpenDTU Fusion v1 with SSD1306 Display",
|
"name": "OpenDTU Fusion v1 with SSD1306 Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 48,
|
"miso": 48,
|
||||||
"mosi": 35,
|
"mosi": 35,
|
||||||
@ -54,6 +60,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "OpenDTU Fusion v1 with SH1106 Display",
|
"name": "OpenDTU Fusion v1 with SH1106 Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 48,
|
"miso": 48,
|
||||||
"mosi": 35,
|
"mosi": 35,
|
||||||
@ -83,6 +92,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "OpenDTU Fusion v2 with CMT2300A and NRF24",
|
"name": "OpenDTU Fusion v2 with CMT2300A and NRF24",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 48,
|
"miso": 48,
|
||||||
"mosi": 35,
|
"mosi": 35,
|
||||||
@ -115,6 +127,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "OpenDTU Fusion v2 with CMT2300A, NRF24 and SH1106 Display",
|
"name": "OpenDTU Fusion v2 with CMT2300A, NRF24 and SH1106 Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 48,
|
"miso": 48,
|
||||||
"mosi": 35,
|
"mosi": 35,
|
||||||
@ -152,6 +167,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "OpenDTU Fusion v2 with CMT2300A, NRF24 and SSD1306 Display",
|
"name": "OpenDTU Fusion v2 with CMT2300A, NRF24 and SSD1306 Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 48,
|
"miso": 48,
|
||||||
"mosi": 35,
|
"mosi": 35,
|
||||||
@ -186,5 +204,122 @@
|
|||||||
"data": 2,
|
"data": 2,
|
||||||
"clk": 1
|
"clk": 1
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "OpenDTU Fusion v2 PoE",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 48,
|
||||||
|
"mosi": 35,
|
||||||
|
"clk": 36,
|
||||||
|
"irq": 47,
|
||||||
|
"en": 38,
|
||||||
|
"cs": 37
|
||||||
|
},
|
||||||
|
"cmt": {
|
||||||
|
"clk": 6,
|
||||||
|
"cs": 4,
|
||||||
|
"fcs": 21,
|
||||||
|
"sdio": 5,
|
||||||
|
"gpio2": 3,
|
||||||
|
"gpio3": 8
|
||||||
|
},
|
||||||
|
"w5500": {
|
||||||
|
"mosi": 40,
|
||||||
|
"miso": 41,
|
||||||
|
"sclk": 39,
|
||||||
|
"cs": 42,
|
||||||
|
"int": 44,
|
||||||
|
"rst": 43
|
||||||
|
},
|
||||||
|
"led": {
|
||||||
|
"led0": 17,
|
||||||
|
"led1": 18
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": 0,
|
||||||
|
"data": 2,
|
||||||
|
"clk": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "OpenDTU Fusion v2 PoE with SH1106 Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 48,
|
||||||
|
"mosi": 35,
|
||||||
|
"clk": 36,
|
||||||
|
"irq": 47,
|
||||||
|
"en": 38,
|
||||||
|
"cs": 37
|
||||||
|
},
|
||||||
|
"cmt": {
|
||||||
|
"clk": 6,
|
||||||
|
"cs": 4,
|
||||||
|
"fcs": 21,
|
||||||
|
"sdio": 5,
|
||||||
|
"gpio2": 3,
|
||||||
|
"gpio3": 8
|
||||||
|
},
|
||||||
|
"w5500": {
|
||||||
|
"mosi": 40,
|
||||||
|
"miso": 41,
|
||||||
|
"sclk": 39,
|
||||||
|
"cs": 42,
|
||||||
|
"int": 44,
|
||||||
|
"rst": 43
|
||||||
|
},
|
||||||
|
"led": {
|
||||||
|
"led0": 17,
|
||||||
|
"led1": 18
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": 3,
|
||||||
|
"data": 2,
|
||||||
|
"clk": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "OpenDTU Fusion v2 PoE with SSD1306 Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 48,
|
||||||
|
"mosi": 35,
|
||||||
|
"clk": 36,
|
||||||
|
"irq": 47,
|
||||||
|
"en": 38,
|
||||||
|
"cs": 37
|
||||||
|
},
|
||||||
|
"cmt": {
|
||||||
|
"clk": 6,
|
||||||
|
"cs": 4,
|
||||||
|
"fcs": 21,
|
||||||
|
"sdio": 5,
|
||||||
|
"gpio2": 3,
|
||||||
|
"gpio3": 8
|
||||||
|
},
|
||||||
|
"w5500": {
|
||||||
|
"mosi": 40,
|
||||||
|
"miso": 41,
|
||||||
|
"sclk": 39,
|
||||||
|
"cs": 42,
|
||||||
|
"int": 44,
|
||||||
|
"rst": 43
|
||||||
|
},
|
||||||
|
"led": {
|
||||||
|
"led0": 17,
|
||||||
|
"led1": 18
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": 2,
|
||||||
|
"data": 2,
|
||||||
|
"clk": 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -1,6 +1,9 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"name": "WT32-ETH01",
|
"name": "WT32-ETH01",
|
||||||
|
"links": [
|
||||||
|
{"name": "Datasheet", "url": "http://www.wireless-tag.com/portfolio/wt32-eth01/"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 4,
|
"miso": 4,
|
||||||
"mosi": 2,
|
"mosi": 2,
|
||||||
@ -19,8 +22,39 @@
|
|||||||
"clk_mode": 0
|
"clk_mode": 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "WT32-ETH01 with SH1106",
|
||||||
|
"links": [
|
||||||
|
{"name": "Datasheet", "url": "http://www.wireless-tag.com/portfolio/wt32-eth01/"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 4,
|
||||||
|
"mosi": 2,
|
||||||
|
"clk": 32,
|
||||||
|
"irq": 33,
|
||||||
|
"en": 14,
|
||||||
|
"cs": 15
|
||||||
|
},
|
||||||
|
"eth": {
|
||||||
|
"enabled": true,
|
||||||
|
"phy_addr": 1,
|
||||||
|
"power": 16,
|
||||||
|
"mdc": 23,
|
||||||
|
"mdio": 18,
|
||||||
|
"type": 0,
|
||||||
|
"clk_mode": 0
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": 3,
|
||||||
|
"data": 5,
|
||||||
|
"clk": 17
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "WT32-ETH01 with SSD1306",
|
"name": "WT32-ETH01 with SSD1306",
|
||||||
|
"links": [
|
||||||
|
{"name": "Datasheet", "url": "http://www.wireless-tag.com/portfolio/wt32-eth01/"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 4,
|
"miso": 4,
|
||||||
"mosi": 2,
|
"mosi": 2,
|
||||||
@ -43,5 +77,33 @@
|
|||||||
"data": 5,
|
"data": 5,
|
||||||
"clk": 17
|
"clk": 17
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "WT32-ETH01 with SSD1309",
|
||||||
|
"links": [
|
||||||
|
{"name": "Datasheet", "url": "http://www.wireless-tag.com/portfolio/wt32-eth01/"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 4,
|
||||||
|
"mosi": 2,
|
||||||
|
"clk": 32,
|
||||||
|
"irq": 33,
|
||||||
|
"en": 14,
|
||||||
|
"cs": 15
|
||||||
|
},
|
||||||
|
"eth": {
|
||||||
|
"enabled": true,
|
||||||
|
"phy_addr": 1,
|
||||||
|
"power": 16,
|
||||||
|
"mdc": 23,
|
||||||
|
"mdio": 18,
|
||||||
|
"type": 0,
|
||||||
|
"clk_mode": 0
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": 4,
|
||||||
|
"data": 5,
|
||||||
|
"clk": 17
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -1,20 +1,3 @@
|
|||||||
# Display integration
|
# Display integration
|
||||||
|
|
||||||
OpenDTU currently supports 3 types of displays (SSD1306, SH1106 and PCD8544). Currently only displays with a resolution of 128x64 pixel are supported. To activate a display you have to specify it's type and pin assignment either in the `platformio_override.ini` or in a device profile. Due to the fact that device profiles work with the pre-compiled binary the following documentation will only cover the device profile method.
|
This documentation will has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/hardware/display/>
|
||||||
|
|
||||||
You can either create your own device profile as described [here](DeviceProfiles.md) or use some pre-defined. The pre-defined profiles can be found [here](DeviceProfiles/). You can simply open the json file with a text editor of your choice to view/edit the pin assignment.
|
|
||||||
|
|
||||||
## Uploading Device Profiles
|
|
||||||
|
|
||||||
Use the "Config Management" site to upload (Restore) the json file. Make sure to choose "Pin Mapping (pin_mapping.json)" in the combo box. After you click on restore the ESP will restart. At this point, the profile is not yet active. Please read the next chapter.
|
|
||||||

|
|
||||||
|
|
||||||
## Selecting a Device Profile
|
|
||||||
|
|
||||||
After you uploaded the device profile you can select the profile in the "Device Manager" view. After a click on "Save" the ESP will be restarted and the pin assignment is active. At this point the display should already show something. Please see the next chapter for display settings.
|
|
||||||

|
|
||||||
|
|
||||||
## Display Settings
|
|
||||||
|
|
||||||
Display settings can also be found in the "Device Manager".
|
|
||||||

|
|
||||||
|
|||||||
@ -1,88 +1,3 @@
|
|||||||
# MQTT Topics
|
# MQTT Topics
|
||||||
|
|
||||||
The base topic, as configured in the web GUI is prepended to all follwing topics.
|
This documentation will has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/firmware/mqtt_topics/>
|
||||||
|
|
||||||
## General topics
|
|
||||||
|
|
||||||
| Topic | R / W | Description | Value / Unit |
|
|
||||||
| --------------------------------------- | ----- | ---------------------------------------------------- | -------------------------- |
|
|
||||||
| dtu/ip | R | IP address of OpenDTU | IP address |
|
|
||||||
| dtu/hostname | R | Current hostname of the dtu (as set in web GUI) | |
|
|
||||||
| dtu/rssi | R | WiFi network quality | db value |
|
|
||||||
| dtu/status | R | Indicates whether OpenDTU network is reachable | online / offline |
|
|
||||||
| dtu/uptime | R | Time in seconds since startup | seconds |
|
|
||||||
|
|
||||||
## Inverter total topics
|
|
||||||
|
|
||||||
Enabled inverter means, that only inverters with "Poll inverter data" enabled are considered.
|
|
||||||
|
|
||||||
| Topic | R / W | Description | Value / Unit |
|
|
||||||
| --------------------------------------- | ----- | ---------------------------------------------------- | -------------------------- |
|
|
||||||
| ac/power | R | Sum of AC active power of all enabled inverters | W |
|
|
||||||
| ac/yieldtotal | R | Sum of energy converted to AC since reset watt hours of all enabled inverters | Kilo watt hours (kWh) |
|
|
||||||
| ac/yieldday | R | Sum of energy converted to AC per day in watt hours of all enabled inverters | Watt hours (Wh)
|
|
||||||
| ac/is_valid | R | Indicator whether all enabled inverters where reachable | 0 or 1 |
|
|
||||||
| dc/power | R | Sum of DC power of all enabled inverters | Watt (W) |
|
|
||||||
| dc/irradiation | R | Produced power of all enabled inverter stripes with defined irradiation settings divided by sum of all enabled inverters irradiation | % |
|
|
||||||
| dc/is_valid | R | Indicator whether all enabled inverters where reachable | 0 or 1 |
|
|
||||||
|
|
||||||
## Inverter specific topics
|
|
||||||
|
|
||||||
serial will be replaced with the serial number of the inverter.
|
|
||||||
|
|
||||||
| Topic | R / W | Description | Value / Unit |
|
|
||||||
| --------------------------------------- | ----- | ---------------------------------------------------- | -------------------------- |
|
|
||||||
| [serial]/name | R | Name of the inverter as configured in web GUI | |
|
|
||||||
| [serial]/device/bootloaderversion | R | Bootloader version of the inverter | |
|
|
||||||
| [serial]/device/fwbuildversion | R | Firmware version of the inverter | |
|
|
||||||
| [serial]/device/fwbuilddatetime | R | Build date / time of inverter firmware | |
|
|
||||||
| [serial]/device/hwpartnumber | R | Hardware part number of the inverter | |
|
|
||||||
| [serial]/device/hwversion | R | Hardware version of the inverter | |
|
|
||||||
| [serial]/status/reachable | R | Indicates whether the inverter is reachable | 0 or 1 |
|
|
||||||
| [serial]/status/producing | R | Indicates whether the inverter is producing AC power | 0 or 1 |
|
|
||||||
| [serial]/status/last_update | R | Unix timestamp of last inverter statistics udpate | seconds since JAN 01 1970 (UTC) |
|
|
||||||
|
|
||||||
### AC channel / global specific topics
|
|
||||||
|
|
||||||
| Topic | R / W | Description | Value / Unit |
|
|
||||||
| --------------------------------------- | ----- | ---------------------------------------------------- | -------------------------- |
|
|
||||||
| [serial]/0/current | R | AC current in ampere | Ampere (A) |
|
|
||||||
| [serial]/0/efficiency | R | Ratio AC Power over DC Power in percent | % |
|
|
||||||
| [serial]/0/frequency | R | AC frequency in hertz | Hertz (Hz) |
|
|
||||||
| [serial]/0/power | R | AC active power in watts | Watt (W) |
|
|
||||||
| [serial]/0/powerdc | R | DC power in watts | Watt (W) |
|
|
||||||
| [serial]/0/powerfactor | R | Power factor in percent | % |
|
|
||||||
| [serial]/0/reactivepower | R | AC reactive power in VAr | VAr |
|
|
||||||
| [serial]/0/temperature | R | Temperature of inverter in degree celsius | Degree Celsius (°C) |
|
|
||||||
| [serial]/0/voltage | R | AC voltage in volt | Volt (V) |
|
|
||||||
| [serial]/0/yieldday | R | Energy converted to AC per day in watt hours | Watt hours (Wh) |
|
|
||||||
| [serial]/0/yieldtotal | R | Energy converted to AC since reset watt hours | Kilo watt hours (kWh) |
|
|
||||||
|
|
||||||
### DC input channel topics
|
|
||||||
|
|
||||||
[1-4] represents the different inputs. The amount depends on the inverter model.
|
|
||||||
|
|
||||||
| Topic | R / W | Description | Value / Unit |
|
|
||||||
| --------------------------------------- | ----- | ---------------------------------------------------- | -------------------------- |
|
|
||||||
| [serial]/[1-4]/current | R | DC current of specific input in ampere | Ampere (A) |
|
|
||||||
| [serial]/[1-4]/name | R | Name of the DC input channel as configured in web GUI| |
|
|
||||||
| [serial]/[1-4]/irradiation | R | Ratio DC Power over set maximum power (in web GUI) | % |
|
|
||||||
| [serial]/[1-4]/power | R | DC power of specific input in watt | Watt (W) |
|
|
||||||
| [serial]/[1-4]/voltage | R | DC voltage of specific input in volt | Volt (V) |
|
|
||||||
| [serial]/[1-4]/yieldday | R | Energy converted to AC per day on specific input | Watt hours (Wh) |
|
|
||||||
| [serial]/[1-4]/yieldtotal | R | Energy converted to AC since reset on specific input | Kilo watt hours (kWh) |
|
|
||||||
|
|
||||||
### Inverter limit specific topics
|
|
||||||
|
|
||||||
cmd topics are used to set values. Status topics are updated from values set in the inverter.
|
|
||||||
|
|
||||||
| Topic | R / W | Description | Value / Unit |
|
|
||||||
| ----------------------------------------- | ----- | ---------------------------------------------------- | -------------------------- |
|
|
||||||
| [serial]/status/limit_relative | R | Current applied production limit of the inverter | % of total possible output |
|
|
||||||
| [serial]/status/limit_absolute | R | Current applied production limit of the inverter | Watt (W) |
|
|
||||||
| [serial]/cmd/limit_persistent_relative | W | Set the inverter limit as a percentage of total production capability. The value will survive the night without power. The updated value will show up in the web GUI and limit_relative topic immediatly. | % |
|
|
||||||
| [serial]/cmd/limit_persistent_absolute | W | Set the inverter limit as a absolute value. The value will survive the night without power. The updated value will set immediatly within the inverter but show up in the web GUI and limit_relative topic after around 4 minutes. If you are using a already known inverter (known Hardware ID), the updated value will show up within a few seconds. | Watt (W) |
|
|
||||||
| [serial]/cmd/limit_nonpersistent_relative | W | Set the inverter limit as a percentage of total production capability. The value will reset to the last persistent value at night without power. The updated value will show up in the web GUI and limit_relative topic immediatly. The value must be published non-retained, otherwise it will be ignored! | % |
|
|
||||||
| [serial]/cmd/limit_nonpersistent_absolute | W | Set the inverter limit as a absolute value. The value will reset to the last persistent value at night without power. The updated value will set immediatly within the inverter but show up in the web GUI and limit_relative topic after around 4 minutes. If you are using a already known inverter (known Hardware ID), the updated value will show up within a few seconds. The value must be published non-retained, otherwise it will be ignored! | Watt (W) |
|
|
||||||
| [serial]/cmd/power | W | Turn the inverter on (1) or off (0) | 0 or 1 |
|
|
||||||
| [serial]/cmd/restart | W | Restarts the inverters (also resets YieldDay) | 1 |
|
|
||||||
|
|||||||
@ -1,21 +1,3 @@
|
|||||||
# Upgrade Partition
|
# Upgrade Partition
|
||||||
|
|
||||||
To be able to install further updates you have to update the partition table of the ESP32. Doing so will **erase** all configuration data. Over The Air update using the web interface is **NOT** possible!
|
This documentation will has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/firmware/howto/upgrade_partition/>
|
||||||
|
|
||||||
**So make sure you export a backup of your configuration files before continuing.**
|
|
||||||
|
|
||||||
There are several possibilities to update the partition table:
|
|
||||||
|
|
||||||
- Using Visual Studio Code or PlatformIO CLI
|
|
||||||
|
|
||||||
If you have already used Visual Studio Code or the `platformio` command you can use it again to install the latest version. The partition table is upgraded automatically.
|
|
||||||
|
|
||||||
- Any kind of flash interface
|
|
||||||
|
|
||||||
If you like to use any kind of flash interface like `esptool.py`, Espressif Flash Download Tool, ESP_Flasher or esptool-js you have to make sure to upload the provided .factory.bin file. It is important to enter the correct target address.
|
|
||||||
|
|
||||||
| Address | File |
|
|
||||||
| ---------| ---------------------- |
|
|
||||||
| 0x0 | opendtu-*.factory.bin |
|
|
||||||
|
|
||||||
After upgrading the ESP32 will open the intergrated access point (AP) again. Just connect to it using the default password ("openDTU42"). If you are connected, just visit <http://192.168.4.1> and enter the "Configuration Management". Recover the previously backuped config files.
|
|
||||||
|
|||||||
563
docs/Web-API.md
563
docs/Web-API.md
@ -1,564 +1,3 @@
|
|||||||
# Web API
|
# Web API
|
||||||
|
|
||||||
Information in JSON format can be obtained through the web API
|
This documentation will has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/firmware/web_api/>
|
||||||
|
|
||||||
## List of URLs
|
|
||||||
|
|
||||||
may be incomplete
|
|
||||||
|
|
||||||
| GET/POST | Auth required | URL |
|
|
||||||
| -------- | --- | -- |
|
|
||||||
| Get | yes | /api/config/get |
|
|
||||||
| Post | yes | /api/config/delete |
|
|
||||||
| Get | yes | /api/config/list |
|
|
||||||
| Post | yes | /api/config/upload |
|
|
||||||
| Get+Post | yes | /api/device/config |
|
|
||||||
| Get | no | /api/devinfo/status |
|
|
||||||
| Get+Post | yes | /api/dtu/config |
|
|
||||||
| Get | no | /api/eventlog/status?inv=inverter-serialnumber |
|
|
||||||
| Post | yes | /api/firmware/update |
|
|
||||||
| Get | yes | /api/inverter/list |
|
|
||||||
| Post | yes | /api/inverter/add |
|
|
||||||
| Post | yes | /api/inverter/del |
|
|
||||||
| Post | yes | /api/inverter/edit |
|
|
||||||
| Post | yes | /api/limit/config |
|
|
||||||
| Get | no | /api/limit/status |
|
|
||||||
| Get | no | /api/livedata/status |
|
|
||||||
| Post | yes | /api/maintenance/reboot |
|
|
||||||
| Get+Post | yes | /api/mqtt/config |
|
|
||||||
| Get | no | /api/mqtt/status |
|
|
||||||
| Get+Post | yes | /api/network/config |
|
|
||||||
| Get | no | /api/network/status |
|
|
||||||
| Get+Post | yes | /api/ntp/config |
|
|
||||||
| Get | no | /api/ntp/status |
|
|
||||||
| Get+Post | yes | /api/ntp/time |
|
|
||||||
| Get | no | /api/power/status |
|
|
||||||
| Post | yes | /api/power/config |
|
|
||||||
| Get | no | /api/prometheus/metrics |
|
|
||||||
| Get+Post | yes | /api/security/config |
|
|
||||||
| Get | yes | /api/security/authenticate |
|
|
||||||
| Get | no | /api/system/status |
|
|
||||||
|
|
||||||
## Examples of Use
|
|
||||||
|
|
||||||
### Important notes
|
|
||||||
|
|
||||||
- IP addresses and serial numbers in this examples are anonymized. Adjust to your own needs.
|
|
||||||
- The output from curl is without a linefeed at the end, so please be careful when copying the output - do not accidentally add the shell prompt directly after it.
|
|
||||||
- When POSTing config data to OpenDTU, always send all settings back, even if only one setting was changed. Sending single settings is not supported and you will receive a response `{"type":"warning","message":"Values are missing!"}`
|
|
||||||
- When POSTing, always put single quotes around the data part. Do not confuse the single quote `'` with the backtick `` ` ``. You have been warned.
|
|
||||||
- Some API calls have a single URL for GET and POST - e.g. `/api/ntp/config`
|
|
||||||
- Other API calls use e.g. `/api/limit/status` to GET data and a different URL `/api/limit/config` to POST data.
|
|
||||||
- If you want to investigate the web api communication, a good tool is [Postman](https://www.postman.com/)
|
|
||||||
- Settings API require username and password provided with Basic Authentication credentials
|
|
||||||
- If you disable the readonly access to the web API, every endpoint requires authentication
|
|
||||||
|
|
||||||
### Get information
|
|
||||||
|
|
||||||
You can "talk" to the OpenDTU with a command line tool like `curl`. The output is in plain JSON, without carriage return/linefeed and is therefore not very human readable.
|
|
||||||
|
|
||||||
#### Get current livedata
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ curl http://192.168.10.10/api/livedata/status
|
|
||||||
{"inverters":[{"serial":"11617160xxxx","name":"Meine Solaranlage","data_age":6983,"reachable":false,"producing":false,"limit_relative":0,"limit_absolute":-1,"AC":{"0":{"Power":{"v":0,"u":"W","d":1},"Voltage":{"v":0,"u":"V","d":1},"Current":{"v":0,"u":"A","d":2},"Power DC":{"v":0,"u":"W","d":1},"YieldDay":{"v":0,"u":"Wh","d":0},"YieldTotal":{"v":0,"u":"kWh","d":3},"Frequency":{"v":0,"u":"Hz","d":2},"PowerFactor":{"v":0,"u":"","d":3},"ReactivePower":{"v":0,"u":"var","d":1},"Efficiency":{"v":0,"u":"%","d":3}}},"DC":{"0":{"name":{"u":""},"Power":{"v":0,"u":"W","d":1},"Voltage":{"v":0,"u":"V","d":1},"Current":{"v":0,"u":"A","d":2},"YieldDay":{"v":0,"u":"Wh","d":0},"YieldTotal":{"v":0,"u":"kWh","d":3},"Irradiation":{"v":0,"u":"%","d":3}},"1":{"name":{"u":""},"Power":{"v":0,"u":"W","d":1},"Voltage":{"v":0,"u":"V","d":1},"Current":{"v":0,"u":"A","d":2},"YieldDay":{"v":0,"u":"Wh","d":0},"YieldTotal":{"v":0,"u":"kWh","d":3},"Irradiation":{"v":0,"u":"%","d":3}},"2":{"name":{"u":""},"Power":{"v":0,"u":"W","d":1},"Voltage":{"v":0,"u":"V","d":1},"Current":{"v":0,"u":"A","d":2},"YieldDay":{"v":0,"u":"Wh","d":0},"YieldTotal":{"v":0,"u":"kWh","d":3},"Irradiation":{"v":0,"u":"%","d":3}},"3":{"name":{"u":""},"Power":{"v":0,"u":"W","d":1},"Voltage":{"v":0,"u":"V","d":1},"Current":{"v":0,"u":"A","d":2},"YieldDay":{"v":0,"u":"Wh","d":0},"YieldTotal":{"v":0,"u":"kWh","d":3}}},"INV":{"0":{"Temperature":{"v":0,"u":"°C","d":1}}},"events":0},{"serial":"11417160xxxx","name":"test","data_age":6983,"reachable":false,"producing":false,"limit_relative":0,"limit_absolute":-1,"AC":{"0":{"Power":{"v":0,"u":"W","d":1},"Voltage":{"v":0,"u":"V","d":1},"Current":{"v":0,"u":"A","d":2},"Power DC":{"v":0,"u":"W","d":1},"YieldDay":{"v":0,"u":"Wh","d":0},"YieldTotal":{"v":0,"u":"kWh","d":3},"Frequency":{"v":0,"u":"Hz","d":2},"PowerFactor":{"v":0,"u":"","d":3},"ReactivePower":{"v":0,"u":"var","d":1},"Efficiency":{"v":0,"u":"%","d":3}}},"DC":{"0":{"name":{"u":"test 1"},"Power":{"v":0,"u":"W","d":1},"Voltage":{"v":0,"u":"V","d":1},"Current":{"v":0,"u":"A","d":2},"YieldDay":{"v":0,"u":"Wh","d":0},"YieldTotal":{"v":0,"u":"kWh","d":3},"Irradiation":{"v":0,"u":"%","d":3}},"1":{"name":{"u":"test 2"},"Power":{"v":0,"u":"W","d":1},"Voltage":{"v":0,"u":"V","d":1},"Current":{"v":0,"u":"A","d":2},"YieldDay":{"v":0,"u":"Wh","d":0},"YieldTotal":{"v":0,"u":"kWh","d":3},"Irradiation":{"v":0,"u":"%","d":3}}},"INV":{"0":{"Temperature":{"v":0,"u":"°C","d":1}}},"events":0}],"total":{"Power":{"v":0,"u":"W","d":1},"YieldDay":{"v":0,"u":"Wh","d":0},"YieldTotal":{"v":0,"u":"kWh","d":2}},"hints":{"time_sync":false,"radio_problem":false,"default_password":false}}
|
|
||||||
```
|
|
||||||
|
|
||||||
To enhance readability (and filter information) use the JSON command line processor `jq`.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ curl --no-progress-meter http://192.168.10.10/api/livedata/status | jq
|
|
||||||
{
|
|
||||||
"inverters": [
|
|
||||||
{
|
|
||||||
"serial": "116171603546",
|
|
||||||
"name": "Meine Solaranlage",
|
|
||||||
"data_age": 7038,
|
|
||||||
"reachable": false,
|
|
||||||
"producing": false,
|
|
||||||
"limit_relative": 0,
|
|
||||||
"limit_absolute": -1,
|
|
||||||
"AC": {
|
|
||||||
"0": {
|
|
||||||
"Power": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "W",
|
|
||||||
"d": 1
|
|
||||||
},
|
|
||||||
"Voltage": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "V",
|
|
||||||
"d": 1
|
|
||||||
},
|
|
||||||
"Current": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "A",
|
|
||||||
"d": 2
|
|
||||||
},
|
|
||||||
"Power DC": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "W",
|
|
||||||
"d": 1
|
|
||||||
},
|
|
||||||
"YieldDay": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "Wh",
|
|
||||||
"d": 0
|
|
||||||
},
|
|
||||||
"YieldTotal": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "kWh",
|
|
||||||
"d": 3
|
|
||||||
},
|
|
||||||
"Frequency": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "Hz",
|
|
||||||
"d": 2
|
|
||||||
},
|
|
||||||
"PowerFactor": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "",
|
|
||||||
"d": 3
|
|
||||||
},
|
|
||||||
"ReactivePower": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "var",
|
|
||||||
"d": 1
|
|
||||||
},
|
|
||||||
"Efficiency": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "%",
|
|
||||||
"d": 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"DC": {
|
|
||||||
"0": {
|
|
||||||
"name": {
|
|
||||||
"u": ""
|
|
||||||
},
|
|
||||||
"Power": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "W",
|
|
||||||
"d": 1
|
|
||||||
},
|
|
||||||
"Voltage": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "V",
|
|
||||||
"d": 1
|
|
||||||
},
|
|
||||||
"Current": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "A",
|
|
||||||
"d": 2
|
|
||||||
},
|
|
||||||
"YieldDay": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "Wh",
|
|
||||||
"d": 0
|
|
||||||
},
|
|
||||||
"YieldTotal": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "kWh",
|
|
||||||
"d": 3
|
|
||||||
},
|
|
||||||
"Irradiation": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "%",
|
|
||||||
"d": 3
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"1": {
|
|
||||||
"name": {
|
|
||||||
"u": ""
|
|
||||||
},
|
|
||||||
"Power": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "W",
|
|
||||||
"d": 1
|
|
||||||
},
|
|
||||||
"Voltage": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "V",
|
|
||||||
"d": 1
|
|
||||||
},
|
|
||||||
"Current": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "A",
|
|
||||||
"d": 2
|
|
||||||
},
|
|
||||||
"YieldDay": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "Wh",
|
|
||||||
"d": 0
|
|
||||||
},
|
|
||||||
"YieldTotal": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "kWh",
|
|
||||||
"d": 3
|
|
||||||
},
|
|
||||||
"Irradiation": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "%",
|
|
||||||
"d": 3
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"name": {
|
|
||||||
"u": ""
|
|
||||||
},
|
|
||||||
"Power": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "W",
|
|
||||||
"d": 1
|
|
||||||
},
|
|
||||||
"Voltage": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "V",
|
|
||||||
"d": 1
|
|
||||||
},
|
|
||||||
"Current": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "A",
|
|
||||||
"d": 2
|
|
||||||
},
|
|
||||||
"YieldDay": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "Wh",
|
|
||||||
"d": 0
|
|
||||||
},
|
|
||||||
"YieldTotal": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "kWh",
|
|
||||||
"d": 3
|
|
||||||
},
|
|
||||||
"Irradiation": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "%",
|
|
||||||
"d": 3
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"name": {
|
|
||||||
"u": ""
|
|
||||||
},
|
|
||||||
"Power": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "W",
|
|
||||||
"d": 1
|
|
||||||
},
|
|
||||||
"Voltage": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "V",
|
|
||||||
"d": 1
|
|
||||||
},
|
|
||||||
"Current": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "A",
|
|
||||||
"d": 2
|
|
||||||
},
|
|
||||||
"YieldDay": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "Wh",
|
|
||||||
"d": 0
|
|
||||||
},
|
|
||||||
"YieldTotal": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "kWh",
|
|
||||||
"d": 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"INV": {
|
|
||||||
"0": {
|
|
||||||
"Temperature": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "°C",
|
|
||||||
"d": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"events": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"serial": "114171603548",
|
|
||||||
"name": "test",
|
|
||||||
"data_age": 7038,
|
|
||||||
"reachable": false,
|
|
||||||
"producing": false,
|
|
||||||
"limit_relative": 0,
|
|
||||||
"limit_absolute": -1,
|
|
||||||
"AC": {
|
|
||||||
"0": {
|
|
||||||
"Power": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "W",
|
|
||||||
"d": 1
|
|
||||||
},
|
|
||||||
"Voltage": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "V",
|
|
||||||
"d": 1
|
|
||||||
},
|
|
||||||
"Current": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "A",
|
|
||||||
"d": 2
|
|
||||||
},
|
|
||||||
"Power DC": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "W",
|
|
||||||
"d": 1
|
|
||||||
},
|
|
||||||
"YieldDay": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "Wh",
|
|
||||||
"d": 0
|
|
||||||
},
|
|
||||||
"YieldTotal": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "kWh",
|
|
||||||
"d": 3
|
|
||||||
},
|
|
||||||
"Frequency": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "Hz",
|
|
||||||
"d": 2
|
|
||||||
},
|
|
||||||
"PowerFactor": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "",
|
|
||||||
"d": 3
|
|
||||||
},
|
|
||||||
"ReactivePower": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "var",
|
|
||||||
"d": 1
|
|
||||||
},
|
|
||||||
"Efficiency": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "%",
|
|
||||||
"d": 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"DC": {
|
|
||||||
"0": {
|
|
||||||
"name": {
|
|
||||||
"u": "test 1"
|
|
||||||
},
|
|
||||||
"Power": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "W",
|
|
||||||
"d": 1
|
|
||||||
},
|
|
||||||
"Voltage": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "V",
|
|
||||||
"d": 1
|
|
||||||
},
|
|
||||||
"Current": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "A",
|
|
||||||
"d": 2
|
|
||||||
},
|
|
||||||
"YieldDay": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "Wh",
|
|
||||||
"d": 0
|
|
||||||
},
|
|
||||||
"YieldTotal": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "kWh",
|
|
||||||
"d": 3
|
|
||||||
},
|
|
||||||
"Irradiation": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "%",
|
|
||||||
"d": 3
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"1": {
|
|
||||||
"name": {
|
|
||||||
"u": "test 2"
|
|
||||||
},
|
|
||||||
"Power": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "W",
|
|
||||||
"d": 1
|
|
||||||
},
|
|
||||||
"Voltage": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "V",
|
|
||||||
"d": 1
|
|
||||||
},
|
|
||||||
"Current": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "A",
|
|
||||||
"d": 2
|
|
||||||
},
|
|
||||||
"YieldDay": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "Wh",
|
|
||||||
"d": 0
|
|
||||||
},
|
|
||||||
"YieldTotal": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "kWh",
|
|
||||||
"d": 3
|
|
||||||
},
|
|
||||||
"Irradiation": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "%",
|
|
||||||
"d": 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"INV": {
|
|
||||||
"0": {
|
|
||||||
"Temperature": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "°C",
|
|
||||||
"d": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"events": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"total": {
|
|
||||||
"Power": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "W",
|
|
||||||
"d": 1
|
|
||||||
},
|
|
||||||
"YieldDay": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "Wh",
|
|
||||||
"d": 0
|
|
||||||
},
|
|
||||||
"YieldTotal": {
|
|
||||||
"v": 0,
|
|
||||||
"u": "kWh",
|
|
||||||
"d": 2
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hints": {
|
|
||||||
"time_sync": false,
|
|
||||||
"radio_problem": false,
|
|
||||||
"default_password": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The eventlog can be fetched with the inverter serial number as parameter:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ curl --no-progress-meter http://192.168.10.10/api/eventlog/status?inv=11418186xxxx | jq
|
|
||||||
{
|
|
||||||
"11418186xxxx": {
|
|
||||||
"count": 4,
|
|
||||||
"events": [
|
|
||||||
{
|
|
||||||
"message_id": 1,
|
|
||||||
"message": "Inverter start",
|
|
||||||
"start_time": 28028,
|
|
||||||
"end_time": 28028
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"message_id": 209,
|
|
||||||
"message": "PV-1: No input",
|
|
||||||
"start_time": 28036,
|
|
||||||
"end_time": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"message_id": 2,
|
|
||||||
"message": "DTU command failed",
|
|
||||||
"start_time": 28092,
|
|
||||||
"end_time": 28092
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"message_id": 207,
|
|
||||||
"message": "MPPT-A: Input undervoltage",
|
|
||||||
"start_time": 28336,
|
|
||||||
"end_time": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### combine curl and jq
|
|
||||||
|
|
||||||
`jq` can filter specific fields from json output.
|
|
||||||
|
|
||||||
For example, filter out the current total power:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ curl --no-progress-meter http://192.168.10.10/api/livedata/status | jq '.total | .Power.v'
|
|
||||||
140.7999878
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Get information where login is required
|
|
||||||
|
|
||||||
When config data is requested, username and password have to be provided to `curl`
|
|
||||||
Username is always `admin`, the default password is `openDTU42`. The password is used for both the admin login and the Admin-mode Access Point.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ curl --u admin:openDTU42 http://192.168.10.10/api/ntp/config
|
|
||||||
{"ntp_server":"pool.ntp.org","ntp_timezone":"CET-1CEST,M3.5.0,M10.5.0/3","ntp_timezone_descr":"Europe/Berlin"}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Post information
|
|
||||||
|
|
||||||
With HTTP POST commands information can be written to the OpenDTU.
|
|
||||||
|
|
||||||
The Web API is designed to allow the web frontend in the web browser to communicate with the OpenDTU software running on the ESP32. It is not designed to be intuitive or user-friendly, so please follow the instructions here.
|
|
||||||
|
|
||||||
#### Example 1: change ntp settings
|
|
||||||
|
|
||||||
If you want to configure the ntp server setting, first fetch the information from the web API:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ curl -u "admin:password" http://192.168.10.10/api/ntp/config
|
|
||||||
{"ntp_server":"pool.ntp.org","ntp_timezone":"CET-1CEST,M3.5.0,M10.5.0/3","ntp_timezone_descr":"Europe/Berlin"}
|
|
||||||
```
|
|
||||||
|
|
||||||
Then, second step, send your new settings. Use the text output from curl in the first step, add `data=` and enclose the whole data with single quotes.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ curl -u "admin:password" http://192.168.10.10/api/ntp/config -d 'data={"ntp_server":"my.own.ntp.server.home","ntp_timezone":"CET-1CEST,M3.5.0,M10.5.0/3","ntp_timezone_descr":"Europe/Berlin"}'
|
|
||||||
{"type":"success","message":"Settings saved!"}
|
|
||||||
```
|
|
||||||
|
|
||||||
You will receive a json formatted response.
|
|
||||||
|
|
||||||
#### Example 2: change power limit
|
|
||||||
|
|
||||||
In the second example, I want to change the non persistent power limit of an inverter. Again, first fetch current data:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ curl http://192.168.10.10/api/limit/status
|
|
||||||
{"11418186xxxx":{"limit_relative":100,"max_power":600,"limit_set_status":"Ok"},"11418180xxxx":{"limit_relative":100,"max_power":800,"limit_set_status":"Ok"}}
|
|
||||||
```
|
|
||||||
|
|
||||||
I see data from two configured inverters.
|
|
||||||
|
|
||||||
Now I set the relative power limit of inverter with serialnumber `11418180xxxx` to 50%.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ curl -u "admin:password" http://192.168.10.10/api/limit/config -d 'data={"serial":"11418180xxxx", "limit_type":1, "limit_value":50}'
|
|
||||||
{"type":"success","message":"Settings saved!"}
|
|
||||||
```
|
|
||||||
|
|
||||||
Then I read again the limit status. In the first answer the status is `pending`, some seconds later it changed to `OK`.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ curl http://192.168.10.10/api/limit/status
|
|
||||||
{"11418186xxxx":{"limit_relative":100,"max_power":600,"limit_set_status":"Ok"},"11418180xxxx":{"limit_relative":100,"max_power":800,"limit_set_status":"Pending"}}
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
$ curl http://192.168.10.10/api/limit/status
|
|
||||||
{"11418186xxxx":{"limit_relative":100,"max_power":600,"limit_set_status":"Ok"},"11418180xxxx":{"limit_relative":50,"max_power":800,"limit_set_status":"Ok"}}
|
|
||||||
```
|
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "PinMapping.h"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#define CONFIG_FILENAME "/config.json"
|
#define CONFIG_FILENAME "/config.json"
|
||||||
#define CONFIG_VERSION 0x00011900 // 0.1.24 // make sure to clean all after change
|
#define CONFIG_VERSION 0x00011c00 // 0.1.28 // make sure to clean all after change
|
||||||
|
|
||||||
#define WIFI_MAX_SSID_STRLEN 32
|
#define WIFI_MAX_SSID_STRLEN 32
|
||||||
#define WIFI_MAX_PASSWORD_STRLEN 64
|
#define WIFI_MAX_PASSWORD_STRLEN 64
|
||||||
@ -15,6 +16,7 @@
|
|||||||
#define NTP_MAX_TIMEZONEDESCR_STRLEN 50
|
#define NTP_MAX_TIMEZONEDESCR_STRLEN 50
|
||||||
|
|
||||||
#define MQTT_MAX_HOSTNAME_STRLEN 128
|
#define MQTT_MAX_HOSTNAME_STRLEN 128
|
||||||
|
#define MQTT_MAX_CLIENTID_STRLEN 64
|
||||||
#define MQTT_MAX_USERNAME_STRLEN 64
|
#define MQTT_MAX_USERNAME_STRLEN 64
|
||||||
#define MQTT_MAX_PASSWORD_STRLEN 64
|
#define MQTT_MAX_PASSWORD_STRLEN 64
|
||||||
#define MQTT_MAX_TOPIC_STRLEN 32
|
#define MQTT_MAX_TOPIC_STRLEN 32
|
||||||
@ -29,8 +31,6 @@
|
|||||||
|
|
||||||
#define DEV_MAX_MAPPING_NAME_STRLEN 63
|
#define DEV_MAX_MAPPING_NAME_STRLEN 63
|
||||||
|
|
||||||
#define JSON_BUFFER_SIZE 12288
|
|
||||||
|
|
||||||
struct CHANNEL_CONFIG_T {
|
struct CHANNEL_CONFIG_T {
|
||||||
uint16_t MaxChannelPower;
|
uint16_t MaxChannelPower;
|
||||||
char Name[CHAN_MAX_NAME_STRLEN];
|
char Name[CHAN_MAX_NAME_STRLEN];
|
||||||
@ -48,74 +48,115 @@ struct INVERTER_CONFIG_T {
|
|||||||
uint8_t ReachableThreshold;
|
uint8_t ReachableThreshold;
|
||||||
bool ZeroRuntimeDataIfUnrechable;
|
bool ZeroRuntimeDataIfUnrechable;
|
||||||
bool ZeroYieldDayOnMidnight;
|
bool ZeroYieldDayOnMidnight;
|
||||||
|
bool ClearEventlogOnMidnight;
|
||||||
|
bool YieldDayCorrection;
|
||||||
CHANNEL_CONFIG_T channel[INV_MAX_CHAN_COUNT];
|
CHANNEL_CONFIG_T channel[INV_MAX_CHAN_COUNT];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CONFIG_T {
|
struct CONFIG_T {
|
||||||
uint32_t Cfg_Version;
|
struct {
|
||||||
uint32_t Cfg_SaveCount;
|
uint32_t Version;
|
||||||
|
uint32_t SaveCount;
|
||||||
|
} Cfg;
|
||||||
|
|
||||||
char WiFi_Ssid[WIFI_MAX_SSID_STRLEN + 1];
|
struct {
|
||||||
char WiFi_Password[WIFI_MAX_PASSWORD_STRLEN + 1];
|
char Ssid[WIFI_MAX_SSID_STRLEN + 1];
|
||||||
uint8_t WiFi_Ip[4];
|
char Password[WIFI_MAX_PASSWORD_STRLEN + 1];
|
||||||
uint8_t WiFi_Netmask[4];
|
uint8_t Ip[4];
|
||||||
uint8_t WiFi_Gateway[4];
|
uint8_t Netmask[4];
|
||||||
uint8_t WiFi_Dns1[4];
|
uint8_t Gateway[4];
|
||||||
uint8_t WiFi_Dns2[4];
|
uint8_t Dns1[4];
|
||||||
bool WiFi_Dhcp;
|
uint8_t Dns2[4];
|
||||||
char WiFi_Hostname[WIFI_MAX_HOSTNAME_STRLEN + 1];
|
bool Dhcp;
|
||||||
uint32_t WiFi_ApTimeout;
|
char Hostname[WIFI_MAX_HOSTNAME_STRLEN + 1];
|
||||||
|
uint32_t ApTimeout;
|
||||||
|
} WiFi;
|
||||||
|
|
||||||
char Ntp_Server[NTP_MAX_SERVER_STRLEN + 1];
|
struct {
|
||||||
char Ntp_Timezone[NTP_MAX_TIMEZONE_STRLEN + 1];
|
bool Enabled;
|
||||||
char Ntp_TimezoneDescr[NTP_MAX_TIMEZONEDESCR_STRLEN + 1];
|
} Mdns;
|
||||||
double Ntp_Longitude;
|
|
||||||
double Ntp_Latitude;
|
|
||||||
uint8_t Ntp_SunsetType;
|
|
||||||
|
|
||||||
bool Mqtt_Enabled;
|
struct {
|
||||||
char Mqtt_Hostname[MQTT_MAX_HOSTNAME_STRLEN + 1];
|
char Server[NTP_MAX_SERVER_STRLEN + 1];
|
||||||
uint32_t Mqtt_Port;
|
char Timezone[NTP_MAX_TIMEZONE_STRLEN + 1];
|
||||||
char Mqtt_Username[MQTT_MAX_USERNAME_STRLEN + 1];
|
char TimezoneDescr[NTP_MAX_TIMEZONEDESCR_STRLEN + 1];
|
||||||
char Mqtt_Password[MQTT_MAX_PASSWORD_STRLEN + 1];
|
double Longitude;
|
||||||
char Mqtt_Topic[MQTT_MAX_TOPIC_STRLEN + 1];
|
double Latitude;
|
||||||
bool Mqtt_Retain;
|
uint8_t SunsetType;
|
||||||
char Mqtt_LwtTopic[MQTT_MAX_TOPIC_STRLEN + 1];
|
} Ntp;
|
||||||
char Mqtt_LwtValue_Online[MQTT_MAX_LWTVALUE_STRLEN + 1];
|
|
||||||
char Mqtt_LwtValue_Offline[MQTT_MAX_LWTVALUE_STRLEN + 1];
|
|
||||||
uint32_t Mqtt_PublishInterval;
|
|
||||||
bool Mqtt_CleanSession;
|
|
||||||
|
|
||||||
bool Mqtt_Hass_Enabled;
|
struct {
|
||||||
bool Mqtt_Hass_Retain;
|
bool Enabled;
|
||||||
char Mqtt_Hass_Topic[MQTT_MAX_TOPIC_STRLEN + 1];
|
char Hostname[MQTT_MAX_HOSTNAME_STRLEN + 1];
|
||||||
bool Mqtt_Hass_IndividualPanels;
|
uint32_t Port;
|
||||||
bool Mqtt_Hass_Expire;
|
char ClientId[MQTT_MAX_CLIENTID_STRLEN + 1];
|
||||||
|
char Username[MQTT_MAX_USERNAME_STRLEN + 1];
|
||||||
|
char Password[MQTT_MAX_PASSWORD_STRLEN + 1];
|
||||||
|
char Topic[MQTT_MAX_TOPIC_STRLEN + 1];
|
||||||
|
bool Retain;
|
||||||
|
uint32_t PublishInterval;
|
||||||
|
bool CleanSession;
|
||||||
|
|
||||||
bool Mqtt_Tls;
|
struct {
|
||||||
char Mqtt_RootCaCert[MQTT_MAX_CERT_STRLEN + 1];
|
char Topic[MQTT_MAX_TOPIC_STRLEN + 1];
|
||||||
bool Mqtt_TlsCertLogin;
|
char Value_Online[MQTT_MAX_LWTVALUE_STRLEN + 1];
|
||||||
char Mqtt_ClientCert[MQTT_MAX_CERT_STRLEN + 1];
|
char Value_Offline[MQTT_MAX_LWTVALUE_STRLEN + 1];
|
||||||
char Mqtt_ClientKey[MQTT_MAX_CERT_STRLEN + 1];
|
uint8_t Qos;
|
||||||
|
} Lwt;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool Enabled;
|
||||||
|
bool Retain;
|
||||||
|
char Topic[MQTT_MAX_TOPIC_STRLEN + 1];
|
||||||
|
bool IndividualPanels;
|
||||||
|
bool Expire;
|
||||||
|
} Hass;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool Enabled;
|
||||||
|
char RootCaCert[MQTT_MAX_CERT_STRLEN + 1];
|
||||||
|
bool CertLogin;
|
||||||
|
char ClientCert[MQTT_MAX_CERT_STRLEN + 1];
|
||||||
|
char ClientKey[MQTT_MAX_CERT_STRLEN + 1];
|
||||||
|
} Tls;
|
||||||
|
} Mqtt;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint64_t Serial;
|
||||||
|
uint32_t PollInterval;
|
||||||
|
struct {
|
||||||
|
uint8_t PaLevel;
|
||||||
|
} Nrf;
|
||||||
|
struct {
|
||||||
|
int8_t PaLevel;
|
||||||
|
uint32_t Frequency;
|
||||||
|
uint8_t CountryMode;
|
||||||
|
} Cmt;
|
||||||
|
} Dtu;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
char Password[WIFI_MAX_PASSWORD_STRLEN + 1];
|
||||||
|
bool AllowReadonly;
|
||||||
|
} Security;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool PowerSafe;
|
||||||
|
bool ScreenSaver;
|
||||||
|
uint8_t Rotation;
|
||||||
|
uint8_t Contrast;
|
||||||
|
uint8_t Language;
|
||||||
|
struct {
|
||||||
|
uint32_t Duration;
|
||||||
|
uint8_t Mode;
|
||||||
|
} Diagram;
|
||||||
|
} Display;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint8_t Brightness;
|
||||||
|
} Led_Single[PINMAPPING_LED_COUNT];
|
||||||
|
|
||||||
INVERTER_CONFIG_T Inverter[INV_MAX_COUNT];
|
INVERTER_CONFIG_T Inverter[INV_MAX_COUNT];
|
||||||
|
|
||||||
uint64_t Dtu_Serial;
|
|
||||||
uint32_t Dtu_PollInterval;
|
|
||||||
uint8_t Dtu_NrfPaLevel;
|
|
||||||
int8_t Dtu_CmtPaLevel;
|
|
||||||
uint32_t Dtu_CmtFrequency;
|
|
||||||
|
|
||||||
char Security_Password[WIFI_MAX_PASSWORD_STRLEN + 1];
|
|
||||||
bool Security_AllowReadonly;
|
|
||||||
|
|
||||||
char Dev_PinMapping[DEV_MAX_MAPPING_NAME_STRLEN + 1];
|
char Dev_PinMapping[DEV_MAX_MAPPING_NAME_STRLEN + 1];
|
||||||
|
|
||||||
bool Display_PowerSafe;
|
|
||||||
bool Display_ScreenSaver;
|
|
||||||
uint8_t Display_Rotation;
|
|
||||||
uint8_t Display_Contrast;
|
|
||||||
uint8_t Display_Language;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConfigurationClass {
|
class ConfigurationClass {
|
||||||
@ -127,7 +168,8 @@ public:
|
|||||||
CONFIG_T& get();
|
CONFIG_T& get();
|
||||||
|
|
||||||
INVERTER_CONFIG_T* getFreeInverterSlot();
|
INVERTER_CONFIG_T* getFreeInverterSlot();
|
||||||
INVERTER_CONFIG_T* getInverterConfig(uint64_t serial);
|
INVERTER_CONFIG_T* getInverterConfig(const uint64_t serial);
|
||||||
|
void deleteInverterById(const uint8_t id);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern ConfigurationClass Configuration;
|
extern ConfigurationClass Configuration;
|
||||||
@ -1,13 +1,13 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <TimeoutHelper.h>
|
#include <TaskSchedulerDeclarations.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
class DatastoreClass {
|
class DatastoreClass {
|
||||||
public:
|
public:
|
||||||
void init();
|
DatastoreClass();
|
||||||
void loop();
|
void init(Scheduler& scheduler);
|
||||||
|
|
||||||
// Sum of yield total of all enabled inverters, a inverter which is just disabled at night is also included
|
// Sum of yield total of all enabled inverters, a inverter which is just disabled at night is also included
|
||||||
float getTotalAcYieldTotalEnabled();
|
float getTotalAcYieldTotalEnabled();
|
||||||
@ -58,7 +58,10 @@ public:
|
|||||||
bool getIsAllEnabledReachable();
|
bool getIsAllEnabledReachable();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TimeoutHelper _updateTimeout;
|
void loop();
|
||||||
|
|
||||||
|
Task _loopTask;
|
||||||
|
|
||||||
std::mutex _mutex;
|
std::mutex _mutex;
|
||||||
|
|
||||||
float _totalAcYieldTotalEnabled = 0;
|
float _totalAcYieldTotalEnabled = 0;
|
||||||
|
|||||||
@ -1,14 +1,34 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Display_Graphic_Diagram.h"
|
||||||
#include "defaults.h"
|
#include "defaults.h"
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
#include <U8g2lib.h>
|
#include <U8g2lib.h>
|
||||||
|
|
||||||
|
#define CHART_HEIGHT 20 // chart area hight in pixels
|
||||||
|
#define CHART_WIDTH 47 // chart area width in pixels
|
||||||
|
|
||||||
|
// Left-Upper position of diagram is drawn
|
||||||
|
// (text of Y-axis is display left of that pos)
|
||||||
|
#define CHART_POSX 80
|
||||||
|
#define CHART_POSY 0
|
||||||
|
|
||||||
enum DisplayType_t {
|
enum DisplayType_t {
|
||||||
None,
|
None,
|
||||||
PCD8544,
|
PCD8544,
|
||||||
SSD1306,
|
SSD1306,
|
||||||
SH1106,
|
SH1106,
|
||||||
|
SSD1309,
|
||||||
|
ST7567_GM12864I_59N,
|
||||||
|
DisplayType_Max,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum DiagramMode_t {
|
||||||
|
Off,
|
||||||
|
Small,
|
||||||
|
Fullscreen,
|
||||||
|
DisplayMode_Max,
|
||||||
};
|
};
|
||||||
|
|
||||||
class DisplayGraphicClass {
|
class DisplayGraphicClass {
|
||||||
@ -16,32 +36,39 @@ public:
|
|||||||
DisplayGraphicClass();
|
DisplayGraphicClass();
|
||||||
~DisplayGraphicClass();
|
~DisplayGraphicClass();
|
||||||
|
|
||||||
void init(DisplayType_t type, uint8_t data, uint8_t clk, uint8_t cs, uint8_t reset);
|
void init(Scheduler& scheduler, const DisplayType_t type, const uint8_t data, const uint8_t clk, const uint8_t cs, const uint8_t reset);
|
||||||
void loop();
|
void setContrast(const uint8_t contrast);
|
||||||
void setContrast(uint8_t contrast);
|
void setStatus(const bool turnOn);
|
||||||
void setStatus(bool turnOn);
|
void setOrientation(const uint8_t rotation = DISPLAY_ROTATION);
|
||||||
void setOrientation(uint8_t rotation = DISPLAY_ROTATION);
|
void setLanguage(const uint8_t language);
|
||||||
void setLanguage(uint8_t language);
|
void setDiagramMode(DiagramMode_t mode);
|
||||||
void setStartupDisplay();
|
void setStartupDisplay();
|
||||||
|
|
||||||
|
DisplayGraphicDiagramClass& Diagram();
|
||||||
|
|
||||||
bool enablePowerSafe = true;
|
bool enablePowerSafe = true;
|
||||||
bool enableScreensaver = true;
|
bool enableScreensaver = true;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void printText(const char* text, uint8_t line);
|
void loop();
|
||||||
|
void printText(const char* text, const uint8_t line);
|
||||||
void calcLineHeights();
|
void calcLineHeights();
|
||||||
void setFont(uint8_t line);
|
void setFont(const uint8_t line);
|
||||||
|
bool isValidDisplay();
|
||||||
|
|
||||||
|
Task _loopTask;
|
||||||
|
|
||||||
U8G2* _display;
|
U8G2* _display;
|
||||||
|
DisplayGraphicDiagramClass _diagram;
|
||||||
|
|
||||||
bool _displayTurnedOn;
|
bool _displayTurnedOn;
|
||||||
|
|
||||||
DisplayType_t _display_type = DisplayType_t::None;
|
DisplayType_t _display_type = DisplayType_t::None;
|
||||||
|
DiagramMode_t _diagram_mode = DiagramMode_t::Off;
|
||||||
uint8_t _display_language = DISPLAY_LANGUAGE;
|
uint8_t _display_language = DISPLAY_LANGUAGE;
|
||||||
uint8_t _mExtra;
|
uint8_t _mExtra;
|
||||||
uint16_t _period = 1000;
|
const uint16_t _period = 1000;
|
||||||
uint16_t _interval = 60000; // interval at which to power save (milliseconds)
|
const uint16_t _interval = 60000; // interval at which to power save (milliseconds)
|
||||||
uint32_t _lastDisplayUpdate = 0;
|
|
||||||
uint32_t _previousMillis = 0;
|
uint32_t _previousMillis = 0;
|
||||||
char _fmtText[32];
|
char _fmtText[32];
|
||||||
bool _isLarge = false;
|
bool _isLarge = false;
|
||||||
|
|||||||
36
include/Display_Graphic_Diagram.h
Normal file
36
include/Display_Graphic_Diagram.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
#include <U8g2lib.h>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#define MAX_DATAPOINTS 128
|
||||||
|
|
||||||
|
class DisplayGraphicDiagramClass {
|
||||||
|
public:
|
||||||
|
DisplayGraphicDiagramClass();
|
||||||
|
|
||||||
|
void init(Scheduler& scheduler, U8G2* display);
|
||||||
|
void redraw(uint8_t screenSaverOffsetX, uint8_t xPos, uint8_t yPos, uint8_t width, uint8_t height, bool isFullscreen);
|
||||||
|
|
||||||
|
void updatePeriod();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void averageLoop();
|
||||||
|
void dataPointLoop();
|
||||||
|
|
||||||
|
uint32_t getSecondsPerDot();
|
||||||
|
|
||||||
|
Task _averageTask;
|
||||||
|
Task _dataPointTask;
|
||||||
|
|
||||||
|
U8G2* _display = nullptr;
|
||||||
|
std::array<float, MAX_DATAPOINTS> _graphValues = {};
|
||||||
|
uint8_t _graphValuesCount = 0;
|
||||||
|
|
||||||
|
uint8_t _chartWidth = MAX_DATAPOINTS;
|
||||||
|
|
||||||
|
float _iRunningAverage = 0;
|
||||||
|
uint16_t _iRunningAverageCnt = 0;
|
||||||
|
};
|
||||||
@ -1,15 +1,22 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
#define INVERTER_UPDATE_SETTINGS_INTERVAL 60000l
|
||||||
|
|
||||||
class InverterSettingsClass {
|
class InverterSettingsClass {
|
||||||
public:
|
public:
|
||||||
void init();
|
InverterSettingsClass();
|
||||||
void loop();
|
void init(Scheduler& scheduler);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t _lastUpdate = 0;
|
void settingsLoop();
|
||||||
|
void hoyLoop();
|
||||||
|
|
||||||
|
Task _settingsTask;
|
||||||
|
Task _hoyTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern InverterSettingsClass InverterSettings;
|
extern InverterSettingsClass InverterSettings;
|
||||||
@ -2,38 +2,38 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "PinMapping.h"
|
#include "PinMapping.h"
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
#include <TimeoutHelper.h>
|
#include <TimeoutHelper.h>
|
||||||
|
|
||||||
#define LEDSINGLE_UPDATE_INTERVAL 2000
|
#define LEDSINGLE_UPDATE_INTERVAL 2000
|
||||||
|
|
||||||
enum eLedFunction {
|
|
||||||
CONNECTED_NETWORK,
|
|
||||||
CONNECTED_MQTT,
|
|
||||||
INV_REACHABLE,
|
|
||||||
INV_PRODUCING,
|
|
||||||
};
|
|
||||||
|
|
||||||
class LedSingleClass {
|
class LedSingleClass {
|
||||||
public:
|
public:
|
||||||
LedSingleClass();
|
LedSingleClass();
|
||||||
void init();
|
void init(Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
void turnAllOff();
|
void turnAllOff();
|
||||||
void turnAllOn();
|
void turnAllOn();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void setLoop();
|
||||||
|
void outputLoop();
|
||||||
|
|
||||||
|
void setLed(const uint8_t ledNo, const bool ledState);
|
||||||
|
|
||||||
|
Task _setTask;
|
||||||
|
Task _outputTask;
|
||||||
|
|
||||||
enum class LedState_t {
|
enum class LedState_t {
|
||||||
On,
|
On,
|
||||||
Off,
|
Off,
|
||||||
Blink,
|
Blink,
|
||||||
};
|
};
|
||||||
|
|
||||||
LedState_t _ledState[PINMAPPING_LED_COUNT];
|
LedState_t _ledMode[PINMAPPING_LED_COUNT];
|
||||||
LedState_t _allState;
|
LedState_t _allMode;
|
||||||
TimeoutHelper _updateTimeout;
|
bool _ledStateCurrent[PINMAPPING_LED_COUNT];
|
||||||
TimeoutHelper _blinkTimeout;
|
TimeoutHelper _blinkTimeout;
|
||||||
uint8_t _ledActive = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern LedSingleClass LedSingle;
|
extern LedSingleClass LedSingle;
|
||||||
@ -4,19 +4,25 @@
|
|||||||
#include <AsyncWebSocket.h>
|
#include <AsyncWebSocket.h>
|
||||||
#include <HardwareSerial.h>
|
#include <HardwareSerial.h>
|
||||||
#include <Stream.h>
|
#include <Stream.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#define BUFFER_SIZE 500
|
#define BUFFER_SIZE 500
|
||||||
|
|
||||||
class MessageOutputClass : public Print {
|
class MessageOutputClass : public Print {
|
||||||
public:
|
public:
|
||||||
void loop();
|
MessageOutputClass();
|
||||||
|
void init(Scheduler& scheduler);
|
||||||
size_t write(uint8_t c) override;
|
size_t write(uint8_t c) override;
|
||||||
size_t write(const uint8_t* buffer, size_t size) override;
|
size_t write(const uint8_t* buffer, size_t size) override;
|
||||||
void register_ws_output(AsyncWebSocket* output);
|
void register_ws_output(AsyncWebSocket* output);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AsyncWebSocket* _ws = NULL;
|
void loop();
|
||||||
|
|
||||||
|
Task _loopTask;
|
||||||
|
|
||||||
|
AsyncWebSocket* _ws = nullptr;
|
||||||
char _buffer[BUFFER_SIZE];
|
char _buffer[BUFFER_SIZE];
|
||||||
uint16_t _buff_pos = 0;
|
uint16_t _buff_pos = 0;
|
||||||
uint32_t _lastSend = 0;
|
uint32_t _lastSend = 0;
|
||||||
|
|||||||
@ -1,15 +1,18 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
class MqttHandleDtuClass {
|
class MqttHandleDtuClass {
|
||||||
public:
|
public:
|
||||||
void init();
|
MqttHandleDtuClass();
|
||||||
void loop();
|
void init(Scheduler& scheduler);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t _lastPublish = 0;
|
void loop();
|
||||||
|
|
||||||
|
Task _loopTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern MqttHandleDtuClass MqttHandleDtu;
|
extern MqttHandleDtuClass MqttHandleDtu;
|
||||||
@ -3,31 +3,45 @@
|
|||||||
|
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <Hoymiles.h>
|
#include <Hoymiles.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
// mqtt discovery device classes
|
// mqtt discovery device classes
|
||||||
enum {
|
enum DeviceClassType {
|
||||||
DEVICE_CLS_NONE = 0,
|
DEVICE_CLS_NONE = 0,
|
||||||
DEVICE_CLS_CURRENT,
|
DEVICE_CLS_CURRENT,
|
||||||
DEVICE_CLS_ENERGY,
|
DEVICE_CLS_ENERGY,
|
||||||
DEVICE_CLS_PWR,
|
DEVICE_CLS_PWR,
|
||||||
DEVICE_CLS_VOLTAGE,
|
DEVICE_CLS_VOLTAGE,
|
||||||
DEVICE_CLS_FREQ,
|
DEVICE_CLS_FREQ,
|
||||||
DEVICE_CLS_TEMP,
|
|
||||||
DEVICE_CLS_POWER_FACTOR,
|
DEVICE_CLS_POWER_FACTOR,
|
||||||
DEVICE_CLS_REACTIVE_POWER
|
DEVICE_CLS_REACTIVE_POWER,
|
||||||
|
DEVICE_CLS_CONNECTIVITY,
|
||||||
|
DEVICE_CLS_DURATION,
|
||||||
|
DEVICE_CLS_SIGNAL_STRENGTH,
|
||||||
|
DEVICE_CLS_TEMPERATURE,
|
||||||
|
DEVICE_CLS_RESTART
|
||||||
};
|
};
|
||||||
const char* const deviceClasses[] = { 0, "current", "energy", "power", "voltage", "frequency", "temperature", "power_factor", "reactive_power" };
|
const char* const deviceClass_name[] = { 0, "current", "energy", "power", "voltage", "frequency", "power_factor", "reactive_power", "connectivity", "duration", "signal_strength", "temperature", "restart" };
|
||||||
enum {
|
|
||||||
|
enum StateClassType {
|
||||||
STATE_CLS_NONE = 0,
|
STATE_CLS_NONE = 0,
|
||||||
STATE_CLS_MEASUREMENT,
|
STATE_CLS_MEASUREMENT,
|
||||||
STATE_CLS_TOTAL_INCREASING
|
STATE_CLS_TOTAL_INCREASING
|
||||||
};
|
};
|
||||||
const char* const stateClasses[] = { 0, "measurement", "total_increasing" };
|
const char* const stateClass_name[] = { 0, "measurement", "total_increasing" };
|
||||||
|
|
||||||
|
enum CategoryType {
|
||||||
|
CATEGORY_NONE = 0,
|
||||||
|
CATEGORY_CONFIG,
|
||||||
|
CATEGORY_DIAGNOSTIC
|
||||||
|
};
|
||||||
|
const char* const category_name[] = { 0, "config", "diagnostic" };
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
FieldId_t fieldId; // field id
|
FieldId_t fieldId; // field id
|
||||||
uint8_t deviceClsId; // device class
|
DeviceClassType deviceClsId; // device class
|
||||||
uint8_t stateClsId; // state class
|
StateClassType stateClsId; // state class
|
||||||
} byteAssign_fieldDeviceClass_t;
|
} byteAssign_fieldDeviceClass_t;
|
||||||
|
|
||||||
const byteAssign_fieldDeviceClass_t deviceFieldAssignment[] = {
|
const byteAssign_fieldDeviceClass_t deviceFieldAssignment[] = {
|
||||||
@ -40,7 +54,7 @@ const byteAssign_fieldDeviceClass_t deviceFieldAssignment[] = {
|
|||||||
{ FLD_IAC, DEVICE_CLS_CURRENT, STATE_CLS_MEASUREMENT },
|
{ FLD_IAC, DEVICE_CLS_CURRENT, STATE_CLS_MEASUREMENT },
|
||||||
{ FLD_PAC, DEVICE_CLS_PWR, STATE_CLS_MEASUREMENT },
|
{ FLD_PAC, DEVICE_CLS_PWR, STATE_CLS_MEASUREMENT },
|
||||||
{ FLD_F, DEVICE_CLS_FREQ, STATE_CLS_MEASUREMENT },
|
{ FLD_F, DEVICE_CLS_FREQ, STATE_CLS_MEASUREMENT },
|
||||||
{ FLD_T, DEVICE_CLS_TEMP, STATE_CLS_MEASUREMENT },
|
{ FLD_T, DEVICE_CLS_TEMPERATURE, STATE_CLS_MEASUREMENT },
|
||||||
{ FLD_PF, DEVICE_CLS_POWER_FACTOR, STATE_CLS_MEASUREMENT },
|
{ FLD_PF, DEVICE_CLS_POWER_FACTOR, STATE_CLS_MEASUREMENT },
|
||||||
{ FLD_EFF, DEVICE_CLS_NONE, STATE_CLS_NONE },
|
{ FLD_EFF, DEVICE_CLS_NONE, STATE_CLS_NONE },
|
||||||
{ FLD_IRR, DEVICE_CLS_NONE, STATE_CLS_NONE },
|
{ FLD_IRR, DEVICE_CLS_NONE, STATE_CLS_NONE },
|
||||||
@ -50,18 +64,41 @@ const byteAssign_fieldDeviceClass_t deviceFieldAssignment[] = {
|
|||||||
|
|
||||||
class MqttHandleHassClass {
|
class MqttHandleHassClass {
|
||||||
public:
|
public:
|
||||||
void init();
|
MqttHandleHassClass();
|
||||||
void loop();
|
void init(Scheduler& scheduler);
|
||||||
void publishConfig();
|
void publishConfig();
|
||||||
void forceUpdate();
|
void forceUpdate();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void publish(const String& subtopic, const String& payload);
|
void loop();
|
||||||
void publishField(std::shared_ptr<InverterAbstract> inv, ChannelType_t type, ChannelNum_t channel, byteAssign_fieldDeviceClass_t fieldType, bool clear = false);
|
static void publish(const String& subtopic, const String& payload);
|
||||||
void publishInverterButton(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* icon, const char* category, const char* deviceClass, const char* subTopic, const char* payload);
|
static void publish(const String& subtopic, const JsonDocument& doc);
|
||||||
void publishInverterNumber(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* icon, const char* category, const char* commandTopic, const char* stateTopic, const char* unitOfMeasure, int16_t min = 1, int16_t max = 100);
|
|
||||||
void publishInverterBinarySensor(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* device_class, const char* category, const char* subTopic, const char* payload_on, const char* payload_off);
|
static void addCommonMetadata(JsonDocument& doc, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category);
|
||||||
void createDeviceInfo(JsonObject& object, std::shared_ptr<InverterAbstract> inv);
|
|
||||||
|
// Binary Sensor
|
||||||
|
static void publishBinarySensor(JsonDocument& doc, const String& root_device, const String& unique_id_prefix, const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category);
|
||||||
|
static void publishDtuBinarySensor(const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category);
|
||||||
|
static void publishInverterBinarySensor(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category);
|
||||||
|
|
||||||
|
// Sensor
|
||||||
|
static void publishSensor(JsonDocument& doc, const String& root_device, const String& unique_id_prefix, const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category);
|
||||||
|
static void publishDtuSensor(const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category);
|
||||||
|
static void publishInverterSensor(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category);
|
||||||
|
|
||||||
|
static void publishInverterField(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const byteAssign_fieldDeviceClass_t fieldType, const bool clear = false);
|
||||||
|
static void publishInverterButton(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& payload, const String& icon, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category);
|
||||||
|
static void publishInverterNumber(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& command_topic, const int16_t min, const int16_t max, float step, const String& unit_of_measure, const String& icon, const StateClassType state_class, const CategoryType category);
|
||||||
|
|
||||||
|
static void createInverterInfo(JsonDocument& doc, std::shared_ptr<InverterAbstract> inv);
|
||||||
|
static void createDtuInfo(JsonDocument& doc);
|
||||||
|
|
||||||
|
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;
|
bool _wasConnected = false;
|
||||||
bool _updateForced = false;
|
bool _updateForced = false;
|
||||||
|
|||||||
@ -3,22 +3,28 @@
|
|||||||
|
|
||||||
#include "Configuration.h"
|
#include "Configuration.h"
|
||||||
#include <Hoymiles.h>
|
#include <Hoymiles.h>
|
||||||
#include <TimeoutHelper.h>
|
#include <TaskSchedulerDeclarations.h>
|
||||||
#include <espMqttClient.h>
|
#include <espMqttClient.h>
|
||||||
|
#include <frozen/map.h>
|
||||||
|
#include <frozen/string.h>
|
||||||
|
|
||||||
class MqttHandleInverterClass {
|
class MqttHandleInverterClass {
|
||||||
public:
|
public:
|
||||||
void init();
|
MqttHandleInverterClass();
|
||||||
void loop();
|
void init(Scheduler& scheduler);
|
||||||
|
|
||||||
static String getTopic(std::shared_ptr<InverterAbstract> inv, ChannelType_t type, ChannelNum_t channel, FieldId_t fieldId);
|
static String getTopic(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId);
|
||||||
|
|
||||||
|
void subscribeTopics();
|
||||||
|
void unsubscribeTopics();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void publishField(std::shared_ptr<InverterAbstract> inv, ChannelType_t type, ChannelNum_t channel, FieldId_t fieldId);
|
void loop();
|
||||||
void onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total);
|
void publishField(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId);
|
||||||
|
|
||||||
|
Task _loopTask;
|
||||||
|
|
||||||
uint32_t _lastPublishStats[INV_MAX_COUNT] = { 0 };
|
uint32_t _lastPublishStats[INV_MAX_COUNT] = { 0 };
|
||||||
uint32_t _lastPublish = 0;
|
|
||||||
|
|
||||||
FieldId_t _publishFields[14] = {
|
FieldId_t _publishFields[14] = {
|
||||||
FLD_UDC,
|
FLD_UDC,
|
||||||
@ -36,6 +42,29 @@ private:
|
|||||||
FLD_IRR,
|
FLD_IRR,
|
||||||
FLD_Q
|
FLD_Q
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class Topic : unsigned {
|
||||||
|
LimitPersistentRelative,
|
||||||
|
LimitPersistentAbsolute,
|
||||||
|
LimitNonPersistentRelative,
|
||||||
|
LimitNonPersistentAbsolute,
|
||||||
|
Power,
|
||||||
|
Restart,
|
||||||
|
ResetRfStats,
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr frozen::string _cmdtopic = "+/cmd/";
|
||||||
|
static constexpr frozen::map<frozen::string, Topic, 7> _subscriptions = {
|
||||||
|
{ "limit_persistent_relative", Topic::LimitPersistentRelative },
|
||||||
|
{ "limit_persistent_absolute", Topic::LimitPersistentAbsolute },
|
||||||
|
{ "limit_nonpersistent_relative", Topic::LimitNonPersistentRelative },
|
||||||
|
{ "limit_nonpersistent_absolute", Topic::LimitNonPersistentAbsolute },
|
||||||
|
{ "power", Topic::Power },
|
||||||
|
{ "restart", Topic::Restart },
|
||||||
|
{ "reset_rf_stats", Topic::ResetRfStats },
|
||||||
|
};
|
||||||
|
|
||||||
|
void onMqttMessage(Topic t, const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, const size_t len, const size_t index, const size_t total);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern MqttHandleInverterClass MqttHandleInverter;
|
extern MqttHandleInverterClass MqttHandleInverter;
|
||||||
@ -1,15 +1,17 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <TimeoutHelper.h>
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class MqttHandleInverterTotalClass {
|
class MqttHandleInverterTotalClass {
|
||||||
public:
|
public:
|
||||||
void init();
|
MqttHandleInverterTotalClass();
|
||||||
void loop();
|
void init(Scheduler& scheduler);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TimeoutHelper _lastPublish;
|
void loop();
|
||||||
|
|
||||||
|
Task _loopTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern MqttHandleInverterTotalClass MqttHandleInverterTotal;
|
extern MqttHandleInverterTotalClass MqttHandleInverterTotal;
|
||||||
@ -14,29 +14,28 @@ public:
|
|||||||
void performReconnect();
|
void performReconnect();
|
||||||
bool getConnected();
|
bool getConnected();
|
||||||
void publish(const String& subtopic, const String& payload);
|
void publish(const String& subtopic, const String& payload);
|
||||||
void publishGeneric(const String& topic, const String& payload, bool retain, uint8_t qos = 0);
|
void publishGeneric(const String& topic, const String& payload, const bool retain, const uint8_t qos = 0);
|
||||||
|
|
||||||
void subscribe(const String& topic, uint8_t qos, const espMqttClientTypes::OnMessageCallback& cb);
|
void subscribe(const String& topic, const uint8_t qos, const espMqttClientTypes::OnMessageCallback& cb);
|
||||||
void unsubscribe(const String& topic);
|
void unsubscribe(const String& topic);
|
||||||
|
|
||||||
String getPrefix();
|
String getPrefix() const;
|
||||||
|
String getClientId();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void NetworkEvent(network_event event);
|
void NetworkEvent(network_event event);
|
||||||
|
|
||||||
void onMqttDisconnect(espMqttClientTypes::DisconnectReason reason);
|
void onMqttDisconnect(espMqttClientTypes::DisconnectReason reason);
|
||||||
void onMqttConnect(bool sessionPresent);
|
void onMqttConnect(const bool sessionPresent);
|
||||||
void onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total);
|
void onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, const size_t len, const size_t index, const size_t total);
|
||||||
|
|
||||||
void performConnect();
|
void performConnect();
|
||||||
void performDisconnect();
|
void performDisconnect();
|
||||||
|
|
||||||
void createMqttClientObject();
|
void createMqttClientObject();
|
||||||
|
|
||||||
MqttClient* mqttClient = nullptr;
|
MqttClient* _mqttClient = nullptr;
|
||||||
String clientId;
|
Ticker _mqttReconnectTimer;
|
||||||
String willTopic;
|
|
||||||
Ticker mqttReconnectTimer;
|
|
||||||
MqttSubscribeParser _mqttSubscribeParser;
|
MqttSubscribeParser _mqttSubscribeParser;
|
||||||
std::mutex _clientLock;
|
std::mutex _clientLock;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "W5500.h"
|
||||||
#include <DNSServer.h>
|
#include <DNSServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -22,60 +24,67 @@ enum class network_event {
|
|||||||
NETWORK_EVENT_MAX
|
NETWORK_EVENT_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::function<void(network_event event)> NetworkEventCb;
|
typedef std::function<void(network_event event)> DtuNetworkEventCb;
|
||||||
|
|
||||||
typedef struct NetworkEventCbList {
|
typedef struct DtuNetworkEventCbList {
|
||||||
NetworkEventCb cb;
|
DtuNetworkEventCb cb;
|
||||||
network_event event;
|
network_event event;
|
||||||
|
|
||||||
NetworkEventCbList()
|
DtuNetworkEventCbList()
|
||||||
: cb(NULL)
|
: cb(nullptr)
|
||||||
, event(network_event::NETWORK_UNKNOWN)
|
, event(network_event::NETWORK_UNKNOWN)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
} NetworkEventCbList_t;
|
} DtuNetworkEventCbList_t;
|
||||||
|
|
||||||
class NetworkSettingsClass {
|
class NetworkSettingsClass {
|
||||||
public:
|
public:
|
||||||
NetworkSettingsClass();
|
NetworkSettingsClass();
|
||||||
void init();
|
void init(Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
void applyConfig();
|
void applyConfig();
|
||||||
void enableAdminMode();
|
void enableAdminMode();
|
||||||
String getApName();
|
String getApName() const;
|
||||||
|
|
||||||
IPAddress localIP();
|
IPAddress localIP() const;
|
||||||
IPAddress subnetMask();
|
IPAddress subnetMask() const;
|
||||||
IPAddress gatewayIP();
|
IPAddress gatewayIP() const;
|
||||||
IPAddress dnsIP(uint8_t dns_no = 0);
|
IPAddress dnsIP(const uint8_t dns_no = 0) const;
|
||||||
String macAddress();
|
String macAddress() const;
|
||||||
static String getHostname();
|
static String getHostname();
|
||||||
bool isConnected();
|
bool isConnected() const;
|
||||||
network_mode NetworkMode();
|
network_mode NetworkMode() const;
|
||||||
|
|
||||||
bool onEvent(NetworkEventCb cbEvent, network_event event = network_event::NETWORK_EVENT_MAX);
|
bool onEvent(DtuNetworkEventCb cbEvent, const network_event event = network_event::NETWORK_EVENT_MAX);
|
||||||
void raiseEvent(network_event event);
|
void raiseEvent(const network_event event);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void loop();
|
||||||
void setHostname();
|
void setHostname();
|
||||||
void setStaticIp();
|
void setStaticIp();
|
||||||
|
void handleMDNS();
|
||||||
void setupMode();
|
void setupMode();
|
||||||
void NetworkEvent(WiFiEvent_t event);
|
void NetworkEvent(const WiFiEvent_t event, WiFiEventInfo_t info);
|
||||||
bool adminEnabled = true;
|
|
||||||
bool forceDisconnection = false;
|
Task _loopTask;
|
||||||
uint32_t adminTimeoutCounter = 0;
|
|
||||||
uint32_t adminTimeoutCounterMax = 0;
|
static constexpr byte DNS_PORT = 53;
|
||||||
uint32_t connectTimeoutTimer = 0;
|
|
||||||
uint32_t connectRedoTimer = 0;
|
bool _adminEnabled = true;
|
||||||
uint32_t lastTimerCall = 0;
|
bool _forceDisconnection = false;
|
||||||
const byte DNS_PORT = 53;
|
uint32_t _adminTimeoutCounter = 0;
|
||||||
IPAddress apIp;
|
uint32_t _adminTimeoutCounterMax = 0;
|
||||||
IPAddress apNetmask;
|
uint32_t _connectTimeoutTimer = 0;
|
||||||
std::unique_ptr<DNSServer> dnsServer;
|
uint32_t _connectRedoTimer = 0;
|
||||||
bool dnsServerStatus = false;
|
uint32_t _lastTimerCall = 0;
|
||||||
|
IPAddress _apIp;
|
||||||
|
IPAddress _apNetmask;
|
||||||
|
std::unique_ptr<DNSServer> _dnsServer;
|
||||||
|
bool _dnsServerStatus = false;
|
||||||
network_mode _networkMode = network_mode::Undefined;
|
network_mode _networkMode = network_mode::Undefined;
|
||||||
bool _ethConnected = false;
|
bool _ethConnected = false;
|
||||||
std::vector<NetworkEventCbList_t> _cbEventList;
|
std::vector<DtuNetworkEventCbList_t> _cbEventList;
|
||||||
|
bool _lastMdnsEnabled = false;
|
||||||
|
std::unique_ptr<W5500> _w5500;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern NetworkSettingsClass NetworkSettings;
|
extern NetworkSettingsClass NetworkSettings;
|
||||||
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
struct PinMapping_t {
|
struct PinMapping_t {
|
||||||
char name[MAPPING_NAME_STRLEN + 1];
|
char name[MAPPING_NAME_STRLEN + 1];
|
||||||
|
|
||||||
int8_t nrf24_miso;
|
int8_t nrf24_miso;
|
||||||
int8_t nrf24_mosi;
|
int8_t nrf24_mosi;
|
||||||
int8_t nrf24_clk;
|
int8_t nrf24_clk;
|
||||||
@ -26,6 +27,14 @@ struct PinMapping_t {
|
|||||||
int8_t cmt_gpio3;
|
int8_t cmt_gpio3;
|
||||||
int8_t cmt_sdio;
|
int8_t cmt_sdio;
|
||||||
|
|
||||||
|
int8_t w5500_mosi;
|
||||||
|
int8_t w5500_miso;
|
||||||
|
int8_t w5500_sclk;
|
||||||
|
int8_t w5500_cs;
|
||||||
|
int8_t w5500_int;
|
||||||
|
int8_t w5500_rst;
|
||||||
|
|
||||||
|
#if CONFIG_ETH_USE_ESP32_EMAC
|
||||||
int8_t eth_phy_addr;
|
int8_t eth_phy_addr;
|
||||||
bool eth_enabled;
|
bool eth_enabled;
|
||||||
int eth_power;
|
int eth_power;
|
||||||
@ -33,11 +42,14 @@ struct PinMapping_t {
|
|||||||
int eth_mdio;
|
int eth_mdio;
|
||||||
eth_phy_type_t eth_type;
|
eth_phy_type_t eth_type;
|
||||||
eth_clock_mode_t eth_clk_mode;
|
eth_clock_mode_t eth_clk_mode;
|
||||||
|
#endif
|
||||||
|
|
||||||
uint8_t display_type;
|
uint8_t display_type;
|
||||||
uint8_t display_data;
|
uint8_t display_data;
|
||||||
uint8_t display_clk;
|
uint8_t display_clk;
|
||||||
uint8_t display_cs;
|
uint8_t display_cs;
|
||||||
uint8_t display_reset;
|
uint8_t display_reset;
|
||||||
|
|
||||||
int8_t led[PINMAPPING_LED_COUNT];
|
int8_t led[PINMAPPING_LED_COUNT];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -47,9 +59,12 @@ public:
|
|||||||
bool init(const String& deviceMapping);
|
bool init(const String& deviceMapping);
|
||||||
PinMapping_t& get();
|
PinMapping_t& get();
|
||||||
|
|
||||||
bool isValidNrf24Config();
|
bool isValidNrf24Config() const;
|
||||||
bool isValidCmt2300Config();
|
bool isValidCmt2300Config() const;
|
||||||
bool isValidEthConfig();
|
bool isValidW5500Config() const;
|
||||||
|
#if CONFIG_ETH_USE_ESP32_EMAC
|
||||||
|
bool isValidEthConfig() const;
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PinMapping_t _pinMapping;
|
PinMapping_t _pinMapping;
|
||||||
|
|||||||
18
include/RestartHelper.h
Normal file
18
include/RestartHelper.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
|
class RestartHelperClass {
|
||||||
|
public:
|
||||||
|
RestartHelperClass();
|
||||||
|
void init(Scheduler& scheduler);
|
||||||
|
void triggerRestart();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void loop();
|
||||||
|
|
||||||
|
Task _rebootTask;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern RestartHelperClass RestartHelper;
|
||||||
6
include/Scheduler.h
Normal file
6
include/Scheduler.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
|
extern Scheduler scheduler;
|
||||||
@ -1,32 +1,36 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
#include <atomic>
|
||||||
#include <sunset.h>
|
#include <sunset.h>
|
||||||
|
|
||||||
#define SUNPOS_UPDATE_INTERVAL 60000l
|
|
||||||
|
|
||||||
class SunPositionClass {
|
class SunPositionClass {
|
||||||
public:
|
public:
|
||||||
SunPositionClass();
|
SunPositionClass();
|
||||||
void init();
|
void init(Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
bool isDayPeriod();
|
bool isDayPeriod() const;
|
||||||
bool isSunsetAvailable();
|
bool isSunsetAvailable() const;
|
||||||
bool sunsetTime(struct tm* info);
|
bool sunsetTime(struct tm* info) const;
|
||||||
bool sunriseTime(struct tm* info);
|
bool sunriseTime(struct tm* info) const;
|
||||||
|
void setDoRecalc(const bool doRecalc);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void loop();
|
||||||
void updateSunData();
|
void updateSunData();
|
||||||
|
bool checkRecalcDayChanged() const;
|
||||||
|
bool getSunTime(struct tm* info, const uint32_t offset) const;
|
||||||
|
|
||||||
|
Task _loopTask;
|
||||||
|
|
||||||
SunSet _sun;
|
|
||||||
bool _isDayPeriod = true;
|
|
||||||
bool _isSunsetAvailable = true;
|
bool _isSunsetAvailable = true;
|
||||||
uint32_t _sunriseMinutes = 0;
|
uint32_t _sunriseMinutes = 0;
|
||||||
uint32_t _sunsetMinutes = 0;
|
uint32_t _sunsetMinutes = 0;
|
||||||
|
|
||||||
uint32_t _lastUpdate = 0;
|
|
||||||
bool _isValidInfo = false;
|
bool _isValidInfo = false;
|
||||||
|
std::atomic_bool _doRecalc = true;
|
||||||
|
uint32_t _lastSunPositionCalculatedYMD = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern SunPositionClass SunPosition;
|
extern SunPositionClass SunPosition;
|
||||||
@ -1,6 +1,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <ArduinoJson.h>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
class Utils {
|
class Utils {
|
||||||
@ -8,5 +9,6 @@ public:
|
|||||||
static uint32_t getChipId();
|
static uint32_t getChipId();
|
||||||
static uint64_t generateDtuSerial();
|
static uint64_t generateDtuSerial();
|
||||||
static int getTimezoneOffset();
|
static int getTimezoneOffset();
|
||||||
static void restartDtu();
|
static bool checkJsonAlloc(const JsonDocument& doc, const char* function, const uint16_t line);
|
||||||
|
static void removeAllFiles();
|
||||||
};
|
};
|
||||||
|
|||||||
29
include/W5500.h
Normal file
29
include/W5500.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <driver/spi_master.h>
|
||||||
|
#include <esp_eth.h> // required for esp_eth_handle_t
|
||||||
|
#include <esp_netif.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class W5500 {
|
||||||
|
private:
|
||||||
|
explicit W5500(spi_device_handle_t spi, gpio_num_t pin_int);
|
||||||
|
|
||||||
|
public:
|
||||||
|
W5500(const W5500&) = delete;
|
||||||
|
W5500& operator=(const W5500&) = delete;
|
||||||
|
~W5500();
|
||||||
|
|
||||||
|
static std::unique_ptr<W5500> setup(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, int8_t pin_int, int8_t pin_rst);
|
||||||
|
String macAddress();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool connection_check_spi(spi_device_handle_t spi);
|
||||||
|
static bool connection_check_interrupt(gpio_num_t pin_int);
|
||||||
|
|
||||||
|
esp_eth_handle_t eth_handle;
|
||||||
|
esp_netif_t* eth_netif;
|
||||||
|
};
|
||||||
@ -5,6 +5,7 @@
|
|||||||
#include "WebApi_device.h"
|
#include "WebApi_device.h"
|
||||||
#include "WebApi_devinfo.h"
|
#include "WebApi_devinfo.h"
|
||||||
#include "WebApi_dtu.h"
|
#include "WebApi_dtu.h"
|
||||||
|
#include "WebApi_errors.h"
|
||||||
#include "WebApi_eventlog.h"
|
#include "WebApi_eventlog.h"
|
||||||
#include "WebApi_firmware.h"
|
#include "WebApi_firmware.h"
|
||||||
#include "WebApi_gridprofile.h"
|
#include "WebApi_gridprofile.h"
|
||||||
@ -21,22 +22,29 @@
|
|||||||
#include "WebApi_webapp.h"
|
#include "WebApi_webapp.h"
|
||||||
#include "WebApi_ws_console.h"
|
#include "WebApi_ws_console.h"
|
||||||
#include "WebApi_ws_live.h"
|
#include "WebApi_ws_live.h"
|
||||||
|
#include <AsyncJson.h>
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiClass {
|
class WebApiClass {
|
||||||
public:
|
public:
|
||||||
WebApiClass();
|
WebApiClass();
|
||||||
void init();
|
void init(Scheduler& scheduler);
|
||||||
void loop();
|
void reload();
|
||||||
|
|
||||||
static bool checkCredentials(AsyncWebServerRequest* request);
|
static bool checkCredentials(AsyncWebServerRequest* request);
|
||||||
static bool checkCredentialsReadonly(AsyncWebServerRequest* request);
|
static bool checkCredentialsReadonly(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
static void sendTooManyRequests(AsyncWebServerRequest* request);
|
static void sendTooManyRequests(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
|
static void writeConfig(JsonVariant& retMsg, const WebApiError code = WebApiError::GenericSuccess, const String& message = "Settings saved!");
|
||||||
|
|
||||||
|
static bool parseRequestData(AsyncWebServerRequest* request, AsyncJsonResponse* response, JsonDocument& json_document);
|
||||||
|
static uint64_t parseSerialFromRequest(AsyncWebServerRequest* request, String param_name = "inv");
|
||||||
|
static bool sendJsonResponse(AsyncWebServerRequest* request, AsyncJsonResponse* response, const char* function, const uint16_t line);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AsyncWebServer _server;
|
AsyncWebServer _server;
|
||||||
AsyncEventSource _events;
|
|
||||||
|
|
||||||
WebApiConfigClass _webApiConfig;
|
WebApiConfigClass _webApiConfig;
|
||||||
WebApiDeviceClass _webApiDevice;
|
WebApiDeviceClass _webApiDevice;
|
||||||
|
|||||||
@ -2,11 +2,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiConfigClass {
|
class WebApiConfigClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer* server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onConfigGet(AsyncWebServerRequest* request);
|
void onConfigGet(AsyncWebServerRequest* request);
|
||||||
@ -14,6 +14,4 @@ private:
|
|||||||
void onConfigListGet(AsyncWebServerRequest* request);
|
void onConfigListGet(AsyncWebServerRequest* request);
|
||||||
void onConfigUploadFinish(AsyncWebServerRequest* request);
|
void onConfigUploadFinish(AsyncWebServerRequest* request);
|
||||||
void onConfigUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final);
|
void onConfigUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -2,15 +2,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiDeviceClass {
|
class WebApiDeviceClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer* server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onDeviceAdminGet(AsyncWebServerRequest* request);
|
void onDeviceAdminGet(AsyncWebServerRequest* request);
|
||||||
void onDeviceAdminPost(AsyncWebServerRequest* request);
|
void onDeviceAdminPost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -2,14 +2,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiDevInfoClass {
|
class WebApiDevInfoClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer* server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onDevInfoStatus(AsyncWebServerRequest* request);
|
void onDevInfoStatus(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -2,15 +2,17 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiDtuClass {
|
class WebApiDtuClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer* server);
|
WebApiDtuClass();
|
||||||
void loop();
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onDtuAdminGet(AsyncWebServerRequest* request);
|
void onDtuAdminGet(AsyncWebServerRequest* request);
|
||||||
void onDtuAdminPost(AsyncWebServerRequest* request);
|
void onDtuAdminPost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
Task _applyDataTask;
|
||||||
|
void applyDataTaskCb();
|
||||||
};
|
};
|
||||||
@ -5,15 +5,18 @@ enum WebApiError {
|
|||||||
GenericBase = 1000,
|
GenericBase = 1000,
|
||||||
GenericSuccess,
|
GenericSuccess,
|
||||||
GenericNoValueFound,
|
GenericNoValueFound,
|
||||||
GenericDataTooLarge,
|
GenericDataTooLarge, // not used anymore
|
||||||
GenericParseError,
|
GenericParseError,
|
||||||
GenericValueMissing,
|
GenericValueMissing,
|
||||||
|
GenericWriteFailed,
|
||||||
|
GenericInternalServerError,
|
||||||
|
|
||||||
DtuBase = 2000,
|
DtuBase = 2000,
|
||||||
DtuSerialZero,
|
DtuSerialZero,
|
||||||
DtuPollZero,
|
DtuPollZero,
|
||||||
DtuInvalidPowerLevel,
|
DtuInvalidPowerLevel,
|
||||||
DtuInvalidCmtFrequency,
|
DtuInvalidCmtFrequency,
|
||||||
|
DtuInvalidCmtCountry,
|
||||||
|
|
||||||
ConfigBase = 3000,
|
ConfigBase = 3000,
|
||||||
ConfigNotDeleted,
|
ConfigNotDeleted,
|
||||||
@ -29,6 +32,7 @@ enum WebApiError {
|
|||||||
InverterChanged,
|
InverterChanged,
|
||||||
InverterDeleted,
|
InverterDeleted,
|
||||||
InverterOrdered,
|
InverterOrdered,
|
||||||
|
InverterStatsResetted,
|
||||||
|
|
||||||
LimitBase = 5000,
|
LimitBase = 5000,
|
||||||
LimitSerialZero,
|
LimitSerialZero,
|
||||||
@ -56,6 +60,8 @@ enum WebApiError {
|
|||||||
MqttPublishInterval,
|
MqttPublishInterval,
|
||||||
MqttHassTopicLength,
|
MqttHassTopicLength,
|
||||||
MqttHassTopicCharacter,
|
MqttHassTopicCharacter,
|
||||||
|
MqttLwtQos,
|
||||||
|
MqttClientIdLength,
|
||||||
|
|
||||||
NetworkBase = 8000,
|
NetworkBase = 8000,
|
||||||
NetworkIpInvalid,
|
NetworkIpInvalid,
|
||||||
|
|||||||
@ -2,14 +2,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiEventlogClass {
|
class WebApiEventlogClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer* server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onEventlogStatus(AsyncWebServerRequest* request);
|
void onEventlogStatus(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -2,15 +2,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiFirmwareClass {
|
class WebApiFirmwareClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer* server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onFirmwareUpdateFinish(AsyncWebServerRequest* request);
|
void onFirmwareUpdateFinish(AsyncWebServerRequest* request);
|
||||||
void onFirmwareUpdateUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final);
|
void onFirmwareUpdateUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -2,14 +2,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiGridProfileClass {
|
class WebApiGridProfileClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer* server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onGridProfileStatus(AsyncWebServerRequest* request);
|
void onGridProfileStatus(AsyncWebServerRequest* request);
|
||||||
|
void onGridProfileRawdata(AsyncWebServerRequest* request);
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -2,11 +2,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiInverterClass {
|
class WebApiInverterClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer* server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onInverterList(AsyncWebServerRequest* request);
|
void onInverterList(AsyncWebServerRequest* request);
|
||||||
@ -14,6 +14,5 @@ private:
|
|||||||
void onInverterEdit(AsyncWebServerRequest* request);
|
void onInverterEdit(AsyncWebServerRequest* request);
|
||||||
void onInverterDelete(AsyncWebServerRequest* request);
|
void onInverterDelete(AsyncWebServerRequest* request);
|
||||||
void onInverterOrder(AsyncWebServerRequest* request);
|
void onInverterOrder(AsyncWebServerRequest* request);
|
||||||
|
void onInverterStatReset(AsyncWebServerRequest* request);
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -2,15 +2,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiLimitClass {
|
class WebApiLimitClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer* server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onLimitStatus(AsyncWebServerRequest* request);
|
void onLimitStatus(AsyncWebServerRequest* request);
|
||||||
void onLimitPost(AsyncWebServerRequest* request);
|
void onLimitPost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -2,14 +2,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiMaintenanceClass {
|
class WebApiMaintenanceClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer* server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onRebootPost(AsyncWebServerRequest* request);
|
void onRebootPost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -2,19 +2,15 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
#define MQTT_JSON_DOC_SIZE 10240
|
|
||||||
|
|
||||||
class WebApiMqttClass {
|
class WebApiMqttClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer* server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onMqttStatus(AsyncWebServerRequest* request);
|
void onMqttStatus(AsyncWebServerRequest* request);
|
||||||
void onMqttAdminGet(AsyncWebServerRequest* request);
|
void onMqttAdminGet(AsyncWebServerRequest* request);
|
||||||
void onMqttAdminPost(AsyncWebServerRequest* request);
|
void onMqttAdminPost(AsyncWebServerRequest* request);
|
||||||
String getTlsCertInfo(const char* cert);
|
String getTlsCertInfo(const char* cert);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -2,16 +2,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiNetworkClass {
|
class WebApiNetworkClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer* server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onNetworkStatus(AsyncWebServerRequest* request);
|
void onNetworkStatus(AsyncWebServerRequest* request);
|
||||||
void onNetworkAdminGet(AsyncWebServerRequest* request);
|
void onNetworkAdminGet(AsyncWebServerRequest* request);
|
||||||
void onNetworkAdminPost(AsyncWebServerRequest* request);
|
void onNetworkAdminPost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -2,11 +2,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiNtpClass {
|
class WebApiNtpClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer* server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onNtpStatus(AsyncWebServerRequest* request);
|
void onNtpStatus(AsyncWebServerRequest* request);
|
||||||
@ -14,6 +14,4 @@ private:
|
|||||||
void onNtpAdminPost(AsyncWebServerRequest* request);
|
void onNtpAdminPost(AsyncWebServerRequest* request);
|
||||||
void onNtpTimeGet(AsyncWebServerRequest* request);
|
void onNtpTimeGet(AsyncWebServerRequest* request);
|
||||||
void onNtpTimePost(AsyncWebServerRequest* request);
|
void onNtpTimePost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -2,15 +2,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiPowerClass {
|
class WebApiPowerClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer* server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onPowerStatus(AsyncWebServerRequest* request);
|
void onPowerStatus(AsyncWebServerRequest* request);
|
||||||
void onPowerPost(AsyncWebServerRequest* request);
|
void onPowerPost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -3,21 +3,19 @@
|
|||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
#include <Hoymiles.h>
|
#include <Hoymiles.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
class WebApiPrometheusClass {
|
class WebApiPrometheusClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer* server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onPrometheusMetricsGet(AsyncWebServerRequest* request);
|
void onPrometheusMetricsGet(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
void addField(AsyncResponseStream* stream, String& serial, uint8_t idx, std::shared_ptr<InverterAbstract> inv, ChannelType_t type, ChannelNum_t channel, FieldId_t fieldId, const char* metricName, const char* channelName = NULL);
|
void addField(AsyncResponseStream* stream, const String& serial, const uint8_t idx, std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId, const char* metricName, const char* channelName = nullptr);
|
||||||
|
|
||||||
void addPanelInfo(AsyncResponseStream* stream, String& serial, uint8_t idx, std::shared_ptr<InverterAbstract> inv, ChannelType_t type, ChannelNum_t channel);
|
void addPanelInfo(AsyncResponseStream* stream, const String& serial, const uint8_t idx, std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
|
|
||||||
enum MetricType_t {
|
enum MetricType_t {
|
||||||
NONE = 0,
|
NONE = 0,
|
||||||
|
|||||||
@ -2,17 +2,15 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiSecurityClass {
|
class WebApiSecurityClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer* server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onSecurityGet(AsyncWebServerRequest* request);
|
void onSecurityGet(AsyncWebServerRequest* request);
|
||||||
void onSecurityPost(AsyncWebServerRequest* request);
|
void onSecurityPost(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
void onAuthenticateGet(AsyncWebServerRequest* request);
|
void onAuthenticateGet(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -2,14 +2,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiSysstatusClass {
|
class WebApiSysstatusClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer* server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onSystemStatus(AsyncWebServerRequest* request);
|
void onSystemStatus(AsyncWebServerRequest* request);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
};
|
};
|
||||||
@ -2,12 +2,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiWebappClass {
|
class WebApiWebappClass {
|
||||||
public:
|
public:
|
||||||
void init(AsyncWebServer* server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AsyncWebServer* _server;
|
void responseBinaryDataWithETagCache(AsyncWebServerRequest* request, const String &contentType, const String &contentEncoding, const uint8_t *content, size_t len);
|
||||||
};
|
};
|
||||||
@ -2,18 +2,18 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiWsConsoleClass {
|
class WebApiWsConsoleClass {
|
||||||
public:
|
public:
|
||||||
WebApiWsConsoleClass();
|
WebApiWsConsoleClass();
|
||||||
void init(AsyncWebServer* server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
void reload();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len);
|
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
AsyncWebSocket _ws;
|
AsyncWebSocket _ws;
|
||||||
|
AuthenticationMiddleware _simpleDigestAuth;
|
||||||
|
|
||||||
uint32_t _lastWsCleanup = 0;
|
Task _wsCleanupTask;
|
||||||
|
void wsCleanupTaskCb();
|
||||||
};
|
};
|
||||||
@ -1,30 +1,39 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Configuration.h"
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
#include <Hoymiles.h>
|
#include <Hoymiles.h>
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
class WebApiWsLiveClass {
|
class WebApiWsLiveClass {
|
||||||
public:
|
public:
|
||||||
WebApiWsLiveClass();
|
WebApiWsLiveClass();
|
||||||
void init(AsyncWebServer* server);
|
void init(AsyncWebServer& server, Scheduler& scheduler);
|
||||||
void loop();
|
void reload();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void generateJsonResponse(JsonVariant& root);
|
static void generateInverterCommonJsonResponse(JsonObject& root, std::shared_ptr<InverterAbstract> inv);
|
||||||
void addField(JsonObject& root, uint8_t idx, std::shared_ptr<InverterAbstract> inv, ChannelType_t type, ChannelNum_t channel, FieldId_t fieldId, String topic = "");
|
static void generateInverterChannelJsonResponse(JsonObject& root, std::shared_ptr<InverterAbstract> inv);
|
||||||
void addTotalField(JsonObject& root, String name, float value, String unit, uint8_t digits);
|
static void generateCommonJsonResponse(JsonVariant& root);
|
||||||
|
|
||||||
|
static void addField(JsonObject& root, std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId, String topic = "");
|
||||||
|
static void addTotalField(JsonObject& root, const String& name, const float value, const String& unit, const uint8_t digits);
|
||||||
|
|
||||||
void onLivedataStatus(AsyncWebServerRequest* request);
|
void onLivedataStatus(AsyncWebServerRequest* request);
|
||||||
void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len);
|
void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len);
|
||||||
|
|
||||||
AsyncWebServer* _server;
|
|
||||||
AsyncWebSocket _ws;
|
AsyncWebSocket _ws;
|
||||||
|
AuthenticationMiddleware _simpleDigestAuth;
|
||||||
|
|
||||||
uint32_t _lastWsPublish = 0;
|
uint32_t _lastPublishStats[INV_MAX_COUNT] = { 0 };
|
||||||
uint32_t _lastInvUpdateCheck = 0;
|
|
||||||
uint32_t _lastWsCleanup = 0;
|
|
||||||
uint32_t _newestInverterTimestamp = 0;
|
|
||||||
|
|
||||||
std::mutex _mutex;
|
std::mutex _mutex;
|
||||||
|
|
||||||
|
Task _wsCleanupTask;
|
||||||
|
void wsCleanupTaskCb();
|
||||||
|
|
||||||
|
Task _sendDataTask;
|
||||||
|
void sendDataTaskCb();
|
||||||
};
|
};
|
||||||
9
include/__compiled_constants.h
Normal file
9
include/__compiled_constants.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// The referenced values are generated by pio-scripts/auto_firmware_version.py
|
||||||
|
|
||||||
|
|
||||||
|
extern const char *__COMPILED_GIT_HASH__;
|
||||||
|
extern const char *__COMPILED_GIT_BRANCH__;
|
||||||
|
// extern const char *__COMPILED_DATE_TIME_UTC_STR__;
|
||||||
@ -9,18 +9,21 @@
|
|||||||
|
|
||||||
#define ACCESS_POINT_NAME "OpenDTU-"
|
#define ACCESS_POINT_NAME "OpenDTU-"
|
||||||
#define ACCESS_POINT_PASSWORD "openDTU42"
|
#define ACCESS_POINT_PASSWORD "openDTU42"
|
||||||
#define ACCESS_POINT_TIMEOUT 3;
|
#define ACCESS_POINT_TIMEOUT 3
|
||||||
#define AUTH_USERNAME "admin"
|
#define AUTH_USERNAME "admin"
|
||||||
#define SECURITY_ALLOW_READONLY true
|
#define SECURITY_ALLOW_READONLY true
|
||||||
|
|
||||||
#define WIFI_RECONNECT_TIMEOUT 15
|
#define WIFI_RECONNECT_TIMEOUT 30
|
||||||
#define WIFI_RECONNECT_REDO_TIMEOUT 600
|
#define WIFI_RECONNECT_REDO_TIMEOUT 600
|
||||||
|
|
||||||
#define WIFI_SSID ""
|
#define WIFI_SSID ""
|
||||||
#define WIFI_PASSWORD ""
|
#define WIFI_PASSWORD ""
|
||||||
#define WIFI_DHCP true
|
#define WIFI_DHCP true
|
||||||
|
|
||||||
#define NTP_SERVER "pool.ntp.org"
|
#define MDNS_ENABLED false
|
||||||
|
|
||||||
|
#define NTP_SERVER_OLD "pool.ntp.org"
|
||||||
|
#define NTP_SERVER "opendtu.pool.ntp.org"
|
||||||
#define NTP_TIMEZONE "CET-1CEST,M3.5.0,M10.5.0/3"
|
#define NTP_TIMEZONE "CET-1CEST,M3.5.0,M10.5.0/3"
|
||||||
#define NTP_TIMEZONEDESCR "Europe/Berlin"
|
#define NTP_TIMEZONEDESCR "Europe/Berlin"
|
||||||
#define NTP_LONGITUDE 10.4515f
|
#define NTP_LONGITUDE 10.4515f
|
||||||
@ -73,6 +76,7 @@
|
|||||||
#define MQTT_LWT_TOPIC "dtu/status"
|
#define MQTT_LWT_TOPIC "dtu/status"
|
||||||
#define MQTT_LWT_ONLINE "online"
|
#define MQTT_LWT_ONLINE "online"
|
||||||
#define MQTT_LWT_OFFLINE "offline"
|
#define MQTT_LWT_OFFLINE "offline"
|
||||||
|
#define MQTT_LWT_QOS 2U
|
||||||
#define MQTT_PUBLISH_INTERVAL 5U
|
#define MQTT_PUBLISH_INTERVAL 5U
|
||||||
#define MQTT_CLEAN_SESSION true
|
#define MQTT_CLEAN_SESSION true
|
||||||
|
|
||||||
@ -80,7 +84,8 @@
|
|||||||
#define DTU_POLL_INTERVAL 5U
|
#define DTU_POLL_INTERVAL 5U
|
||||||
#define DTU_NRF_PA_LEVEL 0U
|
#define DTU_NRF_PA_LEVEL 0U
|
||||||
#define DTU_CMT_PA_LEVEL 0
|
#define DTU_CMT_PA_LEVEL 0
|
||||||
#define DTU_CMT_FREQUENCY 865000U
|
#define DTU_CMT_FREQUENCY 865000000U
|
||||||
|
#define DTU_CMT_COUNTRY_MODE 0U
|
||||||
|
|
||||||
#define MQTT_HASS_ENABLED false
|
#define MQTT_HASS_ENABLED false
|
||||||
#define MQTT_HASS_EXPIRE true
|
#define MQTT_HASS_EXPIRE true
|
||||||
@ -95,5 +100,11 @@
|
|||||||
#define DISPLAY_ROTATION 2U
|
#define DISPLAY_ROTATION 2U
|
||||||
#define DISPLAY_CONTRAST 60U
|
#define DISPLAY_CONTRAST 60U
|
||||||
#define DISPLAY_LANGUAGE 0U
|
#define DISPLAY_LANGUAGE 0U
|
||||||
|
#define DISPLAY_DIAGRAM_DURATION (10UL * 60UL * 60UL)
|
||||||
|
#define DISPLAY_DIAGRAM_MODE 1U
|
||||||
|
|
||||||
#define REACHABLE_THRESHOLD 2U
|
#define REACHABLE_THRESHOLD 2U
|
||||||
|
|
||||||
|
#define LED_BRIGHTNESS 100U
|
||||||
|
|
||||||
|
#define MAX_INVERTER_LIMIT 2250
|
||||||
|
|||||||
@ -628,7 +628,7 @@ int CMT2300A_GetRssiDBm(void)
|
|||||||
* for fast frequency hopping operation.
|
* for fast frequency hopping operation.
|
||||||
* @param nChann: the frequency channel
|
* @param nChann: the frequency channel
|
||||||
* *********************************************************/
|
* *********************************************************/
|
||||||
void CMT2300A_SetFrequencyChannel(uint8_t nChann)
|
void CMT2300A_SetFrequencyChannel(const uint8_t nChann)
|
||||||
{
|
{
|
||||||
CMT2300A_WriteReg(CMT2300A_CUS_FREQ_CHNL, nChann);
|
CMT2300A_WriteReg(CMT2300A_CUS_FREQ_CHNL, nChann);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -75,7 +75,7 @@ void CMT2300A_EnableTxDinInvert(bool bEnable);
|
|||||||
bool CMT2300A_IsExist(void);
|
bool CMT2300A_IsExist(void);
|
||||||
uint8_t CMT2300A_GetRssiCode(void);
|
uint8_t CMT2300A_GetRssiCode(void);
|
||||||
int CMT2300A_GetRssiDBm(void);
|
int CMT2300A_GetRssiDBm(void);
|
||||||
void CMT2300A_SetFrequencyChannel(uint8_t nChann);
|
void CMT2300A_SetFrequencyChannel(const uint8_t nChann);
|
||||||
void CMT2300A_SetFrequencyStep(uint8_t nOffset);
|
void CMT2300A_SetFrequencyStep(uint8_t nOffset);
|
||||||
void CMT2300A_SetPayloadLength(uint16_t nLength);
|
void CMT2300A_SetPayloadLength(uint16_t nLength);
|
||||||
void CMT2300A_EnableLfosc(bool bEnable);
|
void CMT2300A_EnableLfosc(bool bEnable);
|
||||||
|
|||||||
@ -26,7 +26,7 @@
|
|||||||
* @name CMT2300A_InitSpi
|
* @name CMT2300A_InitSpi
|
||||||
* @desc Initializes the CMT2300A SPI interface.
|
* @desc Initializes the CMT2300A SPI interface.
|
||||||
* *********************************************************/
|
* *********************************************************/
|
||||||
void CMT2300A_InitSpi(int8_t pin_sdio, int8_t pin_clk, int8_t pin_cs, int8_t pin_fcs, uint32_t spi_speed)
|
void CMT2300A_InitSpi(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const uint32_t spi_speed)
|
||||||
{
|
{
|
||||||
cmt_spi3_init(pin_sdio, pin_clk, pin_cs, pin_fcs, spi_speed);
|
cmt_spi3_init(pin_sdio, pin_clk, pin_cs, pin_fcs, spi_speed);
|
||||||
}
|
}
|
||||||
@ -37,7 +37,7 @@ void CMT2300A_InitSpi(int8_t pin_sdio, int8_t pin_clk, int8_t pin_cs, int8_t pin
|
|||||||
* @param addr: register address
|
* @param addr: register address
|
||||||
* @return Register value
|
* @return Register value
|
||||||
* *********************************************************/
|
* *********************************************************/
|
||||||
uint8_t CMT2300A_ReadReg(uint8_t addr)
|
uint8_t CMT2300A_ReadReg(const uint8_t addr)
|
||||||
{
|
{
|
||||||
return cmt_spi3_read(addr);
|
return cmt_spi3_read(addr);
|
||||||
}
|
}
|
||||||
@ -48,7 +48,7 @@ uint8_t CMT2300A_ReadReg(uint8_t addr)
|
|||||||
* @param addr: register address
|
* @param addr: register address
|
||||||
* dat: register value
|
* dat: register value
|
||||||
* *********************************************************/
|
* *********************************************************/
|
||||||
void CMT2300A_WriteReg(uint8_t addr, uint8_t dat)
|
void CMT2300A_WriteReg(const uint8_t addr, const uint8_t dat)
|
||||||
{
|
{
|
||||||
cmt_spi3_write(addr, dat);
|
cmt_spi3_write(addr, dat);
|
||||||
}
|
}
|
||||||
@ -59,7 +59,7 @@ void CMT2300A_WriteReg(uint8_t addr, uint8_t dat)
|
|||||||
* @param buf: buffer where to copy the FIFO read data
|
* @param buf: buffer where to copy the FIFO read data
|
||||||
* len: number of bytes to be read from the FIFO
|
* len: number of bytes to be read from the FIFO
|
||||||
* *********************************************************/
|
* *********************************************************/
|
||||||
void CMT2300A_ReadFifo(uint8_t buf[], uint16_t len)
|
void CMT2300A_ReadFifo(uint8_t buf[], const uint16_t len)
|
||||||
{
|
{
|
||||||
cmt_spi3_read_fifo(buf, len);
|
cmt_spi3_read_fifo(buf, len);
|
||||||
}
|
}
|
||||||
@ -70,7 +70,7 @@ void CMT2300A_ReadFifo(uint8_t buf[], uint16_t len)
|
|||||||
* @param buf: buffer containing data to be put on the FIFO
|
* @param buf: buffer containing data to be put on the FIFO
|
||||||
* len: number of bytes to be written to the FIFO
|
* len: number of bytes to be written to the FIFO
|
||||||
* *********************************************************/
|
* *********************************************************/
|
||||||
void CMT2300A_WriteFifo(const uint8_t buf[], uint16_t len)
|
void CMT2300A_WriteFifo(const uint8_t buf[], const uint16_t len)
|
||||||
{
|
{
|
||||||
cmt_spi3_write_fifo(buf, len);
|
cmt_spi3_write_fifo(buf, len);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,13 +36,13 @@ extern "C" {
|
|||||||
#define CMT2300A_GetTickCount() millis()
|
#define CMT2300A_GetTickCount() millis()
|
||||||
/* ************************************************************************ */
|
/* ************************************************************************ */
|
||||||
|
|
||||||
void CMT2300A_InitSpi(int8_t pin_sdio, int8_t pin_clk, int8_t pin_cs, int8_t pin_fcs, uint32_t spi_speed);
|
void CMT2300A_InitSpi(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const uint32_t spi_speed);
|
||||||
|
|
||||||
uint8_t CMT2300A_ReadReg(uint8_t addr);
|
uint8_t CMT2300A_ReadReg(const uint8_t addr);
|
||||||
void CMT2300A_WriteReg(uint8_t addr, uint8_t dat);
|
void CMT2300A_WriteReg(const uint8_t addr, const uint8_t dat);
|
||||||
|
|
||||||
void CMT2300A_ReadFifo(uint8_t buf[], uint16_t len);
|
void CMT2300A_ReadFifo(uint8_t buf[], const uint16_t len);
|
||||||
void CMT2300A_WriteFifo(const uint8_t buf[], uint16_t len);
|
void CMT2300A_WriteFifo(const uint8_t buf[], const uint16_t len);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@ -85,14 +85,14 @@
|
|||||||
; RSSI Offset = 0
|
; RSSI Offset = 0
|
||||||
; RSSI Offset Sign = 0
|
; RSSI Offset Sign = 0
|
||||||
*/
|
*/
|
||||||
#ifndef __CMT2300A_PARAMS_H
|
#ifndef __CMT2300A_PARAMS_860_H
|
||||||
#define __CMT2300A_PARAMS_H
|
#define __CMT2300A_PARAMS_860_H
|
||||||
|
|
||||||
#include "cmt2300a_defs.h"
|
#include "cmt2300a_defs.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
/* [CMT Bank] with RSSI offset of +- 0 (and Tx power double bit not set) */
|
/* [CMT Bank] with RSSI offset of +- 0 (and Tx power double bit not set) */
|
||||||
static uint8_t g_cmt2300aCmtBank[CMT2300A_CMT_BANK_SIZE] = {
|
static uint8_t g_cmt2300aCmtBank_860[CMT2300A_CMT_BANK_SIZE] = {
|
||||||
0x00,
|
0x00,
|
||||||
0x66,
|
0x66,
|
||||||
0xEC,
|
0xEC,
|
||||||
@ -108,7 +108,7 @@ static uint8_t g_cmt2300aCmtBank[CMT2300A_CMT_BANK_SIZE] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* [System Bank] */
|
/* [System Bank] */
|
||||||
static uint8_t g_cmt2300aSystemBank[CMT2300A_SYSTEM_BANK_SIZE] = {
|
static uint8_t g_cmt2300aSystemBank_860[CMT2300A_SYSTEM_BANK_SIZE] = {
|
||||||
0xAE,
|
0xAE,
|
||||||
0xE0,
|
0xE0,
|
||||||
0x35,
|
0x35,
|
||||||
@ -124,7 +124,7 @@ static uint8_t g_cmt2300aSystemBank[CMT2300A_SYSTEM_BANK_SIZE] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* [Frequency Bank] 860 MHz */
|
/* [Frequency Bank] 860 MHz */
|
||||||
static uint8_t g_cmt2300aFrequencyBank[CMT2300A_FREQUENCY_BANK_SIZE] = {
|
static uint8_t g_cmt2300aFrequencyBank_860[CMT2300A_FREQUENCY_BANK_SIZE] = {
|
||||||
0x42,
|
0x42,
|
||||||
0x32,
|
0x32,
|
||||||
0xCF,
|
0xCF,
|
||||||
@ -136,7 +136,7 @@ static uint8_t g_cmt2300aFrequencyBank[CMT2300A_FREQUENCY_BANK_SIZE] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* [Data Rate Bank] */
|
/* [Data Rate Bank] */
|
||||||
static uint8_t g_cmt2300aDataRateBank[CMT2300A_DATA_RATE_BANK_SIZE] = {
|
static uint8_t g_cmt2300aDataRateBank_860[CMT2300A_DATA_RATE_BANK_SIZE] = {
|
||||||
0xA6,
|
0xA6,
|
||||||
0xC9,
|
0xC9,
|
||||||
0x20,
|
0x20,
|
||||||
@ -164,7 +164,7 @@ static uint8_t g_cmt2300aDataRateBank[CMT2300A_DATA_RATE_BANK_SIZE] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* [Baseband Bank] - EU */
|
/* [Baseband Bank] - EU */
|
||||||
static uint8_t g_cmt2300aBasebandBank[CMT2300A_BASEBAND_BANK_SIZE] = {
|
static uint8_t g_cmt2300aBasebandBank_860[CMT2300A_BASEBAND_BANK_SIZE] = {
|
||||||
0x12,
|
0x12,
|
||||||
0x1E,
|
0x1E,
|
||||||
0x00,
|
0x00,
|
||||||
@ -197,7 +197,7 @@ static uint8_t g_cmt2300aBasebandBank[CMT2300A_BASEBAND_BANK_SIZE] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* [Tx Bank] 13 dBm */
|
/* [Tx Bank] 13 dBm */
|
||||||
static uint8_t g_cmt2300aTxBank[CMT2300A_TX_BANK_SIZE] = {
|
static uint8_t g_cmt2300aTxBank_860[CMT2300A_TX_BANK_SIZE] = {
|
||||||
0x70,
|
0x70,
|
||||||
0x4D,
|
0x4D,
|
||||||
0x06,
|
0x06,
|
||||||
214
lib/CMT2300a/cmt2300a_params_900.h
Normal file
214
lib/CMT2300a/cmt2300a_params_900.h
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
;---------------------------------------
|
||||||
|
; CMT2300A Configuration File
|
||||||
|
; Generated by CMOSTEK RFPDK 1.46
|
||||||
|
; 2023.03.17 23:16
|
||||||
|
;---------------------------------------
|
||||||
|
; Mode = Advanced
|
||||||
|
; Part Number = CMT2300A
|
||||||
|
; Frequency = 900.000 MHz
|
||||||
|
; Xtal Frequency = 26.0000 MHz
|
||||||
|
; Demodulation = GFSK
|
||||||
|
; AGC = On
|
||||||
|
; Data Rate = 20.0 kbps
|
||||||
|
; Deviation = 20.0 kHz
|
||||||
|
; Tx Xtal Tol. = 20 ppm
|
||||||
|
; Rx Xtal Tol. = 20 ppm
|
||||||
|
; TRx Matching Network Type = 20 dBm
|
||||||
|
; Tx Power = +13 dBm
|
||||||
|
; Gaussian BT = 0.5
|
||||||
|
; Bandwidth = Auto-Select kHz
|
||||||
|
; CDR Type = Counting
|
||||||
|
; CDR DR Range = NA
|
||||||
|
; AFC = On
|
||||||
|
; AFC Method = Auto-Select
|
||||||
|
; Data Representation = 0:F-low 1:F-high
|
||||||
|
; Rx Duty-Cycle = Off
|
||||||
|
; Tx Duty-Cycle = Off
|
||||||
|
; Sleep Timer = Off
|
||||||
|
; Sleep Time = NA
|
||||||
|
; Rx Timer = Off
|
||||||
|
; Rx Time T1 = NA
|
||||||
|
; Rx Time T2 = NA
|
||||||
|
; Rx Exit State = STBY
|
||||||
|
; Tx Exit State = STBY
|
||||||
|
; SLP Mode = Disable
|
||||||
|
; RSSI Valid Source = PJD
|
||||||
|
; PJD Window = 8 Jumps
|
||||||
|
; LFOSC Calibration = On
|
||||||
|
; Xtal Stable Time = 155 us
|
||||||
|
; RSSI Compare TH = NA
|
||||||
|
; Data Mode = Packet
|
||||||
|
; Whitening = Disable
|
||||||
|
; Whiten Type = NA
|
||||||
|
; Whiten Seed Type = NA
|
||||||
|
; Whiten Seed = NA
|
||||||
|
; Manchester = Disable
|
||||||
|
; Manchester Type = NA
|
||||||
|
; FEC = Enable
|
||||||
|
; FEC Type = x^3+x^2+1
|
||||||
|
; Tx Prefix Type = 0
|
||||||
|
; Tx Packet Number = 1
|
||||||
|
; Tx Packet Gap = 32
|
||||||
|
; Packet Type = Variable Length
|
||||||
|
; Node-Length Position = First Node, then Length
|
||||||
|
; Payload Bit Order = Start from msb
|
||||||
|
; Preamble Rx Size = 2
|
||||||
|
; Preamble Tx Size = 30
|
||||||
|
; Preamble Value = 170
|
||||||
|
; Preamble Unit = 8-bit
|
||||||
|
; Sync Size = 4-byte
|
||||||
|
; Sync Value = 1296587336
|
||||||
|
; Sync Tolerance = None
|
||||||
|
; Sync Manchester = Disable
|
||||||
|
; Node ID Size = NA
|
||||||
|
; Node ID Value = NA
|
||||||
|
; Node ID Mode = None
|
||||||
|
; Node ID Err Mask = Disable
|
||||||
|
; Node ID Free = Disable
|
||||||
|
; Payload Length = 32
|
||||||
|
; CRC Options = IBM-16
|
||||||
|
; CRC Seed = 0 crc_seed
|
||||||
|
; CRC Range = Entire Payload
|
||||||
|
; CRC Swap = Start from MSB
|
||||||
|
; CRC Bit Invert = Normal
|
||||||
|
; CRC Bit Order = Start from bit 15
|
||||||
|
; Dout Mute = Off
|
||||||
|
; Dout Adjust Mode = Disable
|
||||||
|
; Dout Adjust Percentage = NA
|
||||||
|
; Collision Detect = Off
|
||||||
|
; Collision Detect Offset = NA
|
||||||
|
; RSSI Detect Mode = At PREAM_OK
|
||||||
|
; RSSI Filter Setting = 32-tap
|
||||||
|
; RF Performance = High
|
||||||
|
; LBD Threshold = 2.4 V
|
||||||
|
; RSSI Offset = 0
|
||||||
|
; RSSI Offset Sign = 0
|
||||||
|
*/
|
||||||
|
#ifndef __CMT2300A_PARAMS_900_H
|
||||||
|
#define __CMT2300A_PARAMS_900_H
|
||||||
|
|
||||||
|
#include "cmt2300a_defs.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* [CMT Bank] with RSSI offset of +- 0 (and Tx power double bit not set) */
|
||||||
|
static uint8_t g_cmt2300aCmtBank_900[CMT2300A_CMT_BANK_SIZE] = {
|
||||||
|
0x00,
|
||||||
|
0x66,
|
||||||
|
0xEC,
|
||||||
|
0x1C,
|
||||||
|
0x70,
|
||||||
|
0x80,
|
||||||
|
0x14,
|
||||||
|
0x08,
|
||||||
|
0x11,
|
||||||
|
0x02,
|
||||||
|
0x02,
|
||||||
|
0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* [System Bank] */
|
||||||
|
static uint8_t g_cmt2300aSystemBank_900[CMT2300A_SYSTEM_BANK_SIZE] = {
|
||||||
|
0xAE,
|
||||||
|
0xE0,
|
||||||
|
0x35,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0xF4,
|
||||||
|
0x10,
|
||||||
|
0xE2,
|
||||||
|
0x42,
|
||||||
|
0x20,
|
||||||
|
0x0C,
|
||||||
|
0x81,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* [Frequency Bank] 900 MHz */
|
||||||
|
static uint8_t g_cmt2300aFrequencyBank_900[CMT2300A_FREQUENCY_BANK_SIZE] = {
|
||||||
|
0x45,
|
||||||
|
0x46,
|
||||||
|
0x0A,
|
||||||
|
0x84,
|
||||||
|
0x45,
|
||||||
|
0x3B,
|
||||||
|
0xB1,
|
||||||
|
0x13,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* [Data Rate Bank] */
|
||||||
|
static uint8_t g_cmt2300aDataRateBank_900[CMT2300A_DATA_RATE_BANK_SIZE] = {
|
||||||
|
0xA6,
|
||||||
|
0xC9,
|
||||||
|
0x20,
|
||||||
|
0x20,
|
||||||
|
0xD2,
|
||||||
|
0x35,
|
||||||
|
0x0C,
|
||||||
|
0x0B,
|
||||||
|
0x9F,
|
||||||
|
0x4B,
|
||||||
|
0x29,
|
||||||
|
0x29,
|
||||||
|
0xC0,
|
||||||
|
0x14,
|
||||||
|
0x05,
|
||||||
|
0x53,
|
||||||
|
0x10,
|
||||||
|
0x00,
|
||||||
|
0xB4,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x01,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* [Baseband Bank] - EU */
|
||||||
|
static uint8_t g_cmt2300aBasebandBank_900[CMT2300A_BASEBAND_BANK_SIZE] = {
|
||||||
|
0x12,
|
||||||
|
0x1E,
|
||||||
|
0x00,
|
||||||
|
0xAA,
|
||||||
|
0x06,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x48,
|
||||||
|
0x5A,
|
||||||
|
0x48,
|
||||||
|
0x4D,
|
||||||
|
0x01,
|
||||||
|
0x1F,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0xC3,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x60,
|
||||||
|
0xFF,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x1F,
|
||||||
|
0x10,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* [Tx Bank] 13 dBm */
|
||||||
|
static uint8_t g_cmt2300aTxBank_900[CMT2300A_TX_BANK_SIZE] = {
|
||||||
|
0x70,
|
||||||
|
0x4D,
|
||||||
|
0x06,
|
||||||
|
0x00,
|
||||||
|
0x07,
|
||||||
|
0x50,
|
||||||
|
0x00,
|
||||||
|
0x53,
|
||||||
|
0x09,
|
||||||
|
0x3F,
|
||||||
|
0x7F,
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -1,12 +1,13 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2023 Thomas Basler and others
|
* Copyright (C) 2023-2024 Thomas Basler and others
|
||||||
*/
|
*/
|
||||||
#include "cmt2300wrapper.h"
|
#include "cmt2300wrapper.h"
|
||||||
#include "cmt2300a.h"
|
#include "cmt2300a.h"
|
||||||
#include "cmt2300a_params.h"
|
#include "cmt2300a_params_860.h"
|
||||||
|
#include "cmt2300a_params_900.h"
|
||||||
|
|
||||||
CMT2300A::CMT2300A(uint8_t pin_sdio, uint8_t pin_clk, uint8_t pin_cs, uint8_t pin_fcs, uint32_t spi_speed)
|
CMT2300A::CMT2300A(const uint8_t pin_sdio, const uint8_t pin_clk, const uint8_t pin_cs, const uint8_t pin_fcs, const uint32_t spi_speed)
|
||||||
{
|
{
|
||||||
_pin_sdio = pin_sdio;
|
_pin_sdio = pin_sdio;
|
||||||
_pin_clk = pin_clk;
|
_pin_clk = pin_clk;
|
||||||
@ -57,7 +58,7 @@ bool CMT2300A::available(void)
|
|||||||
) & CMT2300A_ReadReg(CMT2300A_CUS_INT_FLAG);
|
) & CMT2300A_ReadReg(CMT2300A_CUS_INT_FLAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMT2300A::read(void* buf, uint8_t len)
|
void CMT2300A::read(void* buf, const uint8_t len)
|
||||||
{
|
{
|
||||||
// Fetch the payload
|
// Fetch the payload
|
||||||
CMT2300A_ReadFifo(static_cast<uint8_t*>(buf), len);
|
CMT2300A_ReadFifo(static_cast<uint8_t*>(buf), len);
|
||||||
@ -65,7 +66,7 @@ void CMT2300A::read(void* buf, uint8_t len)
|
|||||||
CMT2300A_ClearInterruptFlags();
|
CMT2300A_ClearInterruptFlags();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CMT2300A::write(const uint8_t* buf, uint8_t len)
|
bool CMT2300A::write(const uint8_t* buf, const uint8_t len)
|
||||||
{
|
{
|
||||||
CMT2300A_GoStby();
|
CMT2300A_GoStby();
|
||||||
CMT2300A_ClearInterruptFlags();
|
CMT2300A_ClearInterruptFlags();
|
||||||
@ -100,7 +101,7 @@ bool CMT2300A::write(const uint8_t* buf, uint8_t len)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMT2300A::setChannel(uint8_t channel)
|
void CMT2300A::setChannel(const uint8_t channel)
|
||||||
{
|
{
|
||||||
CMT2300A_SetFrequencyChannel(channel);
|
CMT2300A_SetFrequencyChannel(channel);
|
||||||
}
|
}
|
||||||
@ -122,7 +123,7 @@ int CMT2300A::getRssiDBm()
|
|||||||
return CMT2300A_GetRssiDBm();
|
return CMT2300A_GetRssiDBm();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CMT2300A::setPALevel(int8_t level)
|
bool CMT2300A::setPALevel(const int8_t level)
|
||||||
{
|
{
|
||||||
uint16_t Tx_dBm_word;
|
uint16_t Tx_dBm_word;
|
||||||
switch (level) {
|
switch (level) {
|
||||||
@ -242,6 +243,22 @@ bool CMT2300A::rxFifoAvailable()
|
|||||||
) & CMT2300A_ReadReg(CMT2300A_CUS_INT_FLAG);
|
) & CMT2300A_ReadReg(CMT2300A_CUS_INT_FLAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t CMT2300A::getBaseFrequency() const
|
||||||
|
{
|
||||||
|
return getBaseFrequency(_frequencyBand);
|
||||||
|
}
|
||||||
|
|
||||||
|
FrequencyBand_t CMT2300A::getFrequencyBand() const
|
||||||
|
{
|
||||||
|
return _frequencyBand;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMT2300A::setFrequencyBand(const FrequencyBand_t mode)
|
||||||
|
{
|
||||||
|
_frequencyBand = mode;
|
||||||
|
_init_radio();
|
||||||
|
}
|
||||||
|
|
||||||
void CMT2300A::flush_rx(void)
|
void CMT2300A::flush_rx(void)
|
||||||
{
|
{
|
||||||
CMT2300A_ClearRxFifo();
|
CMT2300A_ClearRxFifo();
|
||||||
@ -261,12 +278,24 @@ bool CMT2300A::_init_radio()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* config registers */
|
/* config registers */
|
||||||
CMT2300A_ConfigRegBank(CMT2300A_CMT_BANK_ADDR, g_cmt2300aCmtBank, CMT2300A_CMT_BANK_SIZE);
|
switch (_frequencyBand) {
|
||||||
CMT2300A_ConfigRegBank(CMT2300A_SYSTEM_BANK_ADDR, g_cmt2300aSystemBank, CMT2300A_SYSTEM_BANK_SIZE);
|
case FrequencyBand_t::BAND_900:
|
||||||
CMT2300A_ConfigRegBank(CMT2300A_FREQUENCY_BANK_ADDR, g_cmt2300aFrequencyBank, CMT2300A_FREQUENCY_BANK_SIZE);
|
CMT2300A_ConfigRegBank(CMT2300A_CMT_BANK_ADDR, g_cmt2300aCmtBank_900, CMT2300A_CMT_BANK_SIZE);
|
||||||
CMT2300A_ConfigRegBank(CMT2300A_DATA_RATE_BANK_ADDR, g_cmt2300aDataRateBank, CMT2300A_DATA_RATE_BANK_SIZE);
|
CMT2300A_ConfigRegBank(CMT2300A_SYSTEM_BANK_ADDR, g_cmt2300aSystemBank_900, CMT2300A_SYSTEM_BANK_SIZE);
|
||||||
CMT2300A_ConfigRegBank(CMT2300A_BASEBAND_BANK_ADDR, g_cmt2300aBasebandBank, CMT2300A_BASEBAND_BANK_SIZE);
|
CMT2300A_ConfigRegBank(CMT2300A_FREQUENCY_BANK_ADDR, g_cmt2300aFrequencyBank_900, CMT2300A_FREQUENCY_BANK_SIZE);
|
||||||
CMT2300A_ConfigRegBank(CMT2300A_TX_BANK_ADDR, g_cmt2300aTxBank, CMT2300A_TX_BANK_SIZE);
|
CMT2300A_ConfigRegBank(CMT2300A_DATA_RATE_BANK_ADDR, g_cmt2300aDataRateBank_900, CMT2300A_DATA_RATE_BANK_SIZE);
|
||||||
|
CMT2300A_ConfigRegBank(CMT2300A_BASEBAND_BANK_ADDR, g_cmt2300aBasebandBank_900, CMT2300A_BASEBAND_BANK_SIZE);
|
||||||
|
CMT2300A_ConfigRegBank(CMT2300A_TX_BANK_ADDR, g_cmt2300aTxBank_900, CMT2300A_TX_BANK_SIZE);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
CMT2300A_ConfigRegBank(CMT2300A_CMT_BANK_ADDR, g_cmt2300aCmtBank_860, CMT2300A_CMT_BANK_SIZE);
|
||||||
|
CMT2300A_ConfigRegBank(CMT2300A_SYSTEM_BANK_ADDR, g_cmt2300aSystemBank_860, CMT2300A_SYSTEM_BANK_SIZE);
|
||||||
|
CMT2300A_ConfigRegBank(CMT2300A_FREQUENCY_BANK_ADDR, g_cmt2300aFrequencyBank_860, CMT2300A_FREQUENCY_BANK_SIZE);
|
||||||
|
CMT2300A_ConfigRegBank(CMT2300A_DATA_RATE_BANK_ADDR, g_cmt2300aDataRateBank_860, CMT2300A_DATA_RATE_BANK_SIZE);
|
||||||
|
CMT2300A_ConfigRegBank(CMT2300A_BASEBAND_BANK_ADDR, g_cmt2300aBasebandBank_860, CMT2300A_BASEBAND_BANK_SIZE);
|
||||||
|
CMT2300A_ConfigRegBank(CMT2300A_TX_BANK_ADDR, g_cmt2300aTxBank_860, CMT2300A_TX_BANK_SIZE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// xosc_aac_code[2:0] = 2
|
// xosc_aac_code[2:0] = 2
|
||||||
uint8_t tmp;
|
uint8_t tmp;
|
||||||
|
|||||||
@ -4,13 +4,21 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define CMT2300A_ONE_STEP_SIZE 2500 // frequency channel step size for fast frequency hopping operation: One step size is 2.5 kHz.
|
#define CMT2300A_ONE_STEP_SIZE 2500 // frequency channel step size for fast frequency hopping operation: One step size is 2.5 kHz.
|
||||||
#define CMT_BASE_FREQ 860000000 // from Frequency Bank in cmt2300a_params.h
|
|
||||||
#define FH_OFFSET 100 // value * CMT2300A_ONE_STEP_SIZE = channel frequency offset
|
#define FH_OFFSET 100 // value * CMT2300A_ONE_STEP_SIZE = channel frequency offset
|
||||||
#define CMT_SPI_SPEED 4000000 // 4 MHz
|
#define CMT_SPI_SPEED 4000000 // 4 MHz
|
||||||
|
|
||||||
|
#define CMT_BASE_FREQ_900 900000000
|
||||||
|
#define CMT_BASE_FREQ_860 860000000
|
||||||
|
|
||||||
|
enum FrequencyBand_t {
|
||||||
|
BAND_860,
|
||||||
|
BAND_900,
|
||||||
|
FrequencyBand_Max,
|
||||||
|
};
|
||||||
|
|
||||||
class CMT2300A {
|
class CMT2300A {
|
||||||
public:
|
public:
|
||||||
CMT2300A(uint8_t pin_sdio, uint8_t pin_clk, uint8_t pin_cs, uint8_t pin_fcs, uint32_t _spi_speed = CMT_SPI_SPEED);
|
CMT2300A(const uint8_t pin_sdio, const uint8_t pin_clk, const uint8_t pin_cs, const uint8_t pin_fcs, const uint32_t _spi_speed = CMT_SPI_SPEED);
|
||||||
|
|
||||||
bool begin(void);
|
bool begin(void);
|
||||||
|
|
||||||
@ -54,15 +62,15 @@ public:
|
|||||||
* in one call is 32 (for dynamic payload lengths) or whatever number was
|
* in one call is 32 (for dynamic payload lengths) or whatever number was
|
||||||
* previously passed to setPayloadSize() (for static payload lengths).
|
* previously passed to setPayloadSize() (for static payload lengths).
|
||||||
*/
|
*/
|
||||||
void read(void* buf, uint8_t len);
|
void read(void* buf, const uint8_t len);
|
||||||
|
|
||||||
bool write(const uint8_t* buf, uint8_t len);
|
bool write(const uint8_t* buf, const uint8_t len);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set RF communication channel. The frequency used by a channel is
|
* Set RF communication channel. The frequency used by a channel is
|
||||||
* @param channel Which RF channel to communicate on, 0-254
|
* @param channel Which RF channel to communicate on, 0-254
|
||||||
*/
|
*/
|
||||||
void setChannel(uint8_t channel);
|
void setChannel(const uint8_t channel);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get RF communication channel
|
* Get RF communication channel
|
||||||
@ -82,10 +90,26 @@ public:
|
|||||||
|
|
||||||
int getRssiDBm();
|
int getRssiDBm();
|
||||||
|
|
||||||
bool setPALevel(int8_t level);
|
bool setPALevel(const int8_t level);
|
||||||
|
|
||||||
bool rxFifoAvailable();
|
bool rxFifoAvailable();
|
||||||
|
|
||||||
|
uint32_t getBaseFrequency() const;
|
||||||
|
static constexpr uint32_t getBaseFrequency(FrequencyBand_t band)
|
||||||
|
{
|
||||||
|
switch (band) {
|
||||||
|
case FrequencyBand_t::BAND_900:
|
||||||
|
return CMT_BASE_FREQ_900;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return CMT_BASE_FREQ_860;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FrequencyBand_t getFrequencyBand() const;
|
||||||
|
void setFrequencyBand(const FrequencyBand_t mode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Empty the RX (receive) FIFO buffers.
|
* Empty the RX (receive) FIFO buffers.
|
||||||
*/
|
*/
|
||||||
@ -109,4 +133,6 @@ private:
|
|||||||
int8_t _pin_cs;
|
int8_t _pin_cs;
|
||||||
int8_t _pin_fcs;
|
int8_t _pin_fcs;
|
||||||
uint32_t _spi_speed;
|
uint32_t _spi_speed;
|
||||||
|
|
||||||
|
FrequencyBand_t _frequencyBand = FrequencyBand_t::BAND_860;
|
||||||
};
|
};
|
||||||
@ -1,142 +0,0 @@
|
|||||||
#include "cmt_spi3.h"
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <driver/spi_master.h>
|
|
||||||
#include <esp_rom_gpio.h> // for esp_rom_gpio_connect_out_signal
|
|
||||||
|
|
||||||
SemaphoreHandle_t paramLock = NULL;
|
|
||||||
#define SPI_PARAM_LOCK() \
|
|
||||||
do { \
|
|
||||||
} while (xSemaphoreTake(paramLock, portMAX_DELAY) != pdPASS)
|
|
||||||
#define SPI_PARAM_UNLOCK() xSemaphoreGive(paramLock)
|
|
||||||
|
|
||||||
// for ESP32 this is the so-called HSPI
|
|
||||||
// for ESP32-S2/S3/C3 this nomenclature does not really exist anymore,
|
|
||||||
// it is simply the first externally usable hardware SPI master controller
|
|
||||||
#define SPI_CMT SPI2_HOST
|
|
||||||
|
|
||||||
spi_device_handle_t spi_reg, spi_fifo;
|
|
||||||
|
|
||||||
void cmt_spi3_init(int8_t pin_sdio, int8_t pin_clk, int8_t pin_cs, int8_t pin_fcs, uint32_t spi_speed)
|
|
||||||
{
|
|
||||||
paramLock = xSemaphoreCreateMutex();
|
|
||||||
|
|
||||||
spi_bus_config_t buscfg = {
|
|
||||||
.mosi_io_num = pin_sdio,
|
|
||||||
.miso_io_num = -1, // single wire MOSI/MISO
|
|
||||||
.sclk_io_num = pin_clk,
|
|
||||||
.quadwp_io_num = -1,
|
|
||||||
.quadhd_io_num = -1,
|
|
||||||
.max_transfer_sz = 32,
|
|
||||||
};
|
|
||||||
spi_device_interface_config_t devcfg = {
|
|
||||||
.command_bits = 1,
|
|
||||||
.address_bits = 7,
|
|
||||||
.dummy_bits = 0,
|
|
||||||
.mode = 0, // SPI mode 0
|
|
||||||
.cs_ena_pretrans = 1,
|
|
||||||
.cs_ena_posttrans = 1,
|
|
||||||
.clock_speed_hz = spi_speed,
|
|
||||||
.spics_io_num = pin_cs,
|
|
||||||
.flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE,
|
|
||||||
.queue_size = 1,
|
|
||||||
.pre_cb = NULL,
|
|
||||||
.post_cb = NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
ESP_ERROR_CHECK(spi_bus_initialize(SPI_CMT, &buscfg, SPI_DMA_DISABLED));
|
|
||||||
ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg, &spi_reg));
|
|
||||||
|
|
||||||
// FiFo
|
|
||||||
spi_device_interface_config_t devcfg2 = {
|
|
||||||
.command_bits = 0,
|
|
||||||
.address_bits = 0,
|
|
||||||
.dummy_bits = 0,
|
|
||||||
.mode = 0, // SPI mode 0
|
|
||||||
.cs_ena_pretrans = 2,
|
|
||||||
.cs_ena_posttrans = (uint8_t)(1 / (spi_speed * 10e6 * 2) + 2), // >2 us
|
|
||||||
.clock_speed_hz = spi_speed,
|
|
||||||
.spics_io_num = pin_fcs,
|
|
||||||
.flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE,
|
|
||||||
.queue_size = 1,
|
|
||||||
.pre_cb = NULL,
|
|
||||||
.post_cb = NULL,
|
|
||||||
};
|
|
||||||
ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg2, &spi_fifo));
|
|
||||||
|
|
||||||
esp_rom_gpio_connect_out_signal(pin_sdio, spi_periph_signal[SPI_CMT].spid_out, true, false);
|
|
||||||
delay(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cmt_spi3_write(uint8_t addr, uint8_t dat)
|
|
||||||
{
|
|
||||||
uint8_t tx_data;
|
|
||||||
tx_data = ~dat;
|
|
||||||
spi_transaction_t t = {
|
|
||||||
.cmd = 1,
|
|
||||||
.addr = ~addr,
|
|
||||||
.length = 8,
|
|
||||||
.tx_buffer = &tx_data,
|
|
||||||
.rx_buffer = NULL
|
|
||||||
};
|
|
||||||
SPI_PARAM_LOCK();
|
|
||||||
ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t));
|
|
||||||
SPI_PARAM_UNLOCK();
|
|
||||||
delayMicroseconds(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t cmt_spi3_read(uint8_t addr)
|
|
||||||
{
|
|
||||||
uint8_t rx_data;
|
|
||||||
spi_transaction_t t = {
|
|
||||||
.cmd = 0,
|
|
||||||
.addr = ~addr,
|
|
||||||
.length = 8,
|
|
||||||
.rxlength = 8,
|
|
||||||
.tx_buffer = NULL,
|
|
||||||
.rx_buffer = &rx_data
|
|
||||||
};
|
|
||||||
SPI_PARAM_LOCK();
|
|
||||||
ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t));
|
|
||||||
SPI_PARAM_UNLOCK();
|
|
||||||
delayMicroseconds(100);
|
|
||||||
return rx_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cmt_spi3_write_fifo(const uint8_t* buf, uint16_t len)
|
|
||||||
{
|
|
||||||
uint8_t tx_data;
|
|
||||||
|
|
||||||
spi_transaction_t t = {
|
|
||||||
.length = 8,
|
|
||||||
.tx_buffer = &tx_data, // reference to write data
|
|
||||||
.rx_buffer = NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
SPI_PARAM_LOCK();
|
|
||||||
for (uint8_t i = 0; i < len; i++) {
|
|
||||||
tx_data = ~buf[i]; // negate buffer contents
|
|
||||||
ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t));
|
|
||||||
delayMicroseconds(4); // > 4 us
|
|
||||||
}
|
|
||||||
SPI_PARAM_UNLOCK();
|
|
||||||
}
|
|
||||||
|
|
||||||
void cmt_spi3_read_fifo(uint8_t* buf, uint16_t len)
|
|
||||||
{
|
|
||||||
uint8_t rx_data;
|
|
||||||
|
|
||||||
spi_transaction_t t = {
|
|
||||||
.length = 8,
|
|
||||||
.rxlength = 8,
|
|
||||||
.tx_buffer = NULL,
|
|
||||||
.rx_buffer = &rx_data
|
|
||||||
};
|
|
||||||
|
|
||||||
SPI_PARAM_LOCK();
|
|
||||||
for (uint8_t i = 0; i < len; i++) {
|
|
||||||
ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t));
|
|
||||||
delayMicroseconds(4); // > 4 us
|
|
||||||
buf[i] = rx_data;
|
|
||||||
}
|
|
||||||
SPI_PARAM_UNLOCK();
|
|
||||||
}
|
|
||||||
155
lib/CMT2300a/cmt_spi3.cpp
Normal file
155
lib/CMT2300a/cmt_spi3.cpp
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
#include "cmt_spi3.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <driver/spi_master.h>
|
||||||
|
#include <SpiManager.h>
|
||||||
|
|
||||||
|
SemaphoreHandle_t paramLock = NULL;
|
||||||
|
#define SPI_PARAM_LOCK() \
|
||||||
|
do { \
|
||||||
|
} while (xSemaphoreTake(paramLock, portMAX_DELAY) != pdPASS)
|
||||||
|
#define SPI_PARAM_UNLOCK() xSemaphoreGive(paramLock)
|
||||||
|
|
||||||
|
static void IRAM_ATTR pre_cb(spi_transaction_t *trans) {
|
||||||
|
gpio_set_level(*reinterpret_cast<gpio_num_t*>(trans->user), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void IRAM_ATTR post_cb(spi_transaction_t *trans) {
|
||||||
|
gpio_set_level(*reinterpret_cast<gpio_num_t*>(trans->user), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_device_handle_t spi;
|
||||||
|
gpio_num_t cs_reg, cs_fifo;
|
||||||
|
|
||||||
|
void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int32_t spi_speed)
|
||||||
|
{
|
||||||
|
paramLock = xSemaphoreCreateMutex();
|
||||||
|
|
||||||
|
auto bus_config = std::make_shared<SpiBusConfig>(
|
||||||
|
static_cast<gpio_num_t>(pin_sdio),
|
||||||
|
GPIO_NUM_NC,
|
||||||
|
static_cast<gpio_num_t>(pin_clk)
|
||||||
|
);
|
||||||
|
|
||||||
|
spi_device_interface_config_t device_config {
|
||||||
|
.command_bits = 0, // set by transactions individually
|
||||||
|
.address_bits = 0, // set by transactions individually
|
||||||
|
.dummy_bits = 0,
|
||||||
|
.mode = 0, // SPI mode 0
|
||||||
|
.duty_cycle_pos = 0,
|
||||||
|
.cs_ena_pretrans = 2, // only 1 pre and post cycle would be required for register access
|
||||||
|
.cs_ena_posttrans = static_cast<uint8_t>(2 * spi_speed / 1000000), // >2 us
|
||||||
|
.clock_speed_hz = spi_speed,
|
||||||
|
.input_delay_ns = 0,
|
||||||
|
.spics_io_num = -1, // CS handled by callbacks
|
||||||
|
.flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE,
|
||||||
|
.queue_size = 1,
|
||||||
|
.pre_cb = pre_cb,
|
||||||
|
.post_cb = post_cb,
|
||||||
|
};
|
||||||
|
|
||||||
|
spi = SpiManagerInst.alloc_device("", bus_config, device_config);
|
||||||
|
if (!spi)
|
||||||
|
ESP_ERROR_CHECK(ESP_FAIL);
|
||||||
|
|
||||||
|
cs_reg = static_cast<gpio_num_t>(pin_cs);
|
||||||
|
ESP_ERROR_CHECK(gpio_reset_pin(cs_reg));
|
||||||
|
ESP_ERROR_CHECK(gpio_set_level(cs_reg, 1));
|
||||||
|
ESP_ERROR_CHECK(gpio_set_direction(cs_reg, GPIO_MODE_OUTPUT));
|
||||||
|
|
||||||
|
cs_fifo = static_cast<gpio_num_t>(pin_fcs);
|
||||||
|
ESP_ERROR_CHECK(gpio_reset_pin(cs_fifo));
|
||||||
|
ESP_ERROR_CHECK(gpio_set_level(cs_fifo, 1));
|
||||||
|
ESP_ERROR_CHECK(gpio_set_direction(cs_fifo, GPIO_MODE_OUTPUT));
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmt_spi3_write(const uint8_t addr, const uint8_t data)
|
||||||
|
{
|
||||||
|
spi_transaction_ext_t trans {
|
||||||
|
.base {
|
||||||
|
.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR,
|
||||||
|
.cmd = 0,
|
||||||
|
.addr = addr,
|
||||||
|
.length = 8,
|
||||||
|
.rxlength = 0,
|
||||||
|
.user = &cs_reg, // CS for register access
|
||||||
|
.tx_buffer = &data,
|
||||||
|
.rx_buffer = nullptr,
|
||||||
|
},
|
||||||
|
.command_bits = 1,
|
||||||
|
.address_bits = 7,
|
||||||
|
.dummy_bits = 0,
|
||||||
|
};
|
||||||
|
SPI_PARAM_LOCK();
|
||||||
|
ESP_ERROR_CHECK(spi_device_polling_transmit(spi, reinterpret_cast<spi_transaction_t*>(&trans)));
|
||||||
|
SPI_PARAM_UNLOCK();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t cmt_spi3_read(const uint8_t addr)
|
||||||
|
{
|
||||||
|
uint8_t data;
|
||||||
|
spi_transaction_ext_t trans {
|
||||||
|
.base {
|
||||||
|
.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR,
|
||||||
|
.cmd = 1,
|
||||||
|
.addr = addr,
|
||||||
|
.length = 0,
|
||||||
|
.rxlength = 8,
|
||||||
|
.user = &cs_reg, // CS for register access
|
||||||
|
.tx_buffer = nullptr,
|
||||||
|
.rx_buffer = &data,
|
||||||
|
},
|
||||||
|
.command_bits = 1,
|
||||||
|
.address_bits = 7,
|
||||||
|
.dummy_bits = 0,
|
||||||
|
};
|
||||||
|
SPI_PARAM_LOCK();
|
||||||
|
ESP_ERROR_CHECK(spi_device_polling_transmit(spi, reinterpret_cast<spi_transaction_t*>(&trans)));
|
||||||
|
SPI_PARAM_UNLOCK();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmt_spi3_write_fifo(const uint8_t* buf, const uint16_t len)
|
||||||
|
{
|
||||||
|
spi_transaction_t trans {
|
||||||
|
.flags = 0,
|
||||||
|
.cmd = 0,
|
||||||
|
.addr = 0,
|
||||||
|
.length = 8,
|
||||||
|
.rxlength = 0,
|
||||||
|
.user = &cs_fifo, // CS for FIFO access
|
||||||
|
.tx_buffer = nullptr,
|
||||||
|
.rx_buffer = nullptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
SPI_PARAM_LOCK();
|
||||||
|
spi_device_acquire_bus(spi, portMAX_DELAY);
|
||||||
|
for (uint8_t i = 0; i < len; i++) {
|
||||||
|
trans.tx_buffer = buf + i;
|
||||||
|
ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &trans));
|
||||||
|
}
|
||||||
|
spi_device_release_bus(spi);
|
||||||
|
SPI_PARAM_UNLOCK();
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmt_spi3_read_fifo(uint8_t* buf, const uint16_t len)
|
||||||
|
{
|
||||||
|
spi_transaction_t trans {
|
||||||
|
.flags = 0,
|
||||||
|
.cmd = 0,
|
||||||
|
.addr = 0,
|
||||||
|
.length = 0,
|
||||||
|
.rxlength = 8,
|
||||||
|
.user = &cs_fifo, // CS for FIFO access
|
||||||
|
.tx_buffer = nullptr,
|
||||||
|
.rx_buffer = nullptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
SPI_PARAM_LOCK();
|
||||||
|
spi_device_acquire_bus(spi, portMAX_DELAY);
|
||||||
|
for (uint8_t i = 0; i < len; i++) {
|
||||||
|
trans.rx_buffer = buf + i;
|
||||||
|
ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &trans));
|
||||||
|
}
|
||||||
|
spi_device_release_bus(spi);
|
||||||
|
SPI_PARAM_UNLOCK();
|
||||||
|
}
|
||||||
@ -3,12 +3,20 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
void cmt_spi3_init(int8_t pin_sdio, int8_t pin_clk, int8_t pin_cs, int8_t pin_fcs, uint32_t spi_speed);
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
void cmt_spi3_write(uint8_t addr, uint8_t dat);
|
void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int32_t spi_speed);
|
||||||
uint8_t cmt_spi3_read(uint8_t addr);
|
|
||||||
|
|
||||||
void cmt_spi3_write_fifo(const uint8_t* p_buf, uint16_t len);
|
void cmt_spi3_write(const uint8_t addr, const uint8_t dat);
|
||||||
void cmt_spi3_read_fifo(uint8_t* p_buf, uint16_t len);
|
uint8_t cmt_spi3_read(const uint8_t addr);
|
||||||
|
|
||||||
|
void cmt_spi3_write_fifo(const uint8_t* p_buf, const uint16_t len);
|
||||||
|
void cmt_spi3_read_fifo(uint8_t* p_buf, const uint16_t len);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
51
lib/CpuTemperature/src/CpuTemperature.cpp
Normal file
51
lib/CpuTemperature/src/CpuTemperature.cpp
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Thomas Basler and others
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "CpuTemperature.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#if defined(CONFIG_IDF_TARGET_ESP32)
|
||||||
|
// there is no official API available on the original ESP32
|
||||||
|
extern "C" {
|
||||||
|
uint8_t temprature_sens_read();
|
||||||
|
}
|
||||||
|
#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
|
#include "driver/temp_sensor.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CpuTemperatureClass CpuTemperature;
|
||||||
|
|
||||||
|
float CpuTemperatureClass::read()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
|
||||||
|
float temperature = NAN;
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
#if defined(CONFIG_IDF_TARGET_ESP32)
|
||||||
|
uint8_t raw = temprature_sens_read();
|
||||||
|
ESP_LOGV(TAG, "Raw temperature value: %d", raw);
|
||||||
|
temperature = (raw - 32) / 1.8f;
|
||||||
|
success = (raw != 128);
|
||||||
|
#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
|
temp_sensor_config_t tsens = TSENS_CONFIG_DEFAULT();
|
||||||
|
temp_sensor_set_config(tsens);
|
||||||
|
temp_sensor_start();
|
||||||
|
#if defined(CONFIG_IDF_TARGET_ESP32S3) && (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 4, 3))
|
||||||
|
#error \
|
||||||
|
"ESP32-S3 internal temperature sensor requires ESP IDF V4.4.3 or higher. See https://github.com/esphome/issues/issues/4271"
|
||||||
|
#endif
|
||||||
|
esp_err_t result = temp_sensor_read_celsius(&temperature);
|
||||||
|
temp_sensor_stop();
|
||||||
|
success = (result == ESP_OK);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (success && std::isfinite(temperature)) {
|
||||||
|
return temperature;
|
||||||
|
} else {
|
||||||
|
ESP_LOGD(TAG, "Ignoring invalid temperature (success=%d, value=%.1f)", success, temperature);
|
||||||
|
return NAN;
|
||||||
|
}
|
||||||
|
}
|
||||||
14
lib/CpuTemperature/src/CpuTemperature.h
Normal file
14
lib/CpuTemperature/src/CpuTemperature.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
class CpuTemperatureClass {
|
||||||
|
public:
|
||||||
|
float read();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex _mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern CpuTemperatureClass CpuTemperature;
|
||||||
3
lib/Frozen/AUTHORS
Normal file
3
lib/Frozen/AUTHORS
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
serge-sans-paille <sguelton@quarkslab.com>
|
||||||
|
Jérôme Dumesnil <jerome.dumesnil@gmail.com>
|
||||||
|
Chris Beck <chbeck@tesla.com>
|
||||||
202
lib/Frozen/LICENSE
Normal file
202
lib/Frozen/LICENSE
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright 2017 Quarkslab
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
245
lib/Frozen/README.rst
Normal file
245
lib/Frozen/README.rst
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
Frozen
|
||||||
|
######
|
||||||
|
|
||||||
|
.. image:: https://travis-ci.org/serge-sans-paille/frozen.svg?branch=master
|
||||||
|
:target: https://travis-ci.org/serge-sans-paille/frozen
|
||||||
|
|
||||||
|
Header-only library that provides 0 cost initialization for immutable containers, fixed-size containers, and various algorithms.
|
||||||
|
|
||||||
|
Frozen provides:
|
||||||
|
|
||||||
|
- immutable (a.k.a. frozen), ``constexpr``-compatible versions of ``std::set``,
|
||||||
|
``std::unordered_set``, ``std::map`` and ``std::unordered_map``.
|
||||||
|
|
||||||
|
- fixed-capacity, ``constinit``-compatible versions of ``std::map`` and
|
||||||
|
``std::unordered_map`` with immutable, compile-time selected keys mapped
|
||||||
|
to mutable values.
|
||||||
|
|
||||||
|
- 0-cost initialization version of ``std::search`` for frozen needles using
|
||||||
|
Boyer-Moore or Knuth-Morris-Pratt algorithms.
|
||||||
|
|
||||||
|
|
||||||
|
The ``unordered_*`` containers are guaranteed *perfect* (a.k.a. no hash
|
||||||
|
collision) and the extra storage is linear with respect to the number of keys.
|
||||||
|
|
||||||
|
Once initialized, the container keys cannot be updated, and in exchange, lookups
|
||||||
|
are faster. And initialization is free when ``constexpr`` or ``constinit`` is
|
||||||
|
used :-).
|
||||||
|
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
Just copy the ``include/frozen`` directory somewhere and points to it using the ``-I`` flag. Alternatively, using CMake:
|
||||||
|
|
||||||
|
.. code:: sh
|
||||||
|
|
||||||
|
> mkdir build
|
||||||
|
> cd build
|
||||||
|
> cmake -D CMAKE_BUILD_TYPE=Release ..
|
||||||
|
> make install
|
||||||
|
|
||||||
|
|
||||||
|
Installation via CMake populates configuration files into the ``/usr/local/share``
|
||||||
|
directory which can be consumed by CMake's ``find_package`` instrinsic function.
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
|
||||||
|
A C++ compiler that supports C++14. Clang version 5 is a good pick, GCC version
|
||||||
|
6 lags behind in terms of ``constexpr`` compilation time (At least on my
|
||||||
|
setup), but compiles correctly. Visual Studio 2017 also works correctly!
|
||||||
|
|
||||||
|
Note that gcc 5 isn't supported. (Here's an `old compat branch`_ where a small amount of stuff was ported.)
|
||||||
|
|
||||||
|
.. _old compat branch: https://github.com/cbeck88/frozen/tree/gcc5-support
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
Compiled with ``-std=c++14`` flag:
|
||||||
|
|
||||||
|
.. code:: C++
|
||||||
|
|
||||||
|
#include <frozen/set.h>
|
||||||
|
|
||||||
|
constexpr frozen::set<int, 4> some_ints = {1,2,3,5};
|
||||||
|
|
||||||
|
constexpr bool letitgo = some_ints.count(8);
|
||||||
|
|
||||||
|
extern int n;
|
||||||
|
bool letitgoooooo = some_ints.count(n);
|
||||||
|
|
||||||
|
|
||||||
|
As the constructor and some methods are ``constexpr``, it's also possible to write weird stuff like:
|
||||||
|
|
||||||
|
.. code:: C++
|
||||||
|
|
||||||
|
#include <frozen/set.h>
|
||||||
|
|
||||||
|
template<std::size_t N>
|
||||||
|
std::enable_if_t< frozen::set<int, 3>{{1,11,111}}.count(N), int> foo();
|
||||||
|
|
||||||
|
String support is built-in:
|
||||||
|
|
||||||
|
.. code:: C++
|
||||||
|
|
||||||
|
#include <frozen/unordered_map.h>
|
||||||
|
#include <frozen/string.h>
|
||||||
|
|
||||||
|
constexpr frozen::unordered_map<frozen::string, int, 2> olaf = {
|
||||||
|
{"19", 19},
|
||||||
|
{"31", 31},
|
||||||
|
};
|
||||||
|
constexpr auto val = olaf.at("19");
|
||||||
|
|
||||||
|
The associative containers have different functionality with and without ``constexpr``.
|
||||||
|
With ``constexpr``, frozen maps have immutable keys and values. Without ``constexpr``, the
|
||||||
|
values can be updated in runtime (the keys, however, remain immutable):
|
||||||
|
|
||||||
|
.. code:: C++
|
||||||
|
|
||||||
|
|
||||||
|
#include <frozen/unordered_map.h>
|
||||||
|
#include <frozen/string.h>
|
||||||
|
|
||||||
|
static constinit frozen::unordered_map<frozen::string, frozen::string, 2> voice = {
|
||||||
|
{"Anna", "???"},
|
||||||
|
{"Elsa", "???"}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
voice.at("Anna") = "Kristen";
|
||||||
|
voice.at("Elsa") = "Idina";
|
||||||
|
}
|
||||||
|
|
||||||
|
You may also prefer a slightly more DRY initialization syntax:
|
||||||
|
|
||||||
|
.. code:: C++
|
||||||
|
|
||||||
|
#include <frozen/set.h>
|
||||||
|
|
||||||
|
constexpr auto some_ints = frozen::make_set<int>({1,2,3,5});
|
||||||
|
|
||||||
|
There are similar ``make_X`` functions for all frozen containers.
|
||||||
|
|
||||||
|
Exception Handling
|
||||||
|
------------------
|
||||||
|
|
||||||
|
For compatibility with STL's API, Frozen may eventually throw exceptions, as in
|
||||||
|
``frozen::map::at``. If you build your code without exception support, or
|
||||||
|
define the ``FROZEN_NO_EXCEPTIONS`` macro variable, they will be turned into an
|
||||||
|
``std::abort``.
|
||||||
|
|
||||||
|
Extending
|
||||||
|
---------
|
||||||
|
|
||||||
|
Just like the regular C++14 container, you can specialize the hash function,
|
||||||
|
the key equality comparator for ``unordered_*`` containers, and the comparison
|
||||||
|
functions for the ordered version.
|
||||||
|
|
||||||
|
It's also possible to specialize the ``frozen::elsa`` structure used for
|
||||||
|
hashing. Note that unlike `std::hash`, the hasher also takes a seed in addition
|
||||||
|
to the value being hashed.
|
||||||
|
|
||||||
|
.. code:: C++
|
||||||
|
|
||||||
|
template <class T> struct elsa {
|
||||||
|
// in case of collisions, different seeds are tried
|
||||||
|
constexpr std::size_t operator()(T const &value, std::size_t seed) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
Ideally, the hash function should have nice statistical properties like *pairwise-independence*:
|
||||||
|
|
||||||
|
If ``x`` and ``y`` are different values, the chance that ``elsa<T>{}(x, seed) == elsa<T>{}(y, seed)``
|
||||||
|
should be very low for a random value of ``seed``.
|
||||||
|
|
||||||
|
Note that frozen always ultimately produces a perfect hash function, and you will always have ``O(1)``
|
||||||
|
lookup with frozen. It's just that if the input hasher performs poorly, the search will take longer and
|
||||||
|
your project will take longer to compile.
|
||||||
|
|
||||||
|
Troubleshooting
|
||||||
|
---------------
|
||||||
|
|
||||||
|
If you hit a message like this:
|
||||||
|
|
||||||
|
.. code:: none
|
||||||
|
|
||||||
|
[...]
|
||||||
|
note: constexpr evaluation hit maximum step limit; possible infinite loop?
|
||||||
|
|
||||||
|
Then either you've got a very big container and you should increase Clang's
|
||||||
|
thresholds, using ``-fconstexpr-steps=1000000000`` for instance, or the hash
|
||||||
|
functions used by frozen do not suit your data, and you should change them, as
|
||||||
|
in the following:
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
struct olaf {
|
||||||
|
constexpr std::size_t operator()(frozen::string const &value, std::size_t seed) const { return seed ^ value[0];}
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr frozen::unordered_set<frozen::string, 2, olaf/*custom hash*/> hans = { "a", "b" };
|
||||||
|
|
||||||
|
Tests and Benchmarks
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Using hand-written Makefiles crafted with love and care:
|
||||||
|
|
||||||
|
.. code:: sh
|
||||||
|
|
||||||
|
> # running tests
|
||||||
|
> make -C tests check
|
||||||
|
> # running benchmarks
|
||||||
|
> make -C benchmarks GOOGLE_BENCHMARK_PREFIX=<GOOGLE-BENCHMARK_INSTALL_DIR>
|
||||||
|
|
||||||
|
Using CMake to generate a static configuration build system:
|
||||||
|
|
||||||
|
.. code:: sh
|
||||||
|
|
||||||
|
> mkdir build
|
||||||
|
> cd build
|
||||||
|
> cmake -D CMAKE_BUILD_TYPE=Release \
|
||||||
|
-D frozen.benchmark=ON \
|
||||||
|
-G <"Unix Makefiles" or "Ninja"> ..
|
||||||
|
> # building the tests and benchmarks...
|
||||||
|
> make # ... with make
|
||||||
|
> ninja # ... with ninja
|
||||||
|
> cmake --build . # ... with cmake
|
||||||
|
> # running the tests...
|
||||||
|
> make test # ... with make
|
||||||
|
> ninja test # ... with ninja
|
||||||
|
> cmake --build . --target test # ... with cmake
|
||||||
|
> ctest # ... with ctest
|
||||||
|
> # running the benchmarks...
|
||||||
|
> make benchmark # ... with make
|
||||||
|
> ninja benchmark # ... with ninja
|
||||||
|
> cmake --build . --target benchmark # ... with cmake
|
||||||
|
|
||||||
|
Using CMake to generate an IDE build system with test and benchmark targets
|
||||||
|
|
||||||
|
.. code:: sh
|
||||||
|
|
||||||
|
> mkdir build
|
||||||
|
> cd build
|
||||||
|
> cmake -D frozen.benchmark=ON -G <"Xcode" or "Visual Studio 15 2017"> ..
|
||||||
|
> # using cmake to drive the IDE build, test, and benchmark
|
||||||
|
> cmake --build . --config Release
|
||||||
|
> cmake --build . --target test
|
||||||
|
> cmake --build . --target benchmark
|
||||||
|
|
||||||
|
|
||||||
|
Credits
|
||||||
|
-------
|
||||||
|
|
||||||
|
The perfect hashing is strongly inspired by the blog post `Throw away the keys:
|
||||||
|
Easy, Minimal Perfect Hashing <http://stevehanov.ca/blog/index.php?id=119>`_.
|
||||||
|
|
||||||
|
Thanks a lot to Jérôme Dumesnil for his high-quality reviews, and to Chris Beck
|
||||||
|
for his contributions on perfect hashing.
|
||||||
|
|
||||||
|
Contact
|
||||||
|
-------
|
||||||
|
|
||||||
|
Serge sans Paille ``<serge.guelton@telecom-bretagne.eu>``
|
||||||
|
|
||||||
12
lib/Frozen/frozen/CMakeLists.txt
Normal file
12
lib/Frozen/frozen/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
target_sources(frozen-headers INTERFACE
|
||||||
|
"${prefix}/frozen/algorithm.h"
|
||||||
|
"${prefix}/frozen/map.h"
|
||||||
|
"${prefix}/frozen/random.h"
|
||||||
|
"${prefix}/frozen/set.h"
|
||||||
|
"${prefix}/frozen/string.h"
|
||||||
|
"${prefix}/frozen/unordered_map.h"
|
||||||
|
"${prefix}/frozen/unordered_set.h"
|
||||||
|
"${prefix}/frozen/bits/algorithms.h"
|
||||||
|
"${prefix}/frozen/bits/basic_types.h"
|
||||||
|
"${prefix}/frozen/bits/elsa.h"
|
||||||
|
"${prefix}/frozen/bits/pmh.h")
|
||||||
198
lib/Frozen/frozen/algorithm.h
Normal file
198
lib/Frozen/frozen/algorithm.h
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FROZEN_LETITGO_ALGORITHM_H
|
||||||
|
#define FROZEN_LETITGO_ALGORITHM_H
|
||||||
|
|
||||||
|
#include "frozen/bits/basic_types.h"
|
||||||
|
#include "frozen/bits/version.h"
|
||||||
|
#include "frozen/string.h"
|
||||||
|
|
||||||
|
namespace frozen {
|
||||||
|
|
||||||
|
// 'search' implementation if C++17 is not available
|
||||||
|
// https://en.cppreference.com/w/cpp/algorithm/search
|
||||||
|
template<class ForwardIterator, class Searcher>
|
||||||
|
ForwardIterator search(ForwardIterator first, ForwardIterator last, const Searcher & searcher)
|
||||||
|
{
|
||||||
|
return searcher(first, last).first;
|
||||||
|
}
|
||||||
|
|
||||||
|
// text book implementation from
|
||||||
|
// https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm
|
||||||
|
|
||||||
|
template <std::size_t size> class knuth_morris_pratt_searcher {
|
||||||
|
bits::carray<std::ptrdiff_t, size> step_;
|
||||||
|
bits::carray<char, size> needle_;
|
||||||
|
|
||||||
|
static constexpr bits::carray<std::ptrdiff_t, size>
|
||||||
|
build_kmp_cache(char const (&needle)[size + 1]) {
|
||||||
|
std::ptrdiff_t cnd = 0;
|
||||||
|
bits::carray<std::ptrdiff_t, size> cache(-1);
|
||||||
|
for (std::size_t pos = 1; pos < size; ++pos) {
|
||||||
|
if (needle[pos] == needle[cnd]) {
|
||||||
|
cache[pos] = cache[cnd];
|
||||||
|
cnd += 1;
|
||||||
|
} else {
|
||||||
|
cache[pos] = cnd;
|
||||||
|
cnd = cache[cnd];
|
||||||
|
while (cnd >= 0 && needle[pos] != needle[cnd])
|
||||||
|
cnd = cache[cnd];
|
||||||
|
cnd += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr knuth_morris_pratt_searcher(char const (&needle)[size + 1])
|
||||||
|
: step_{build_kmp_cache(needle)}, needle_(needle) {}
|
||||||
|
|
||||||
|
template <class ForwardIterator>
|
||||||
|
constexpr std::pair<ForwardIterator, ForwardIterator> operator()(ForwardIterator first, ForwardIterator last) const {
|
||||||
|
std::size_t i = 0;
|
||||||
|
ForwardIterator iter = first;
|
||||||
|
while (iter != last) {
|
||||||
|
if (needle_[i] == *iter) {
|
||||||
|
if (i == (size - 1))
|
||||||
|
return { iter - i, iter - i + size };
|
||||||
|
++i;
|
||||||
|
++iter;
|
||||||
|
} else {
|
||||||
|
if (step_[i] > -1) {
|
||||||
|
i = step_[i];
|
||||||
|
} else {
|
||||||
|
++iter;
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { last, last };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t N>
|
||||||
|
constexpr knuth_morris_pratt_searcher<N - 1> make_knuth_morris_pratt_searcher(char const (&needle)[N]) {
|
||||||
|
return {needle};
|
||||||
|
}
|
||||||
|
|
||||||
|
// text book implementation from
|
||||||
|
// https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm
|
||||||
|
|
||||||
|
template <std::size_t size> class boyer_moore_searcher {
|
||||||
|
using skip_table_type = bits::carray<std::ptrdiff_t, sizeof(char) << 8>;
|
||||||
|
using suffix_table_type = bits::carray<std::ptrdiff_t, size>;
|
||||||
|
|
||||||
|
skip_table_type skip_table_;
|
||||||
|
suffix_table_type suffix_table_;
|
||||||
|
bits::carray<char, size> needle_;
|
||||||
|
|
||||||
|
constexpr auto build_skip_table(char const (&needle)[size + 1]) {
|
||||||
|
skip_table_type skip_table(size);
|
||||||
|
for (std::size_t i = 0; i < size - 1; ++i)
|
||||||
|
skip_table[needle[i]] -= i + 1;
|
||||||
|
return skip_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool is_prefix(char const (&needle)[size + 1], std::size_t pos) {
|
||||||
|
std::size_t suffixlen = size - pos;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < suffixlen; i++) {
|
||||||
|
if (needle[i] != needle[pos + i])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr std::size_t suffix_length(char const (&needle)[size + 1],
|
||||||
|
std::size_t pos) {
|
||||||
|
// increment suffix length slen to the first mismatch or beginning
|
||||||
|
// of the word
|
||||||
|
for (std::size_t slen = 0; slen < pos ; slen++)
|
||||||
|
if (needle[pos - slen] != needle[size - 1 - slen])
|
||||||
|
return slen;
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto build_suffix_table(char const (&needle)[size + 1]) {
|
||||||
|
suffix_table_type suffix;
|
||||||
|
std::ptrdiff_t last_prefix_index = size - 1;
|
||||||
|
|
||||||
|
// first loop
|
||||||
|
for (std::ptrdiff_t p = size - 1; p >= 0; p--) {
|
||||||
|
if (is_prefix(needle, p + 1))
|
||||||
|
last_prefix_index = p + 1;
|
||||||
|
|
||||||
|
suffix[p] = last_prefix_index + (size - 1 - p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// second loop
|
||||||
|
for (std::size_t p = 0; p < size - 1; p++) {
|
||||||
|
auto slen = suffix_length(needle, p);
|
||||||
|
if (needle[p - slen] != needle[size - 1 - slen])
|
||||||
|
suffix[size - 1 - slen] = size - 1 - p + slen;
|
||||||
|
|
||||||
|
}
|
||||||
|
return suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr boyer_moore_searcher(char const (&needle)[size + 1])
|
||||||
|
: skip_table_{build_skip_table(needle)},
|
||||||
|
suffix_table_{build_suffix_table(needle)},
|
||||||
|
needle_(needle) {}
|
||||||
|
|
||||||
|
template <class RandomAccessIterator>
|
||||||
|
constexpr std::pair<RandomAccessIterator, RandomAccessIterator> operator()(RandomAccessIterator first, RandomAccessIterator last) const {
|
||||||
|
if (size == 0)
|
||||||
|
return { first, first };
|
||||||
|
|
||||||
|
if (size > size_t(last - first))
|
||||||
|
return { last, last };
|
||||||
|
|
||||||
|
RandomAccessIterator iter = first + size - 1;
|
||||||
|
while (true) {
|
||||||
|
std::ptrdiff_t j = size - 1;
|
||||||
|
while (j > 0 && (*iter == needle_[j])) {
|
||||||
|
--iter;
|
||||||
|
--j;
|
||||||
|
}
|
||||||
|
if (j == 0 && *iter == needle_[0])
|
||||||
|
return { iter, iter + size};
|
||||||
|
|
||||||
|
std::ptrdiff_t jump = std::max(skip_table_[*iter], suffix_table_[j]);
|
||||||
|
if (jump >= last - iter)
|
||||||
|
return { last, last };
|
||||||
|
iter += jump;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t N>
|
||||||
|
constexpr boyer_moore_searcher<N - 1> make_boyer_moore_searcher(char const (&needle)[N]) {
|
||||||
|
return {needle};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace frozen
|
||||||
|
|
||||||
|
#endif
|
||||||
235
lib/Frozen/frozen/bits/algorithms.h
Normal file
235
lib/Frozen/frozen/bits/algorithms.h
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FROZEN_LETITGO_BITS_ALGORITHMS_H
|
||||||
|
#define FROZEN_LETITGO_BITS_ALGORITHMS_H
|
||||||
|
|
||||||
|
#include "frozen/bits/basic_types.h"
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
namespace frozen {
|
||||||
|
|
||||||
|
namespace bits {
|
||||||
|
|
||||||
|
auto constexpr next_highest_power_of_two(std::size_t v) {
|
||||||
|
// https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
|
||||||
|
constexpr auto trip_count = std::numeric_limits<decltype(v)>::digits;
|
||||||
|
v--;
|
||||||
|
for(std::size_t i = 1; i < trip_count; i <<= 1)
|
||||||
|
v |= v >> i;
|
||||||
|
v++;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
auto constexpr log(T v) {
|
||||||
|
std::size_t n = 0;
|
||||||
|
while (v > 1) {
|
||||||
|
n += 1;
|
||||||
|
v >>= 1;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr std::size_t bit_weight(std::size_t n) {
|
||||||
|
return (n <= 8*sizeof(unsigned int))
|
||||||
|
+ (n <= 8*sizeof(unsigned long))
|
||||||
|
+ (n <= 8*sizeof(unsigned long long))
|
||||||
|
+ (n <= 128);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int select_uint_least(std::integral_constant<std::size_t, 4>);
|
||||||
|
unsigned long select_uint_least(std::integral_constant<std::size_t, 3>);
|
||||||
|
unsigned long long select_uint_least(std::integral_constant<std::size_t, 2>);
|
||||||
|
template<std::size_t N>
|
||||||
|
unsigned long long select_uint_least(std::integral_constant<std::size_t, N>) {
|
||||||
|
static_assert(N < 2, "unsupported type size");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<std::size_t N>
|
||||||
|
using select_uint_least_t = decltype(select_uint_least(std::integral_constant<std::size_t, bit_weight(N)>()));
|
||||||
|
|
||||||
|
template <typename Iter, typename Compare>
|
||||||
|
constexpr auto min_element(Iter begin, const Iter end,
|
||||||
|
Compare const &compare) {
|
||||||
|
auto result = begin;
|
||||||
|
while (begin != end) {
|
||||||
|
if (compare(*begin, *result)) {
|
||||||
|
result = begin;
|
||||||
|
}
|
||||||
|
++begin;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
constexpr void cswap(T &a, T &b) {
|
||||||
|
auto tmp = a;
|
||||||
|
a = b;
|
||||||
|
b = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, class U>
|
||||||
|
constexpr void cswap(std::pair<T, U> & a, std::pair<T, U> & b) {
|
||||||
|
cswap(a.first, b.first);
|
||||||
|
cswap(a.second, b.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class... Tys, std::size_t... Is>
|
||||||
|
constexpr void cswap(std::tuple<Tys...> &a, std::tuple<Tys...> &b, std::index_sequence<Is...>) {
|
||||||
|
using swallow = int[];
|
||||||
|
(void) swallow{(cswap(std::get<Is>(a), std::get<Is>(b)), 0)...};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class... Tys>
|
||||||
|
constexpr void cswap(std::tuple<Tys...> &a, std::tuple<Tys...> &b) {
|
||||||
|
cswap(a, b, std::make_index_sequence<sizeof...(Tys)>());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Iter>
|
||||||
|
constexpr void iter_swap(Iter a, Iter b) {
|
||||||
|
cswap(*a, *b);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Iterator, class Compare>
|
||||||
|
constexpr Iterator partition(Iterator left, Iterator right, Compare const &compare) {
|
||||||
|
auto pivot = left + (right - left) / 2;
|
||||||
|
iter_swap(right, pivot);
|
||||||
|
pivot = right;
|
||||||
|
for (auto it = left; 0 < right - it; ++it) {
|
||||||
|
if (compare(*it, *pivot)) {
|
||||||
|
iter_swap(it, left);
|
||||||
|
left++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iter_swap(pivot, left);
|
||||||
|
pivot = left;
|
||||||
|
return pivot;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Iterator, class Compare>
|
||||||
|
constexpr void quicksort(Iterator left, Iterator right, Compare const &compare) {
|
||||||
|
while (0 < right - left) {
|
||||||
|
auto new_pivot = bits::partition(left, right, compare);
|
||||||
|
quicksort(left, new_pivot, compare);
|
||||||
|
left = new_pivot + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Container, class Compare>
|
||||||
|
constexpr Container quicksort(Container const &array,
|
||||||
|
Compare const &compare) {
|
||||||
|
Container res = array;
|
||||||
|
quicksort(res.begin(), res.end() - 1, compare);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, class Compare> struct LowerBound {
|
||||||
|
T const &value_;
|
||||||
|
Compare const &compare_;
|
||||||
|
constexpr LowerBound(T const &value, Compare const &compare)
|
||||||
|
: value_(value), compare_(compare) {}
|
||||||
|
|
||||||
|
template <class ForwardIt>
|
||||||
|
inline constexpr ForwardIt doit_fast(ForwardIt first,
|
||||||
|
std::integral_constant<std::size_t, 0>) {
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class ForwardIt, std::size_t N>
|
||||||
|
inline constexpr ForwardIt doit_fast(ForwardIt first,
|
||||||
|
std::integral_constant<std::size_t, N>) {
|
||||||
|
auto constexpr step = N / 2;
|
||||||
|
static_assert(N/2 == N - N / 2 - 1, "power of two minus 1");
|
||||||
|
auto it = first + step;
|
||||||
|
auto next_it = compare_(*it, value_) ? it + 1 : first;
|
||||||
|
return doit_fast(next_it, std::integral_constant<std::size_t, N / 2>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class ForwardIt, std::size_t N>
|
||||||
|
inline constexpr ForwardIt doitfirst(ForwardIt first, std::integral_constant<std::size_t, N>, std::integral_constant<bool, true>) {
|
||||||
|
return doit_fast(first, std::integral_constant<std::size_t, N>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class ForwardIt, std::size_t N>
|
||||||
|
inline constexpr ForwardIt doitfirst(ForwardIt first, std::integral_constant<std::size_t, N>, std::integral_constant<bool, false>) {
|
||||||
|
auto constexpr next_power = next_highest_power_of_two(N);
|
||||||
|
auto constexpr next_start = next_power / 2 - 1;
|
||||||
|
auto it = first + next_start;
|
||||||
|
if (compare_(*it, value_)) {
|
||||||
|
auto constexpr next = N - next_start - 1;
|
||||||
|
return doitfirst(it + 1, std::integral_constant<std::size_t, next>{}, std::integral_constant<bool, next_highest_power_of_two(next) - 1 == next>{});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return doit_fast(first, std::integral_constant<std::size_t, next_start>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class ForwardIt>
|
||||||
|
inline constexpr ForwardIt doitfirst(ForwardIt first, std::integral_constant<std::size_t, 1>, std::integral_constant<bool, false>) {
|
||||||
|
return doit_fast(first, std::integral_constant<std::size_t, 1>{});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t N, class ForwardIt, class T, class Compare>
|
||||||
|
constexpr ForwardIt lower_bound(ForwardIt first, const T &value, Compare const &compare) {
|
||||||
|
return LowerBound<T, Compare>{value, compare}.doitfirst(first, std::integral_constant<std::size_t, N>{}, std::integral_constant<bool, next_highest_power_of_two(N) - 1 == N>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t N, class Compare, class ForwardIt, class T>
|
||||||
|
constexpr bool binary_search(ForwardIt first, const T &value,
|
||||||
|
Compare const &compare) {
|
||||||
|
ForwardIt where = lower_bound<N>(first, value, compare);
|
||||||
|
return (!(where == first + N) && !(compare(value, *where)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class InputIt1, class InputIt2>
|
||||||
|
constexpr bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2)
|
||||||
|
{
|
||||||
|
for (; first1 != last1; ++first1, ++first2) {
|
||||||
|
if (!(*first1 == *first2)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class InputIt1, class InputIt2>
|
||||||
|
constexpr bool lexicographical_compare(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2)
|
||||||
|
{
|
||||||
|
for (; (first1 != last1) && (first2 != last2); ++first1, ++first2) {
|
||||||
|
if (*first1 < *first2)
|
||||||
|
return true;
|
||||||
|
if (*first2 < *first1)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (first1 == last1) && (first2 != last2);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace bits
|
||||||
|
} // namespace frozen
|
||||||
|
|
||||||
|
#endif
|
||||||
198
lib/Frozen/frozen/bits/basic_types.h
Normal file
198
lib/Frozen/frozen/bits/basic_types.h
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FROZEN_LETITGO_BASIC_TYPES_H
|
||||||
|
#define FROZEN_LETITGO_BASIC_TYPES_H
|
||||||
|
|
||||||
|
#include "frozen/bits/exceptions.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <utility>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace frozen {
|
||||||
|
|
||||||
|
namespace bits {
|
||||||
|
|
||||||
|
// used as a fake argument for frozen::make_set and frozen::make_map in the case of N=0
|
||||||
|
struct ignored_arg {};
|
||||||
|
|
||||||
|
template <class T, std::size_t N>
|
||||||
|
class cvector {
|
||||||
|
T data [N] = {}; // zero-initialization for scalar type T, default-initialized otherwise
|
||||||
|
std::size_t dsize = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Container typdefs
|
||||||
|
using value_type = T;
|
||||||
|
using reference = value_type &;
|
||||||
|
using const_reference = const value_type &;
|
||||||
|
using pointer = value_type *;
|
||||||
|
using const_pointer = const value_type *;
|
||||||
|
using iterator = pointer;
|
||||||
|
using const_iterator = const_pointer;
|
||||||
|
using size_type = std::size_t;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
constexpr cvector(void) = default;
|
||||||
|
constexpr cvector(size_type count, const T& value) : dsize(count) {
|
||||||
|
for (std::size_t i = 0; i < N; ++i)
|
||||||
|
data[i] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterators
|
||||||
|
constexpr iterator begin() noexcept { return data; }
|
||||||
|
constexpr iterator end() noexcept { return data + dsize; }
|
||||||
|
constexpr const_iterator begin() const noexcept { return data; }
|
||||||
|
constexpr const_iterator end() const noexcept { return data + dsize; }
|
||||||
|
|
||||||
|
// Capacity
|
||||||
|
constexpr size_type size() const { return dsize; }
|
||||||
|
|
||||||
|
// Element access
|
||||||
|
constexpr reference operator[](std::size_t index) { return data[index]; }
|
||||||
|
constexpr const_reference operator[](std::size_t index) const { return data[index]; }
|
||||||
|
|
||||||
|
constexpr reference back() { return data[dsize - 1]; }
|
||||||
|
constexpr const_reference back() const { return data[dsize - 1]; }
|
||||||
|
|
||||||
|
// Modifiers
|
||||||
|
constexpr void push_back(const T & a) { data[dsize++] = a; }
|
||||||
|
constexpr void push_back(T && a) { data[dsize++] = std::move(a); }
|
||||||
|
constexpr void pop_back() { --dsize; }
|
||||||
|
|
||||||
|
constexpr void clear() { dsize = 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, std::size_t N>
|
||||||
|
class carray {
|
||||||
|
T data_ [N] = {}; // zero-initialization for scalar type T, default-initialized otherwise
|
||||||
|
|
||||||
|
template <class Iter, std::size_t... I>
|
||||||
|
constexpr carray(Iter iter, std::index_sequence<I...>)
|
||||||
|
: data_{((void)I, *iter++)...} {}
|
||||||
|
template <std::size_t... I>
|
||||||
|
constexpr carray(const T& value, std::index_sequence<I...>)
|
||||||
|
: data_{((void)I, value)...} {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Container typdefs
|
||||||
|
using value_type = T;
|
||||||
|
using reference = value_type &;
|
||||||
|
using const_reference = const value_type &;
|
||||||
|
using pointer = value_type *;
|
||||||
|
using const_pointer = const value_type *;
|
||||||
|
using iterator = pointer;
|
||||||
|
using const_iterator = const_pointer;
|
||||||
|
using size_type = std::size_t;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
constexpr carray() = default;
|
||||||
|
constexpr carray(const value_type& val)
|
||||||
|
: carray(val, std::make_index_sequence<N>()) {}
|
||||||
|
template <typename U, std::enable_if_t<std::is_convertible<U, T>::value, std::size_t> M>
|
||||||
|
constexpr carray(U const (&init)[M])
|
||||||
|
: carray(init, std::make_index_sequence<N>())
|
||||||
|
{
|
||||||
|
static_assert(M >= N, "Cannot initialize a carray with an smaller array");
|
||||||
|
}
|
||||||
|
template <typename U, std::enable_if_t<std::is_convertible<U, T>::value, std::size_t> M>
|
||||||
|
constexpr carray(std::array<U, M> const &init)
|
||||||
|
: carray(init.begin(), std::make_index_sequence<N>())
|
||||||
|
{
|
||||||
|
static_assert(M >= N, "Cannot initialize a carray with an smaller array");
|
||||||
|
}
|
||||||
|
template <typename U, std::enable_if_t<std::is_convertible<U, T>::value>* = nullptr>
|
||||||
|
constexpr carray(std::initializer_list<U> init)
|
||||||
|
: carray(init.begin(), std::make_index_sequence<N>())
|
||||||
|
{
|
||||||
|
// clang & gcc doesn't recognize init.size() as a constexpr
|
||||||
|
// static_assert(init.size() >= N, "Cannot initialize a carray with an smaller initializer list");
|
||||||
|
}
|
||||||
|
template <typename U, std::enable_if_t<std::is_convertible<U, T>::value>* = nullptr>
|
||||||
|
constexpr carray(const carray<U, N>& rhs)
|
||||||
|
: carray(rhs.begin(), std::make_index_sequence<N>())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterators
|
||||||
|
constexpr iterator begin() noexcept { return data_; }
|
||||||
|
constexpr const_iterator begin() const noexcept { return data_; }
|
||||||
|
constexpr iterator end() noexcept { return data_ + N; }
|
||||||
|
constexpr const_iterator end() const noexcept { return data_ + N; }
|
||||||
|
|
||||||
|
// Capacity
|
||||||
|
constexpr size_type size() const { return N; }
|
||||||
|
constexpr size_type max_size() const { return N; }
|
||||||
|
|
||||||
|
// Element access
|
||||||
|
constexpr reference operator[](std::size_t index) { return data_[index]; }
|
||||||
|
constexpr const_reference operator[](std::size_t index) const { return data_[index]; }
|
||||||
|
|
||||||
|
constexpr reference at(std::size_t index) {
|
||||||
|
if (index > N)
|
||||||
|
FROZEN_THROW_OR_ABORT(std::out_of_range("Index (" + std::to_string(index) + ") out of bound (" + std::to_string(N) + ')'));
|
||||||
|
return data_[index];
|
||||||
|
}
|
||||||
|
constexpr const_reference at(std::size_t index) const {
|
||||||
|
if (index > N)
|
||||||
|
FROZEN_THROW_OR_ABORT(std::out_of_range("Index (" + std::to_string(index) + ") out of bound (" + std::to_string(N) + ')'));
|
||||||
|
return data_[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr reference front() { return data_[0]; }
|
||||||
|
constexpr const_reference front() const { return data_[0]; }
|
||||||
|
|
||||||
|
constexpr reference back() { return data_[N - 1]; }
|
||||||
|
constexpr const_reference back() const { return data_[N - 1]; }
|
||||||
|
|
||||||
|
constexpr value_type* data() noexcept { return data_; }
|
||||||
|
constexpr const value_type* data() const noexcept { return data_; }
|
||||||
|
};
|
||||||
|
template <class T>
|
||||||
|
class carray<T, 0> {
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Container typdefs
|
||||||
|
using value_type = T;
|
||||||
|
using reference = value_type &;
|
||||||
|
using const_reference = const value_type &;
|
||||||
|
using pointer = value_type *;
|
||||||
|
using const_pointer = const value_type *;
|
||||||
|
using iterator = pointer;
|
||||||
|
using const_iterator = const_pointer;
|
||||||
|
using size_type = std::size_t;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
constexpr carray(void) = default;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace bits
|
||||||
|
|
||||||
|
} // namespace frozen
|
||||||
|
|
||||||
|
#endif
|
||||||
40
lib/Frozen/frozen/bits/constexpr_assert.h
Normal file
40
lib/Frozen/frozen/bits/constexpr_assert.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FROZEN_LETITGO_CONSTEXPR_ASSERT_H
|
||||||
|
#define FROZEN_LETITGO_CONSTEXPR_ASSERT_H
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
|
||||||
|
// FIXME: find a way to implement that correctly for msvc
|
||||||
|
#define constexpr_assert(cond, msg)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define constexpr_assert(cond, msg)\
|
||||||
|
assert(cond && msg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
66
lib/Frozen/frozen/bits/defines.h
Normal file
66
lib/Frozen/frozen/bits/defines.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FROZEN_LETITGO_DEFINES_H
|
||||||
|
#define FROZEN_LETITGO_DEFINES_H
|
||||||
|
|
||||||
|
#if defined(_MSVC_LANG) && !(defined(__EDG__) && defined(__clang__)) // TRANSITION, VSO#273681
|
||||||
|
#define FROZEN_LETITGO_IS_MSVC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Code taken from https://stackoverflow.com/questions/43639122/which-values-can-msvc-lang-have
|
||||||
|
#if defined(FROZEN_LETITGO_IS_MSVC)
|
||||||
|
#if _MSVC_LANG > 201402
|
||||||
|
#define FROZEN_LETITGO_HAS_CXX17 1
|
||||||
|
#else /* _MSVC_LANG > 201402 */
|
||||||
|
#define FROZEN_LETITGO_HAS_CXX17 0
|
||||||
|
#endif /* _MSVC_LANG > 201402 */
|
||||||
|
#else /* _MSVC_LANG etc. */
|
||||||
|
#if __cplusplus > 201402
|
||||||
|
#define FROZEN_LETITGO_HAS_CXX17 1
|
||||||
|
#else /* __cplusplus > 201402 */
|
||||||
|
#define FROZEN_LETITGO_HAS_CXX17 0
|
||||||
|
#endif /* __cplusplus > 201402 */
|
||||||
|
#endif /* _MSVC_LANG etc. */
|
||||||
|
// End if taken code
|
||||||
|
|
||||||
|
#if FROZEN_LETITGO_HAS_CXX17 == 1 && defined(FROZEN_LETITGO_IS_MSVC)
|
||||||
|
#define FROZEN_LETITGO_HAS_STRING_VIEW // We assume Visual Studio always has string_view in C++17
|
||||||
|
#else
|
||||||
|
#if FROZEN_LETITGO_HAS_CXX17 == 1 && __has_include(<string_view>)
|
||||||
|
#define FROZEN_LETITGO_HAS_STRING_VIEW
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cpp_char8_t
|
||||||
|
#define FROZEN_LETITGO_HAS_CHAR8T
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __cpp_deduction_guides >= 201703L
|
||||||
|
#define FROZEN_LETITGO_HAS_DEDUCTION_GUIDES
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __cpp_lib_constexpr_string >= 201907L
|
||||||
|
#define FROZEN_LETITGO_HAS_CONSTEXPR_STRING
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // FROZEN_LETITGO_DEFINES_H
|
||||||
57
lib/Frozen/frozen/bits/elsa.h
Normal file
57
lib/Frozen/frozen/bits/elsa.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FROZEN_LETITGO_ELSA_H
|
||||||
|
#define FROZEN_LETITGO_ELSA_H
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace frozen {
|
||||||
|
|
||||||
|
template <class T = void> struct elsa {
|
||||||
|
static_assert(std::is_integral<T>::value || std::is_enum<T>::value,
|
||||||
|
"only supports integral types, specialize for other types");
|
||||||
|
|
||||||
|
constexpr std::size_t operator()(T const &value, std::size_t seed) const {
|
||||||
|
std::size_t key = seed ^ static_cast<std::size_t>(value);
|
||||||
|
key = (~key) + (key << 21); // key = (key << 21) - key - 1;
|
||||||
|
key = key ^ (key >> 24);
|
||||||
|
key = (key + (key << 3)) + (key << 8); // key * 265
|
||||||
|
key = key ^ (key >> 14);
|
||||||
|
key = (key + (key << 2)) + (key << 4); // key * 21
|
||||||
|
key = key ^ (key >> 28);
|
||||||
|
key = key + (key << 31);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct elsa<void> {
|
||||||
|
template<class T>
|
||||||
|
constexpr std::size_t operator()(T const &value, std::size_t seed) const {
|
||||||
|
return elsa<T>{}(value, seed);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T=void> using anna = elsa<T>;
|
||||||
|
} // namespace frozen
|
||||||
|
|
||||||
|
#endif
|
||||||
41
lib/Frozen/frozen/bits/elsa_std.h
Normal file
41
lib/Frozen/frozen/bits/elsa_std.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#ifndef FROZEN_LETITGO_BITS_ELSA_STD_H
|
||||||
|
#define FROZEN_LETITGO_BITS_ELSA_STD_H
|
||||||
|
|
||||||
|
#include "defines.h"
|
||||||
|
#include "elsa.h"
|
||||||
|
#include "hash_string.h"
|
||||||
|
|
||||||
|
#ifdef FROZEN_LETITGO_HAS_STRING_VIEW
|
||||||
|
#include <string_view>
|
||||||
|
#endif
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace frozen {
|
||||||
|
|
||||||
|
#ifdef FROZEN_LETITGO_HAS_STRING_VIEW
|
||||||
|
|
||||||
|
template <typename CharT> struct elsa<std::basic_string_view<CharT>>
|
||||||
|
{
|
||||||
|
constexpr std::size_t operator()(const std::basic_string_view<CharT>& value) const {
|
||||||
|
return hash_string(value);
|
||||||
|
}
|
||||||
|
constexpr std::size_t operator()(const std::basic_string_view<CharT>& value, std::size_t seed) const {
|
||||||
|
return hash_string(value, seed);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename CharT> struct elsa<std::basic_string<CharT>>
|
||||||
|
{
|
||||||
|
constexpr std::size_t operator()(const std::basic_string<CharT>& value) const {
|
||||||
|
return hash_string(value);
|
||||||
|
}
|
||||||
|
constexpr std::size_t operator()(const std::basic_string<CharT>& value, std::size_t seed) const {
|
||||||
|
return hash_string(value, seed);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace frozen
|
||||||
|
|
||||||
|
#endif // FROZEN_LETITGO_BITS_ELSA_STD_H
|
||||||
39
lib/Frozen/frozen/bits/exceptions.h
Normal file
39
lib/Frozen/frozen/bits/exceptions.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FROZEN_LETITGO_EXCEPTIONS_H
|
||||||
|
#define FROZEN_LETITGO_EXCEPTIONS_H
|
||||||
|
|
||||||
|
#if defined(FROZEN_NO_EXCEPTIONS) || (defined(_MSC_VER) && !defined(_CPPUNWIND)) || (!defined(_MSC_VER) && !defined(__cpp_exceptions))
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#define FROZEN_THROW_OR_ABORT(_) std::abort()
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
#define FROZEN_THROW_OR_ABORT(err) throw err
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
28
lib/Frozen/frozen/bits/hash_string.h
Normal file
28
lib/Frozen/frozen/bits/hash_string.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#ifndef FROZEN_LETITGO_BITS_HASH_STRING_H
|
||||||
|
#define FROZEN_LETITGO_BITS_HASH_STRING_H
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
namespace frozen {
|
||||||
|
|
||||||
|
template <typename String>
|
||||||
|
constexpr std::size_t hash_string(const String& value) {
|
||||||
|
std::size_t d = 5381;
|
||||||
|
for (const auto& c : value)
|
||||||
|
d = d * 33 + static_cast<std::size_t>(c);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
|
||||||
|
// With the lowest bits removed, based on experimental setup.
|
||||||
|
template <typename String>
|
||||||
|
constexpr std::size_t hash_string(const String& value, std::size_t seed) {
|
||||||
|
std::size_t d = (0x811c9dc5 ^ seed) * static_cast<std::size_t>(0x01000193);
|
||||||
|
for (const auto& c : value)
|
||||||
|
d = (d ^ static_cast<std::size_t>(c)) * static_cast<std::size_t>(0x01000193);
|
||||||
|
return d >> 8 ;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace frozen
|
||||||
|
|
||||||
|
#endif // FROZEN_LETITGO_BITS_HASH_STRING_H
|
||||||
56
lib/Frozen/frozen/bits/mpl.h
Normal file
56
lib/Frozen/frozen/bits/mpl.h
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2022 Giel van Schijndel
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FROZEN_LETITGO_BITS_MPL_H
|
||||||
|
#define FROZEN_LETITGO_BITS_MPL_H
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace frozen {
|
||||||
|
|
||||||
|
namespace bits {
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
template <class, std::size_t>
|
||||||
|
class carray;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct remove_cv : std::remove_cv<T> {};
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
struct remove_cv<std::pair<T...>> {
|
||||||
|
using type = std::pair<typename remove_cv<T>::type...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, std::size_t N>
|
||||||
|
struct remove_cv<carray<T, N>> {
|
||||||
|
using type = carray<typename remove_cv<T>::type, N>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using remove_cv_t = typename remove_cv<T>::type;
|
||||||
|
|
||||||
|
} // namespace bits
|
||||||
|
|
||||||
|
} // namespace frozen
|
||||||
|
|
||||||
|
#endif
|
||||||
254
lib/Frozen/frozen/bits/pmh.h
Normal file
254
lib/Frozen/frozen/bits/pmh.h
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// inspired from http://stevehanov.ca/blog/index.php?id=119
|
||||||
|
#ifndef FROZEN_LETITGO_PMH_H
|
||||||
|
#define FROZEN_LETITGO_PMH_H
|
||||||
|
|
||||||
|
#include "frozen/bits/algorithms.h"
|
||||||
|
#include "frozen/bits/basic_types.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace frozen {
|
||||||
|
|
||||||
|
namespace bits {
|
||||||
|
|
||||||
|
// Function object for sorting buckets in decreasing order of size
|
||||||
|
struct bucket_size_compare {
|
||||||
|
template <typename B>
|
||||||
|
bool constexpr operator()(B const &b0,
|
||||||
|
B const &b1) const {
|
||||||
|
return b0.size() > b1.size();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Step One in pmh routine is to take all items and hash them into buckets,
|
||||||
|
// with some collisions. Then process those buckets further to build a perfect
|
||||||
|
// hash function.
|
||||||
|
// pmh_buckets represents the initial placement into buckets.
|
||||||
|
|
||||||
|
template <std::size_t M>
|
||||||
|
struct pmh_buckets {
|
||||||
|
// Step 0: Bucket max is 2 * sqrt M
|
||||||
|
// TODO: Come up with justification for this, should it not be O(log M)?
|
||||||
|
static constexpr auto bucket_max = 2 * (1u << (log(M) / 2));
|
||||||
|
|
||||||
|
using bucket_t = cvector<std::size_t, bucket_max>;
|
||||||
|
carray<bucket_t, M> buckets;
|
||||||
|
std::uint64_t seed;
|
||||||
|
|
||||||
|
// Represents a reference to a bucket. This is used because the buckets
|
||||||
|
// have to be sorted, but buckets are big, making it slower than sorting refs
|
||||||
|
struct bucket_ref {
|
||||||
|
unsigned hash;
|
||||||
|
const bucket_t * ptr;
|
||||||
|
|
||||||
|
// Forward some interface of bucket
|
||||||
|
using value_type = typename bucket_t::value_type;
|
||||||
|
using const_iterator = typename bucket_t::const_iterator;
|
||||||
|
|
||||||
|
constexpr auto size() const { return ptr->size(); }
|
||||||
|
constexpr const auto & operator[](std::size_t idx) const { return (*ptr)[idx]; }
|
||||||
|
constexpr auto begin() const { return ptr->begin(); }
|
||||||
|
constexpr auto end() const { return ptr->end(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make a bucket_ref for each bucket
|
||||||
|
template <std::size_t... Is>
|
||||||
|
carray<bucket_ref, M> constexpr make_bucket_refs(std::index_sequence<Is...>) const {
|
||||||
|
return {{ bucket_ref{Is, &buckets[Is]}... }};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Makes a bucket_ref for each bucket and sorts them by size
|
||||||
|
carray<bucket_ref, M> constexpr get_sorted_buckets() const {
|
||||||
|
carray<bucket_ref, M> result{this->make_bucket_refs(std::make_index_sequence<M>())};
|
||||||
|
bits::quicksort(result.begin(), result.end() - 1, bucket_size_compare{});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t M, class Item, std::size_t N, class Hash, class Key, class PRG>
|
||||||
|
pmh_buckets<M> constexpr make_pmh_buckets(const carray<Item, N> & items,
|
||||||
|
Hash const & hash,
|
||||||
|
Key const & key,
|
||||||
|
PRG & prg) {
|
||||||
|
using result_t = pmh_buckets<M>;
|
||||||
|
// Continue until all items are placed without exceeding bucket_max
|
||||||
|
while (1) {
|
||||||
|
result_t result{};
|
||||||
|
result.seed = prg();
|
||||||
|
bool rejected = false;
|
||||||
|
for (std::size_t i = 0; i < items.size(); ++i) {
|
||||||
|
auto & bucket = result.buckets[hash(key(items[i]), static_cast<std::size_t>(result.seed)) % M];
|
||||||
|
if (bucket.size() >= result_t::bucket_max) {
|
||||||
|
rejected = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bucket.push_back(i);
|
||||||
|
}
|
||||||
|
if (!rejected) { return result; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if an item appears in a cvector
|
||||||
|
template<class T, std::size_t N>
|
||||||
|
constexpr bool all_different_from(cvector<T, N> & data, T & a) {
|
||||||
|
for (std::size_t i = 0; i < data.size(); ++i)
|
||||||
|
if (data[i] == a)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Represents either an index to a data item array, or a seed to be used with
|
||||||
|
// a hasher. Seed must have high bit of 1, value has high bit of zero.
|
||||||
|
struct seed_or_index {
|
||||||
|
using value_type = std::uint64_t;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr value_type MINUS_ONE = std::numeric_limits<value_type>::max();
|
||||||
|
static constexpr value_type HIGH_BIT = ~(MINUS_ONE >> 1);
|
||||||
|
|
||||||
|
value_type value_ = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr value_type value() const { return value_; }
|
||||||
|
constexpr bool is_seed() const { return value_ & HIGH_BIT; }
|
||||||
|
|
||||||
|
constexpr seed_or_index(bool is_seed, value_type value)
|
||||||
|
: value_(is_seed ? (value | HIGH_BIT) : (value & ~HIGH_BIT)) {}
|
||||||
|
|
||||||
|
constexpr seed_or_index() = default;
|
||||||
|
constexpr seed_or_index(const seed_or_index &) = default;
|
||||||
|
constexpr seed_or_index & operator =(const seed_or_index &) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Represents the perfect hash function created by pmh algorithm
|
||||||
|
template <std::size_t M, class Hasher>
|
||||||
|
struct pmh_tables : private Hasher {
|
||||||
|
std::uint64_t first_seed_;
|
||||||
|
carray<seed_or_index, M> first_table_;
|
||||||
|
carray<std::size_t, M> second_table_;
|
||||||
|
|
||||||
|
constexpr pmh_tables(
|
||||||
|
std::uint64_t first_seed,
|
||||||
|
carray<seed_or_index, M> first_table,
|
||||||
|
carray<std::size_t, M> second_table,
|
||||||
|
Hasher hash) noexcept
|
||||||
|
: Hasher(hash)
|
||||||
|
, first_seed_(first_seed)
|
||||||
|
, first_table_(first_table)
|
||||||
|
, second_table_(second_table)
|
||||||
|
{}
|
||||||
|
|
||||||
|
constexpr Hasher const& hash_function() const noexcept {
|
||||||
|
return static_cast<Hasher const&>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename KeyType>
|
||||||
|
constexpr std::size_t lookup(const KeyType & key) const {
|
||||||
|
return lookup(key, hash_function());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks up a given key, to find its expected index in carray<Item, N>
|
||||||
|
// Always returns a valid index, must use KeyEqual test after to confirm.
|
||||||
|
template <typename KeyType, typename HasherType>
|
||||||
|
constexpr std::size_t lookup(const KeyType & key, const HasherType& hasher) const {
|
||||||
|
auto const d = first_table_[hasher(key, static_cast<std::size_t>(first_seed_)) % M];
|
||||||
|
if (!d.is_seed()) { return static_cast<std::size_t>(d.value()); } // this is narrowing std::uint64 -> std::size_t but should be fine
|
||||||
|
else { return second_table_[hasher(key, static_cast<std::size_t>(d.value())) % M]; }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make pmh tables for given items, hash function, prg, etc.
|
||||||
|
template <std::size_t M, class Item, std::size_t N, class Hash, class Key, class PRG>
|
||||||
|
pmh_tables<M, Hash> constexpr make_pmh_tables(const carray<Item, N> &
|
||||||
|
items,
|
||||||
|
Hash const &hash,
|
||||||
|
Key const &key,
|
||||||
|
PRG prg) {
|
||||||
|
// Step 1: Place all of the keys into buckets
|
||||||
|
auto step_one = make_pmh_buckets<M>(items, hash, key, prg);
|
||||||
|
|
||||||
|
// Step 2: Sort the buckets to process the ones with the most items first.
|
||||||
|
auto buckets = step_one.get_sorted_buckets();
|
||||||
|
|
||||||
|
// Special value for unused slots. This is purposefully the index
|
||||||
|
// one-past-the-end of 'items' to function as a sentinel value. Both to avoid
|
||||||
|
// the need to apply the KeyEqual predicate and to be easily convertible to
|
||||||
|
// end().
|
||||||
|
// Unused entries in both hash tables (G and H) have to contain this value.
|
||||||
|
const auto UNUSED = items.size();
|
||||||
|
|
||||||
|
// G becomes the first hash table in the resulting pmh function
|
||||||
|
carray<seed_or_index, M> G({false, UNUSED});
|
||||||
|
|
||||||
|
// H becomes the second hash table in the resulting pmh function
|
||||||
|
carray<std::size_t, M> H(UNUSED);
|
||||||
|
|
||||||
|
// Step 3: Map the items in buckets into hash tables.
|
||||||
|
for (const auto & bucket : buckets) {
|
||||||
|
auto const bsize = bucket.size();
|
||||||
|
|
||||||
|
if (bsize == 1) {
|
||||||
|
// Store index to the (single) item in G
|
||||||
|
// assert(bucket.hash == hash(key(items[bucket[0]]), step_one.seed) % M);
|
||||||
|
G[bucket.hash] = {false, static_cast<std::uint64_t>(bucket[0])};
|
||||||
|
} else if (bsize > 1) {
|
||||||
|
|
||||||
|
// Repeatedly try different H of d until we find a hash function
|
||||||
|
// that places all items in the bucket into free slots
|
||||||
|
seed_or_index d{true, prg()};
|
||||||
|
cvector<std::size_t, decltype(step_one)::bucket_max> bucket_slots;
|
||||||
|
|
||||||
|
while (bucket_slots.size() < bsize) {
|
||||||
|
auto slot = hash(key(items[bucket[bucket_slots.size()]]), static_cast<std::size_t>(d.value())) % M;
|
||||||
|
|
||||||
|
if (H[slot] != UNUSED || !all_different_from(bucket_slots, slot)) {
|
||||||
|
bucket_slots.clear();
|
||||||
|
d = {true, prg()};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket_slots.push_back(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put successful seed in G, and put indices to items in their slots
|
||||||
|
// assert(bucket.hash == hash(key(items[bucket[0]]), step_one.seed) % M);
|
||||||
|
G[bucket.hash] = d;
|
||||||
|
for (std::size_t i = 0; i < bsize; ++i)
|
||||||
|
H[bucket_slots[i]] = bucket[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {step_one.seed, G, H, hash};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace bits
|
||||||
|
|
||||||
|
} // namespace frozen
|
||||||
|
|
||||||
|
#endif
|
||||||
30
lib/Frozen/frozen/bits/version.h
Normal file
30
lib/Frozen/frozen/bits/version.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FROZEN_LETITGO_VERSION_H
|
||||||
|
#define FROZEN_LETITGO_VERSION_H
|
||||||
|
|
||||||
|
#define FROZEN_MAJOR_VERSION 1
|
||||||
|
#define FROZEN_MINOR_VERSION 1
|
||||||
|
#define FROZEN_PATCH_VERSION 1
|
||||||
|
|
||||||
|
#endif
|
||||||
357
lib/Frozen/frozen/map.h
Normal file
357
lib/Frozen/frozen/map.h
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FROZEN_LETITGO_MAP_H
|
||||||
|
#define FROZEN_LETITGO_MAP_H
|
||||||
|
|
||||||
|
#include "frozen/bits/algorithms.h"
|
||||||
|
#include "frozen/bits/basic_types.h"
|
||||||
|
#include "frozen/bits/constexpr_assert.h"
|
||||||
|
#include "frozen/bits/exceptions.h"
|
||||||
|
#include "frozen/bits/mpl.h"
|
||||||
|
#include "frozen/bits/version.h"
|
||||||
|
|
||||||
|
#include <iterator>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace frozen {
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
template <class Comparator> class CompareKey : private Comparator {
|
||||||
|
public:
|
||||||
|
constexpr Comparator const& key_comp() const noexcept {
|
||||||
|
return static_cast<Comparator const&>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr CompareKey(Comparator const &comparator)
|
||||||
|
: Comparator(comparator) {}
|
||||||
|
|
||||||
|
template <class Key1, class Key2, class Value>
|
||||||
|
constexpr int operator()(std::pair<Key1, Value> const &self,
|
||||||
|
std::pair<Key2, Value> const &other) const {
|
||||||
|
return key_comp()(std::get<0>(self), std::get<0>(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Key1, class Key2, class Value>
|
||||||
|
constexpr int operator()(Key1 const &self_key,
|
||||||
|
std::pair<Key2, Value> const &other) const {
|
||||||
|
return key_comp()(self_key, std::get<0>(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Key1, class Key2, class Value>
|
||||||
|
constexpr int operator()(std::pair<Key1, Value> const &self,
|
||||||
|
Key2 const &other_key) const {
|
||||||
|
return key_comp()(std::get<0>(self), other_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Key1, class Key2>
|
||||||
|
constexpr int operator()(Key1 const &self_key, Key2 const &other_key) const {
|
||||||
|
return key_comp()(self_key, other_key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace impl
|
||||||
|
|
||||||
|
template <class Key, class Value, std::size_t N, class Compare = std::less<Key>>
|
||||||
|
class map : private impl::CompareKey<Compare> {
|
||||||
|
using container_type = bits::carray<std::pair<const Key, Value>, N>;
|
||||||
|
container_type items_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using key_type = Key;
|
||||||
|
using mapped_type = Value;
|
||||||
|
using value_type = typename container_type::value_type;
|
||||||
|
using size_type = typename container_type::size_type;
|
||||||
|
using difference_type = typename container_type::difference_type;
|
||||||
|
using key_compare = Compare;
|
||||||
|
using value_compare = impl::CompareKey<Compare>;
|
||||||
|
using reference = typename container_type::reference;
|
||||||
|
using const_reference = typename container_type::const_reference;
|
||||||
|
using pointer = typename container_type::pointer;
|
||||||
|
using const_pointer = typename container_type::const_pointer;
|
||||||
|
using iterator = typename container_type::iterator;
|
||||||
|
using const_iterator = typename container_type::const_iterator;
|
||||||
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||||
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/* constructors */
|
||||||
|
constexpr map(container_type items, Compare const &compare)
|
||||||
|
: impl::CompareKey<Compare>{compare}
|
||||||
|
, items_{bits::quicksort(bits::remove_cv_t<container_type>(items), value_comp())} {}
|
||||||
|
|
||||||
|
explicit constexpr map(container_type items)
|
||||||
|
: map{items, Compare{}} {}
|
||||||
|
|
||||||
|
constexpr map(std::initializer_list<value_type> items, Compare const &compare)
|
||||||
|
: map{container_type {items}, compare} {
|
||||||
|
constexpr_assert(items.size() == N, "Inconsistent initializer_list size and type size argument");
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr map(std::initializer_list<value_type> items)
|
||||||
|
: map{items, Compare{}} {}
|
||||||
|
|
||||||
|
/* element access */
|
||||||
|
constexpr Value const& at(Key const &key) const {
|
||||||
|
return at_impl(*this, key);
|
||||||
|
}
|
||||||
|
constexpr Value& at(Key const &key) {
|
||||||
|
return at_impl(*this, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* iterators */
|
||||||
|
constexpr iterator begin() { return items_.begin(); }
|
||||||
|
constexpr const_iterator begin() const { return items_.begin(); }
|
||||||
|
constexpr const_iterator cbegin() const { return items_.begin(); }
|
||||||
|
constexpr iterator end() { return items_.end(); }
|
||||||
|
constexpr const_iterator end() const { return items_.end(); }
|
||||||
|
constexpr const_iterator cend() const { return items_.end(); }
|
||||||
|
|
||||||
|
constexpr reverse_iterator rbegin() { return reverse_iterator{items_.end()}; }
|
||||||
|
constexpr const_reverse_iterator rbegin() const { return const_reverse_iterator{items_.end()}; }
|
||||||
|
constexpr const_reverse_iterator crbegin() const { return const_reverse_iterator{items_.end()}; }
|
||||||
|
constexpr reverse_iterator rend() { return reverse_iterator{items_.begin()}; }
|
||||||
|
constexpr const_reverse_iterator rend() const { return const_reverse_iterator{items_.begin()}; }
|
||||||
|
constexpr const_reverse_iterator crend() const { return const_reverse_iterator{items_.begin()}; }
|
||||||
|
|
||||||
|
/* capacity */
|
||||||
|
constexpr bool empty() const { return !N; }
|
||||||
|
constexpr size_type size() const { return N; }
|
||||||
|
constexpr size_type max_size() const { return N; }
|
||||||
|
|
||||||
|
/* lookup */
|
||||||
|
|
||||||
|
template <class KeyType>
|
||||||
|
constexpr std::size_t count(KeyType const &key) const {
|
||||||
|
return bits::binary_search<N>(items_.begin(), key, value_comp());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class KeyType>
|
||||||
|
constexpr const_iterator find(KeyType const &key) const {
|
||||||
|
return map::find_impl(*this, key);
|
||||||
|
}
|
||||||
|
template <class KeyType>
|
||||||
|
constexpr iterator find(KeyType const &key) {
|
||||||
|
return map::find_impl(*this, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class KeyType>
|
||||||
|
constexpr bool contains(KeyType const &key) const {
|
||||||
|
return this->find(key) != this->end();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class KeyType>
|
||||||
|
constexpr std::pair<const_iterator, const_iterator>
|
||||||
|
equal_range(KeyType const &key) const {
|
||||||
|
return equal_range_impl(*this, key);
|
||||||
|
}
|
||||||
|
template <class KeyType>
|
||||||
|
constexpr std::pair<iterator, iterator> equal_range(KeyType const &key) {
|
||||||
|
return equal_range_impl(*this, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class KeyType>
|
||||||
|
constexpr const_iterator lower_bound(KeyType const &key) const {
|
||||||
|
return lower_bound_impl(*this, key);
|
||||||
|
}
|
||||||
|
template <class KeyType>
|
||||||
|
constexpr iterator lower_bound(KeyType const &key) {
|
||||||
|
return lower_bound_impl(*this, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class KeyType>
|
||||||
|
constexpr const_iterator upper_bound(KeyType const &key) const {
|
||||||
|
return upper_bound_impl(*this, key);
|
||||||
|
}
|
||||||
|
template <class KeyType>
|
||||||
|
constexpr iterator upper_bound(KeyType const &key) {
|
||||||
|
return upper_bound_impl(*this, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* observers */
|
||||||
|
constexpr const key_compare& key_comp() const { return value_comp().key_comp(); }
|
||||||
|
constexpr const value_compare& value_comp() const { return static_cast<impl::CompareKey<Compare> const&>(*this); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <class This, class KeyType>
|
||||||
|
static inline constexpr auto& at_impl(This&& self, KeyType const &key) {
|
||||||
|
auto where = self.find(key);
|
||||||
|
if (where != self.end())
|
||||||
|
return where->second;
|
||||||
|
else
|
||||||
|
FROZEN_THROW_OR_ABORT(std::out_of_range("unknown key"));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class This, class KeyType>
|
||||||
|
static inline constexpr auto find_impl(This&& self, KeyType const &key) {
|
||||||
|
auto where = self.lower_bound(key);
|
||||||
|
if (where != self.end() && !self.value_comp()(key, *where))
|
||||||
|
return where;
|
||||||
|
else
|
||||||
|
return self.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class This, class KeyType>
|
||||||
|
static inline constexpr auto equal_range_impl(This&& self, KeyType const &key) {
|
||||||
|
auto lower = self.lower_bound(key);
|
||||||
|
using lower_t = decltype(lower);
|
||||||
|
if (lower != self.end() && !self.value_comp()(key, *lower))
|
||||||
|
return std::pair<lower_t, lower_t>{lower, lower + 1};
|
||||||
|
else
|
||||||
|
return std::pair<lower_t, lower_t>{lower, lower};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class This, class KeyType>
|
||||||
|
static inline constexpr auto lower_bound_impl(This&& self, KeyType const &key) -> decltype(self.end()) {
|
||||||
|
return bits::lower_bound<N>(self.items_.begin(), key, self.value_comp());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class This, class KeyType>
|
||||||
|
static inline constexpr auto upper_bound_impl(This&& self, KeyType const &key) {
|
||||||
|
auto lower = self.lower_bound(key);
|
||||||
|
if (lower != self.end() && !self.value_comp()(key, *lower))
|
||||||
|
return lower + 1;
|
||||||
|
else
|
||||||
|
return lower;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Key, class Value, class Compare>
|
||||||
|
class map<Key, Value, 0, Compare> : private impl::CompareKey<Compare> {
|
||||||
|
using container_type = bits::carray<std::pair<Key, Value>, 0>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using key_type = Key;
|
||||||
|
using mapped_type = Value;
|
||||||
|
using value_type = typename container_type::value_type;
|
||||||
|
using size_type = typename container_type::size_type;
|
||||||
|
using difference_type = typename container_type::difference_type;
|
||||||
|
using key_compare = Compare;
|
||||||
|
using value_compare = impl::CompareKey<Compare>;
|
||||||
|
using reference = typename container_type::reference;
|
||||||
|
using const_reference = typename container_type::const_reference;
|
||||||
|
using pointer = typename container_type::pointer;
|
||||||
|
using const_pointer = typename container_type::const_pointer;
|
||||||
|
using iterator = pointer;
|
||||||
|
using const_iterator = const_pointer;
|
||||||
|
using reverse_iterator = pointer;
|
||||||
|
using const_reverse_iterator = const_pointer;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/* constructors */
|
||||||
|
constexpr map(const map &other) = default;
|
||||||
|
constexpr map(std::initializer_list<value_type>, Compare const &compare)
|
||||||
|
: impl::CompareKey<Compare>{compare} {}
|
||||||
|
constexpr map(std::initializer_list<value_type> items)
|
||||||
|
: map{items, Compare{}} {}
|
||||||
|
|
||||||
|
/* element access */
|
||||||
|
template <class KeyType>
|
||||||
|
constexpr mapped_type at(KeyType const &) const {
|
||||||
|
FROZEN_THROW_OR_ABORT(std::out_of_range("invalid key"));
|
||||||
|
}
|
||||||
|
template <class KeyType>
|
||||||
|
constexpr mapped_type at(KeyType const &) {
|
||||||
|
FROZEN_THROW_OR_ABORT(std::out_of_range("invalid key"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* iterators */
|
||||||
|
constexpr iterator begin() { return nullptr; }
|
||||||
|
constexpr const_iterator begin() const { return nullptr; }
|
||||||
|
constexpr const_iterator cbegin() const { return nullptr; }
|
||||||
|
constexpr iterator end() { return nullptr; }
|
||||||
|
constexpr const_iterator end() const { return nullptr; }
|
||||||
|
constexpr const_iterator cend() const { return nullptr; }
|
||||||
|
|
||||||
|
constexpr reverse_iterator rbegin() { return nullptr; }
|
||||||
|
constexpr const_reverse_iterator rbegin() const { return nullptr; }
|
||||||
|
constexpr const_reverse_iterator crbegin() const { return nullptr; }
|
||||||
|
constexpr reverse_iterator rend() { return nullptr; }
|
||||||
|
constexpr const_reverse_iterator rend() const { return nullptr; }
|
||||||
|
constexpr const_reverse_iterator crend() const { return nullptr; }
|
||||||
|
|
||||||
|
/* capacity */
|
||||||
|
constexpr bool empty() const { return true; }
|
||||||
|
constexpr size_type size() const { return 0; }
|
||||||
|
constexpr size_type max_size() const { return 0; }
|
||||||
|
|
||||||
|
/* lookup */
|
||||||
|
|
||||||
|
template <class KeyType>
|
||||||
|
constexpr std::size_t count(KeyType const &) const { return 0; }
|
||||||
|
|
||||||
|
template <class KeyType>
|
||||||
|
constexpr const_iterator find(KeyType const &) const { return end(); }
|
||||||
|
template <class KeyType>
|
||||||
|
constexpr iterator find(KeyType const &) { return end(); }
|
||||||
|
|
||||||
|
template <class KeyType>
|
||||||
|
constexpr std::pair<const_iterator, const_iterator>
|
||||||
|
equal_range(KeyType const &) const { return {end(), end()}; }
|
||||||
|
template <class KeyType>
|
||||||
|
constexpr std::pair<iterator, iterator>
|
||||||
|
equal_range(KeyType const &) { return {end(), end()}; }
|
||||||
|
|
||||||
|
template <class KeyType>
|
||||||
|
constexpr const_iterator lower_bound(KeyType const &) const { return end(); }
|
||||||
|
template <class KeyType>
|
||||||
|
constexpr iterator lower_bound(KeyType const &) { return end(); }
|
||||||
|
|
||||||
|
template <class KeyType>
|
||||||
|
constexpr const_iterator upper_bound(KeyType const &) const { return end(); }
|
||||||
|
template <class KeyType>
|
||||||
|
constexpr iterator upper_bound(KeyType const &) { return end(); }
|
||||||
|
|
||||||
|
/* observers */
|
||||||
|
constexpr key_compare const& key_comp() const { return value_comp().key_comp(); }
|
||||||
|
constexpr value_compare const& value_comp() const { return static_cast<impl::CompareKey<Compare> const&>(*this); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U, typename Compare = std::less<T>>
|
||||||
|
constexpr auto make_map(bits::ignored_arg = {}/* for consistency with the initializer below for N = 0*/) {
|
||||||
|
return map<T, U, 0, Compare>{};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename U, std::size_t N>
|
||||||
|
constexpr auto make_map(std::pair<T, U> const (&items)[N]) {
|
||||||
|
return map<T, U, N>{items};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename U, std::size_t N>
|
||||||
|
constexpr auto make_map(std::array<std::pair<T, U>, N> const &items) {
|
||||||
|
return map<T, U, N>{items};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename U, typename Compare, std::size_t N>
|
||||||
|
constexpr auto make_map(std::pair<T, U> const (&items)[N], Compare const& compare = Compare{}) {
|
||||||
|
return map<T, U, N, Compare>{items, compare};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename U, typename Compare, std::size_t N>
|
||||||
|
constexpr auto make_map(std::array<std::pair<T, U>, N> const &items, Compare const& compare = Compare{}) {
|
||||||
|
return map<T, U, N, Compare>{items, compare};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace frozen
|
||||||
|
|
||||||
|
#endif
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user