Compare commits

...

1065 Commits

Author SHA1 Message Date
Bernhard Kirchen
81b1e6e158
Prepare Release 2024.11.20
merge development into master
2024-11-20 22:57:58 +01:00
Bernhard Kirchen
bfa55a8371 Feature: process VE.Direct "FWE" text data 2024-11-20 22:45:37 +01:00
SW-Nico
4beea357e7 Feature: add VE.Direct "RELAY" to live view, MQTT, and HASS 2024-11-20 22:45:37 +01:00
Bernhard Kirchen
1d5816d57f readme: add section "Getting Started" 2024-11-20 21:00:06 +01:00
Bernhard Kirchen
61aa32ab5e webapp: DPL: order inverters as configured
order the appearance of inverters in the DPL settings like configured in
the list of all inverters.
2024-11-18 22:18:18 +01:00
Bernhard Kirchen
a8f57d98a4 DPL: clean up automatic inverter restart 2024-11-17 21:43:27 +01:00
Andreas Böhm
cbffafaa28 DPL: multiple inverters: add proper support for solar-powered inverters (#1343) 2024-11-17 21:43:27 +01:00
vaterlangen
cf4a59c740 Feature: DPL: support overscaling on all inverters (#1286)
this change allows to support overscaling for all inverters, as the configuration of
inputs (which one is part of a particular MPPT) is now provided from withing the
code. this information is used to implement overscaling for any of the inverters
(which are generally compatible with OpenDTU(-OnBattery)).
2024-11-17 21:43:27 +01:00
Bernhard Kirchen
4524c0405d Feature: DPL: add support for multiple inverters 2024-11-17 21:43:27 +01:00
Bernhard Kirchen
7e19f19655 webapp: fix line break for reload button
if a page uses the reload button, it had only 1 column of space, and
only if the viewport was at least "sm". this is not the case for typical
smartphones, in which case the reload button would appear on its own row
instead of to the right.

we now limit the heading to 10 columns if and only if the reload button
is to be used, otherwise the heading uses all 12 columns, regardless of
the viewport size. the reload buton uses two columns -- if it is
displayed at all.

the font size of the icon is increased slightly.

as the font size of h1 headings changes with the viewport size, we need
to center both the heading and the button vertically.
2024-11-12 22:27:41 +01:00
Bernhard Kirchen
d5d66ecfd5 replace C-style casts with C++ static_casts 2024-11-12 20:54:44 +01:00
Bernhard Kirchen
386e6b8720 use config write guard
developed by myself, this feature was now merged from upstream, and the
required changes are made to use the config write guard, which enforces
synchronized writes to the configuration struct.
2024-11-11 22:16:17 +01:00
Bernhard Kirchen
d9177dad34 support localization for grid usage in display 2024-11-11 22:15:10 +01:00
Bernhard Kirchen
4f6f0fd101 Merge upstream tag 'v24.11.7' into development 2024-11-11 21:30:36 +01:00
Bernhard Kirchen
827a272a19 webapp: declare emitted event in FormFooter component
fixes an annoying warning (visible in the browser console):

[Vue warn]: Extraneous non-emits event listeners (reload) were passed to
component but could not be automatically inherited because component
renders fragment or text root nodes. If the listener is intended to be a
component custom event listener only, declare it using the "emits"
option.
2024-11-06 22:24:38 +01:00
Bernhard Kirchen
8a2192ece4 webapp: fix inverter selection button breaking
on small viewports, the icon and the inverter label would be displayed
in two lines. this change keeps the icon and the label tied together in
any case, and the icon is centered vertically around the label.
2024-11-03 20:18:21 +01:00
Niko
f9b84a02ef
Feature: add VE.Direct "load current" to live view, MQTT, and HASS (#1367)
use label "Virtual load output state" if LOAD=true and IL=false
2024-11-03 19:48:48 +01:00
Bernhard Kirchen
fcf21ac1d6 keep console.log() when serving webapp
the removal of console and debugger statements by esbuild even when not
building for production seems to be a regression, as these were
definitely working in the past.

this change uses the command parameter to configure esbuild to either
keep or indeed remove the respective statements. they are only kept if
command is not "serve".

to avoid having to indent everything in defineConfig() by one block, the
return statement and closing curly brace were added "inline".
2024-11-03 11:55:34 +01:00
Bernhard Kirchen
41fd52db52 Fix: protect api/powerlimiter/status endpoint
this endpoint must not spill info if read-only access is disabled.
2024-11-02 22:08:26 +01:00
Bernhard Kirchen
c55ff7d9ae Feature: add HASS autodiscovery for JBD BMS 2024-10-30 21:53:47 +01:00
Bernhard Kirchen
ad0e9faad1 webapp: add JDB BMS issue texts 2024-10-30 21:53:47 +01:00
Bernhard Kirchen
3b0c83fa25 webapp: generalize battery serial interface settings 2024-10-30 21:53:47 +01:00
Manuel Bruehl
7cd1984d60 Feature: support for JBD BMS using serial connection 2024-10-30 21:53:47 +01:00
Bernhard Kirchen
33b7697b37 add "important differences" to README 2024-10-29 17:07:07 +01:00
Bernhard Kirchen
a352821f77 restructure and improve README
* move badges to the top
* merge introductory paragraph with main topic
* add links to users
* fix typos, grammar, wording
2024-10-29 17:07:00 +01:00
Bernhard Kirchen
1bd22376f2 webapp: optimize body bottom padding and length
long forms, when scrolled to the bottom, would leave no space between
the bottom of the viewport and the buttons, which is unpleasent.

short views would still createa large (high) body, for apparently no
reason.
2024-10-29 14:56:06 +01:00
Bernhard Kirchen
4a5aea09b9 webapp: always scroll up when navigating to another view 2024-10-29 14:56:06 +01:00
Bernhard Kirchen
77e889cf9f webapp: optimize spacing around power meter test buttons 2024-10-29 14:56:06 +01:00
Bernhard Kirchen
29cf2bc5bb webapp: optimize look of login page
improve spacing and align login buton to the right, where all our
buttons are.
2024-10-29 14:56:06 +01:00
Bernhard Kirchen
c0cfdf04a8 webapp: fix AC charger admin view
use InputElement where possible, which in particular fixes that the
inputs of the second card were all in the same row.
2024-10-29 14:56:06 +01:00
Bernhard Kirchen
a425be5a03 webapp: inverter advanced tab needs space at the top
this avoids the input text box from colliding with the tab navigation
bottom border.
2024-10-29 14:56:06 +01:00
Bernhard Kirchen
e6ee0d584f webapp: optimize look of firmware update cards 2024-10-29 14:56:06 +01:00
Bernhard Kirchen
2f0699d62e webapp: properly space alert with hint for hostname 2024-10-29 14:56:06 +01:00
Bernhard Kirchen
72e0708e51 webapp: device manager: optimize cards for tab nav
the top border of the card was breaking the design of the tabs, where
the active tab would be "visually connected" to the content. also, the
rounded border at the top did not blend in with the navbar's bottom
border.
2024-10-29 14:56:06 +01:00
Bernhard Kirchen
3c3d97fe05 webapp: show pin mapping categories as cards
on a desktop browser, this approach allows to display all categories at
once. we also increase readability as the values are much closer to
their label. previously, the values were far to the right of the screen
and it was unpleasent to read which value belonged to which setting. the
grouping of values per category was also not very well conceived.

by using cards, we also avoid some styling issues, namely the use of
rowspan, which caused a spurious table cell border at the end of the old
table layout.
2024-10-29 14:56:06 +01:00
Bernhard Kirchen
c87ba97a2b webapp: optimize placement of device profile doc buttons
* remove empty container for device profile links. if a device profile
  has no links, no buttons are generated, but a row was still part of
  the DOM, adding spurious space between the select and the alert with
  the hint.
* "float" the buttons to the right, as we always place these kinds of
  buttons to the right.
2024-10-29 14:56:06 +01:00
Bernhard Kirchen
121b7cfc40 webapp: replace remaining "OpenDTU" texts with "OpenDTU-OnBattery" 2024-10-29 14:56:06 +01:00
Bernhard Kirchen
89a433c356 webapp: consistently use no colon in form labels
there are no colons for table headers as well. some form labels had no
colon already, so this change uses a unified look among form labels.
2024-10-29 14:56:06 +01:00
Bernhard Kirchen
db5520452d webapp: fixup battery total cards
the total cards have been trimmed at the bottom as the card's last child
has its margin-bottom removed (set to "auto"). that is desired as it
places the text in the middle of the cards. however, the battery total
cards are different, as they show two values each, which are arranged by
div.flex-fill containers, which are the children of the cards rather
than the h2 tags as in all other totals cards.
2024-10-29 14:56:06 +01:00
Bernhard Kirchen
50db50174e webapp: use value class to format live values
avoids inline style and removes right padding such that the value and
its unit move closed together, replicating the design of the inverter
channel info tables.
2024-10-29 14:56:06 +01:00
Bernhard Kirchen
bad5260630 webapp: avoid inline style for inverter channel info value 2024-10-29 14:56:06 +01:00
Bernhard Kirchen
31c26b0d25 webapp: MQTT: no login with cert if TLS disabled
in the settings view we hide the "login with cert" setting while TLS is
disabled, so we should also hide that info in the info view when TLS is
disabled.
2024-10-29 14:56:06 +01:00
Bernhard Kirchen
8174fb1176 webapp: MQTT: use v-if in favor of v-show
if we hide elements (which is done using style="display:none;"), they
are still part of the DOM and mess with CSS rules that shall apply to
the last element of a card or the last row of a table.
2024-10-29 14:56:06 +01:00
Bernhard Kirchen
2d49093c68 webapp: optimize spacing on bottom of cards
if the last child in a card (div.card > div.card-body) adds bottom
marging, we don't want the card to add more space through its
padding-bottom. most cards have children that add sufficient space
at the bottom anyways.
2024-10-29 14:56:06 +01:00
Bernhard Kirchen
dc78a83b84 webapp: avoid spurious DOM elements, avoid v-show, use v-if
avoid hidden (but existing) or simply redundant DOM elements from
messing with the style sheet, which uses :last-child in particular to
fix up the margin on the bottom of cards.
2024-10-29 14:56:06 +01:00
Bernhard Kirchen
8c46521d6e webapp: do not use div with v-if but template elements
when using v-if on a div element, the div will be part of the DOM in
case the condition is true. if we group elements to use v-if on the
group, we shall use a template element, so the group elements appear
as siblings of the other elements.

in particular, these spurious div do not mess up our CSS patch that sets
the bottom margin for the last child in a card to "auto".
2024-10-29 14:56:06 +01:00
Bernhard Kirchen
b6c0a850c9 webapp: fix inverter "add" and "save order" button positions
the source tells us that the buttons are supposed to be on the right of
tha card, but the CSS broke at some point.
2024-10-29 14:56:06 +01:00
Bernhard Kirchen
0a5bd65bd2 webapp: beautify radio statistics reset button
it would be nice to have this in the header of the accordion, which is
hard, but doable. however, clicking the button then also toggles the
accordion, which is unacceptable. preventing that seems non-trivial, as
the @click.stop() is not enough. also, nesting interactive elements is
simply bad practice. the button can also go to the right of header, with
reasonable effort, but the corner radii are then messed up and would
need to react interactively (accordion collapsed or not), which is also
a pain.

we now "float" the reset button to the right, add a nice icon, and give
the button some space so it at least looks like it belongs there.
2024-10-29 14:56:06 +01:00
Bernhard Kirchen
a582f9c6d3 webapp: adjust look of tables in accordions to live view cards
this is relevant for the radio statistics table, as well as the tables
in the grid profile modal.
2024-10-29 14:56:06 +01:00
Bernhard Kirchen
aef0efcfae webapp: apply card-table class to info view cards
the cards in all information views still used a div.card-body around the
table, which added a margin on all sides of the table. to achieve a
unified look, these cards and tables now look the same as the inverter
channel cards.
2024-10-29 14:56:06 +01:00
Bernhard Kirchen
28bbd27280 webapp: align table headers with card headers
set the left margin of table header cells to the same marging the card
header use, such that the text align on the same axis.
2024-10-29 14:56:06 +01:00
Bernhard Kirchen
d8fa70371c webapp: use reasonable name for radio stats accordion 2024-10-29 14:56:06 +01:00
Bernhard Kirchen
e15fdab3e1 webapp: equalize style of cards with tables in live view
this change adjusts the style of cards showing tables such that they
look the same as inverter channel info tables.
2024-10-29 14:56:06 +01:00
Bernhard Kirchen
3edfdb7ab6 webapp: avoid inline style in inverter channel info card 2024-10-29 14:56:06 +01:00
Bernhard Kirchen
4aa9080d4e webapp: add gap between inverter selectors 2024-10-29 14:56:06 +01:00
Bernhard Kirchen
983b58fa0c webapp: remove table's bottom margin
we don't need a margin at the bottom of tables in general. not sure why
this is even a thing in bootstrap. this change, in particular, makes the
space between a table and a parent card symmetric on all sides.
2024-10-29 14:56:06 +01:00
Bernhard Kirchen
c5f37a8010 webapp: last table row shall have no bottom border
similar to the first row which has no border at the top.
2024-10-29 14:56:06 +01:00
Bernhard Kirchen
94123fe415 webapp: right-align labels for inputs on non-sm viewports
this change tries to achieve a pleasing look of input forms by
right-aligning the texts of labels. the input form now looks similar
to a table, achieving a cleaner look, especially for forms where the
labels have varying text lenghts.
2024-10-29 14:56:06 +01:00
Bernhard Kirchen
993f3213a1 Fix: skip BOM in JSON files (pin_mapping and config) 2024-10-29 11:02:07 +01:00
Niko
3c1d3f7207
Feature: retrieve absorption and float voltage from Victron MPPTs (#1140)
the absorption and float voltage setting is retrieved from connected
Victron Ve.Direct MPPTs using the HEX protocol. the values are
displayed in the live view, published to MQTT, and added to Home
Assistent auto-discovery.
2024-10-25 21:42:26 +02:00
Andreas Böhm
cfb5c3f550 webapp: prettify battery settings UI
* use wide labels for all battery settings
* dynamically show and hide valid battery discharge limit settings
2024-10-24 15:07:54 +02:00
Tobias Diedrich
2e85b420d5 Feature: add SoC & voltage thresholds for battery current limit
This changes the custom current limit so the custom limit is only
applied when any of:

- SoC is valid and not ignored and SoC < threshold
- Voltage is valid and Voltage < threshold
- Voltage is invalid

Independently, if "Use Battery-Reported limit" is enabled and valid, it
is applied (unless a lower custom limit already was applied).
2024-10-24 15:07:54 +02:00
Andreas Böhm
d36b30ae0e Fix: SBS Unipower battery discharge current handling
SBS CAN receiver implementation was not using the correct way to provide
discharge current limit.
2024-10-24 15:07:54 +02:00
Bernhard Kirchen
17016b179f actions: use RELEASE_TOKEN to create a release 2024-10-23 13:46:50 +02:00
Bernhard Kirchen
80a84e5e09 actions: use RELEASE_TOKEN to create a release 2024-10-22 22:19:17 +02:00
Bernhard Kirchen
225a66a685
Prepare Release 2024.10.22 2024-10-22 21:52:50 +02:00
Bernhard Kirchen
3aa850a6a6 Fix: German translation for Victron MPPT load output 2024-10-22 21:33:06 +02:00
Bernhard Kirchen
0fe1dd8bd2 Merge upstream tag 'v24.10.15' into development 2024-10-22 20:10:22 +02:00
PhilJaro
83437b2759
webapp: Enable horizontal scrolling for battery view on small screens (#1324)
Enables horizontal scrolling for the batter views in order to not break the UI on small screens.
2024-10-22 18:19:13 +02:00
1-am-r00t
28d4f87301
Fix: HTTP power meter custom port (#1333) 2024-10-20 21:59:02 +02:00
Niko
844d92008b
Feature: show "Smart Battery Sense" temperature in web UI (#1292)
a battery temperature value measured by a Victron smart battery sense
and communicated to a connected Victron MPPT charge controller will now
appear in the live view card.
2024-10-13 21:05:39 +02:00
Bernhard Kirchen
4bdfd655f8 Fix: conversion from Kelvin to Centigrade (SmartBatterySense) 2024-10-13 21:01:45 +02:00
Tobias Diedrich
28c0c9d7a5 Fix typo in Battery.cpp
When the limit is valid, use the actual configured limit, not the bool value :)
2024-10-10 18:06:58 +02:00
Tobias Diedrich
2454cead03 Fix default value for discharge current limit
If the default type is an int, setting to a fractional value is not possible.

Presumably this is because in
`target.DischargeCurrentLimit = source["discharge_current_limit"] | BATTERY_DISCHARGE_CURRENT_LIMIT;`
the JSON library uses the default to force the expected type and on type mismatch the default is used.

As per https://arduinojson.org/v7/api/jsonvariant/or/:
`defaultValue: the value to return if the JsonVariant is null or incompatible with the requested type.`
2024-10-10 18:06:58 +02:00
ranma
c523f066b3
feature: Add support for native pytes CAN protocol (#1196)
* Allow scaleValue() for 32bit values

* Victron: Implement CAN message 0x360

This one-byte message is set to 0xff to request charging below a
 certain SoC threshold (10% in my tests).

* Pytes: Add support for native CAN protocol

The recently added PytesCanReceiver.cpp implements the Victron CAN protocol.

This change additionally adds support for the native Pytes CAN
protocol messages.

Features only supported in Pytes protocol:
- High-resolution state of charge / full and remaining mAh
- Charge cycle counter
- Balancing state

Features only supported in Victron protocol:
- FW version
- Serial number

Note that the only known way to select the native Pytes protocol is
via the serial console (Cisco-compatible cables work):

```
login config
setprt PYTES
logout
```

to return to Victron protocol use:
```
login config
setprt VICTRON
logout
```

to return to DIP-switch based protocol setting:
```
login config
setprt DIP
logout
```
2024-10-10 18:04:58 +02:00
Bernhard Kirchen
c483347e41 Feature: show task details in system info view
shows whether or not known tasks are alive, and in particular shows how
much of the respective stack is still available.
2024-10-08 22:10:49 +02:00
Bernhard Kirchen
f760520489 Fix: generic_esp32_8mb firmware did not boot
we need to set board_upload.flash_size = 8MB for the firmware to
actually boot using the 8MB partition scheme.
2024-10-08 22:04:05 +02:00
Bernhard Kirchen
9de71fe63c cpplint: use cpplint version 1.6.1 for now
when upgrading to cpplint 2.0.0, C-style casts are flagged. since many
of them are in libraries, we will use version 1.6.1 until we figure out
whether we shall ignore C-style casts or if we will replace them.
2024-10-07 21:20:16 +02:00
Bernhard Kirchen
e2ec3840d1 welcome to hoylabs: update README and URLs 2024-10-07 21:09:04 +02:00
Bernhard Kirchen
d6d5f32329 webapp: pin assignment: hide unsupported pins
if the pin_mapping.json includes unsupported pins, e.g., `eth` pins on
an ESP32-S3, the whole category should still be hidden in the device
manager.
2024-10-06 21:55:23 +02:00
Bernhard Kirchen
ee54bebd0b webapp: address eslint issues 2024-10-06 21:34:32 +02:00
Bernhard Kirchen
b2913f24d3 Merge upstream tag 'v24.10.6' into development 2024-10-06 21:21:26 +02:00
Bernhard Kirchen
d9b9e8a7fc
Huawei CAN: use SpiManager to claim SPI bus (#1311) 2024-10-06 20:50:33 +02:00
Bernhard Kirchen
1812e6eb6a Fix: prevent unauthorized access to OnBattery websockets
it turns out that authentication was never implemented on
OpenDTU-OnBattery-specific websocket connections. found while
applying https://github.com/tbnobody/OpenDTU/pull/2320
2024-09-30 22:26:31 +02:00
Bernhard Kirchen
185ac36282 Merge upstream tag 'v24.9.30' into development 2024-09-30 21:34:17 +02:00
Bernhard Kirchen
cbad181b99 Revert "Feature: SPIPortManager allows simultaneous use of CMT2300 and Huawei charger"
This reverts commit df53f34b51.
2024-09-30 21:07:05 +02:00
Bernhard Kirchen
0a289bbcab Revert "Feature: Support for W5500 ethernet module (#1231)"
This reverts commit 89d9a40296.
2024-09-30 20:59:22 +02:00
Bernhard Kirchen
a89c1fa45a Revert "Fix: device profile for OpenDTU Fusion with W5500 (#1259)"
This reverts commit 27f5a943f6.
2024-09-30 20:56:57 +02:00
Bernhard Kirchen
d0ba065f70 webapp: optimize syslog settings
* avoid duplicate id for hostname input
* hide server and port inputs if syslog disabled
2024-09-28 21:36:31 +02:00
Bernhard Kirchen
aa159fd8ee prevent actions from running twice when pushing to PRs 2024-09-27 20:45:04 +02:00
Bernhard Kirchen
5d8bb8f810 Merge upstream tag 'v24.9.26' into development 2024-09-27 20:07:53 +02:00
ranma
20159f341e
Feature: Add syslog logger (#1267)
This implements RFC5424 version of the protocol.

Don't use https://github.com/arcao/Syslog since the protocol itself
is trivial and most of the libraries functionality is not needed here.
The library also doesn't support setting the PROCID field, which is set
to a random id to indicate a reboot here.

Add UI for syslog configuration to network admin view.
2024-09-26 23:01:06 +02:00
Bernhard Kirchen
a7dbf0a12e issue template: turn environment input into dropdown 2024-09-26 22:08:53 +02:00
Bernhard Kirchen
de171c5bcc issue template: fix phrasing 2024-09-26 22:08:53 +02:00
Bernhard Kirchen
22d96fd8a5 issue template: fix typo 2024-09-26 22:08:53 +02:00
Bernhard Kirchen
411fe7e383 issue template: adjust all remaining occurences of "OpenDTU" 2024-09-26 22:08:53 +02:00
vaterlangen
f36a3bf359
increase chunkSizeWarningLimit for webapp build (#1287)
increase from 500k (default) to 1024k in order to get rid of the warning messages.
2024-09-26 21:22:08 +02:00
ranma
191cc8007d
Feature: parse additional Pylontech CAN protocol fields (#1213)
I noticed that these are missing while looking at dissassembly of the
Pytes implementation of the protocol. I also found Pylontech sample
CAN messages] which match the Pytes implementation [1]:

```
CAN ID – followed by 2 to 8 bytes of data:
0x351 – 14 02 74 0E 74 0E CC 01 – Battery voltage + current limits
                          ^^^^^ discharge cutoff voltage 46.0V
0x355 – 1A 00 64 00 – State of Health (SOH) / State of Charge (SOC)
0x356 – 4e 13 02 03 04 05 – Voltage / Current / Temp
0x359 – 00 00 00 00 0A 50 4E – Protection & Alarm flags
                       ^^^^^ always 0x50 0x59 in Pytes implementation
                    ^^ module count (matches the blog article image)
0x35C – C0 00 – Battery charge request flags
        ^^ two possible additional flags (bit 3 and bit 4)
0x35E – 50 59 4C 4F 4E 20 20 20 – Manufacturer name (“PYLON “)
        ^^^^^^^^^^^^^^ Note: Pytes sends a 5-byte message "PYTES" instead
                       padding with spaces
```

The extra charge request flag is "bit4: SOC low" (Seems to be SoC < 10%
threshold for Pytes), I haven't bothered adding that as it provides
little value.

[1] https://www.setfirelabs.com/green-energy/pylontech-can-reading-can-replication
2024-09-25 14:45:52 +02:00
Bernhard Kirchen
2265992836 actions: prevent workflows triggering twice
the workflows running unconditionally for each push and each PR are run
twice when pushing to a branch that is to be merged by a PR and if that
branch is a branch local to the repo (rather than in a fork).
2024-09-23 21:53:33 +02:00
Bernhard Kirchen
82de98c7c0 Merge remote-tracking branch 'tbnobody/master' into development 2024-09-23 21:38:50 +02:00
Eugen
2637e32145
Feature: rxen/txen support for RS485 transceiver for SDM power meter (#1269)
This allows to talk to the SDM power meter through an RS485 transceiver
with separate rxen and txen pins, like on the OpenDTU Fusion board.
2024-09-23 21:20:03 +02:00
ranma
c16b3aa21b
Fix: typo in HASS auto-discovery metadata for heap stats (#1274)
Fixes a copy and paste error in commit b3ee38b0 from when maxAlloc/minFree were renamed.
2024-09-23 21:10:10 +02:00
Bernhard Kirchen
97f95f8a11 avoid deprecated containsKey() method of ArduinoJson 7.2.0 2024-09-21 22:09:42 +02:00
Bernhard Kirchen
38726b99ab Merge remote-tracking branch 'tbnobody/master' into development 2024-09-21 22:00:49 +02:00
Bernhard Kirchen
f298fd92f5 webapp: autocompile script: changes package.json triggers build
if the package.json file changes, we must trigger building the web
application, as in particular, a completely different version of yarn
could be required.
2024-09-21 21:47:02 +02:00
Bernhard Kirchen
503455dc51 webapp: switch to Node.js v22 2024-09-21 21:43:05 +02:00
Bernhard Kirchen
2f4eef47e9 webapp: autocompile script needs to run yarn within webapp dir
we need to change the working directory to the webapp directory such
that corepack installs and uses the expected version of yarn. otherwise,
corepack installs a copy of yarn into the repository root directory.
2024-09-21 21:04:10 +02:00
Bernhard Kirchen
a2092e66c4 webapp: autocompile script: use portable subprocess
we added shell=True so that on Windows, yarn would be found. however,
using the list syntax to define the command and its arguments to
Python's subprocess is broken on GNU/Linux by shell=True. instead, use a
single string (command and arguments), which works on both Windows and
GNU/Linux.
2024-09-21 21:00:48 +02:00
Bernhard Kirchen
d1aad0b8e0 actions: enable corepack to use fixed version of yarn
this allows us to fix the version of yarn, the Node.js package manager,
to a particular version. using corepack is the recommended way to use
yarn these days.
2024-09-20 22:07:27 +02:00
Bernhard Kirchen
f9926501f1 format BatteryAdminView changes introduced by #1245 2024-09-20 17:02:14 +02:00
Bernhard Kirchen
c3c31e5694 actions: run yarn prettier to check web app formatting 2024-09-20 17:00:58 +02:00
Bernhard Kirchen
58e01cdcc7 actions: switch to node version 20 for linting
use version consistent with the version used when building the web
application.
2024-09-20 17:00:58 +02:00
Bernhard Kirchen
51286047d4 actions: fix a typo 2024-09-20 17:00:58 +02:00
Bernhard Kirchen
0bcbec1baa actions: use setup-node@v4 as v3 causes warning
the "Yarn Linting" action causes a warning to appear about a deprecated
Node version. switch to actions/setup-node@v4, which is already in use
by the action building the web app for the firmware, to avoid this
warning.
2024-09-20 17:00:37 +02:00
Bernhard Kirchen
ea1592ed0d actions: consistently use checkout@v4
this was already in use by "Gather Environments" and "Build
Environments", but neither in "Create Release" nor "Yarn Linting"
nor "cpplint".
2024-09-20 16:59:52 +02:00
Bernhard Kirchen
59a89e9165 changelogs: group webapp-related changes 2024-09-20 16:51:53 +02:00
Bernhard Kirchen
163abbbe58 remove test_build.yml: unmaintained and redundant
it seems this action has not been run for nealry a year. it has only
been run twice at all. it is not maintained, as it uses older Node.js
in particular. also, it seems to be redundant to build.yml, which
already builds firmware for every commit.
2024-09-20 16:51:53 +02:00
Bernhard Kirchen
789a07e288 webapp: use shell=True in autocompile script
we need shell=True as on Windows, path resolution to find the yarn
"exectuable" (a shell script) is only performed by cmd.exe, not by
Python itself. as we are calling yarn with fixed arguments, using
shell=True is fine.
2024-09-20 15:53:05 +02:00
Bernhard Kirchen
4f5648689f webapp documentation moved as well 2024-09-20 15:39:47 +02:00
vaterlangen
27f5a943f6
Fix: device profile for OpenDTU Fusion with W5500 (#1259)
* explicitly disable NRF24 when using CMT + W5500. fixes #1257.
* explicitly disable CMT when using NRF + W5500
* added missing LED mapping for W5500 profiles
2024-09-16 22:10:43 +02:00
Bernhard Kirchen
8ff94e7b94 issue template: asks for firmware variant 2024-09-16 16:10:38 +02:00
Bernhard Kirchen
95dac71cb2 bug report: allow to select non-release GitHub build 2024-09-16 16:10:38 +02:00
Bernhard Kirchen
3a33fb06b3 tailor issue templates to OpenDTU-OnBattery
these were never adapted to our fork but merely copied form the upstream
project.
2024-09-16 16:10:38 +02:00
Snoopy-HSS
3fc43098a6
Feature: Support for SBS Unipower batteries (#1199)
Allows to connect to SBS Unipower batteries using a CAN bus.
2024-09-16 15:22:15 +02:00
Andreas Böhm
a6e7007f4b
Feature: extend battery discharge limit support (#1245)
* implements UI to configure battery discharge limit
* adds support for discharge limit to MQTT battery provider
* add option to hide `issues` section from battery live view (for MQTT battery)
2024-09-13 20:52:23 +02:00
ranma
6318ab4a8b
Feature: DPL: Honor battery-provided discharge power limit (#1198)
When the BMS provides a discharge current limit, apply
this limit in the DPL to the inverter power target when running
from battery.
2024-09-13 20:36:16 +02:00
Andreas Böhm
c96762c765
Fix: don't republish MQTT powermeter data to MQTT (#1250) 2024-09-13 17:26:30 +02:00
ranma
86cab0f281
Feature: ESP heap and temperature details on MQTT and HASS (#1242)
I noticed that some useful ESP stats are missing on the MQTT broker, so this adds:

- ESP temperature
- ESP heap stats (size, free, minFree, maxAlloc)
2024-09-13 16:55:38 +02:00
Gumbagubanga
89d9a40296
Feature: Support for W5500 ethernet module (#1231)
adds support for w5500 Ethernet chip, present on
OpenDTU Fusion PoE Ethernet hat in particular.
2024-09-13 16:51:22 +02:00
Bernhard Kirchen
83ed72e142
Prepare Release 2024.09.11 2024-09-11 22:12:23 +02:00
Bernhard Kirchen
1fe8d3f513 disable "based on OpenDTU" badge
disabled as uploading the changed gist failed repeatedly. maybe the
token in secrets.GIST_SECRET has expired? need help from repo owner
@helgeerbe to fix this.
2024-09-11 21:59:42 +02:00
Bernhard Kirchen
f8595865ea webapp autocompile: try yarnpkg 2024-09-11 21:51:08 +02:00
Bernhard Kirchen
9067acdab7 fix: typo in german web app locale
closes #1223.
2024-09-10 14:33:39 +02:00
Bernhard Kirchen
ffb30de4d2 Revert "webapp: pylontech battery provider can also read DEYE"
This reverts commit cdd6f4cf1b.

See #1244.
2024-09-10 14:24:35 +02:00
Bernhard Kirchen
5ea8fbecf9 WebApi_dtu: remove trailing whitespace
probably added during conflict resolving. these removed whitespace are
not part of the upstream.
2024-09-09 22:28:35 +02:00
Bernhard Kirchen
cdd6f4cf1b webapp: pylontech battery provider can also read DEYE 2024-09-09 21:53:51 +02:00
ranma
f42f018f3e
Fix: sanitize battery manufacturer name string (#1235)
If the string contains control characters for some reason, the browser
will reject the json with the error `bad control character in string
literal`.

This adds a setManufacturer function that validates the string is ASCII
and will cut off the string at the first non-ascii character.

Pylontech: `PYLON` (50 59 4C 4F 4E 20 20 20)
Pytes: `PYTES` (50 59 54 45 53)
Deye: `DY001` (44 59 30 30 31 03 E8 03)

See https://github.com/helgeerbe/OpenDTU-OnBattery/discussions/1226#discussioncomment-10566898
2024-09-09 21:46:47 +02:00
spcqike
cec4003f6e
Implement webapp autocompile (#1193)
add pre-script to check if webapp was compiled or sources changed and auto compile.

---------

Co-authored-by: Bernhard Kirchen <schlimmchen@posteo.net>
2024-09-05 23:02:42 +02:00
ranma
a87f9fa2cd
Fix: Allow higher-resolution SoC in live view header (#1197)
Commit accc70dea0 added the battery SoC to
the live view header. But due to getSoC() returning an int,
the precision was limited.

This changes getSoC() to return float so when a source with higher
precision is available, the respective precision is shown.
2024-09-02 11:08:21 +02:00
Bernhard Kirchen
5ad63e6c44 Fix: DPL settings: do not reset solar_passthrough_enabled
we must not reset the configuration switch value in this context. this
leads to solar passthrough being disabled once the DPL is disabled. when
re-enabling the DPL, solar passthrough is suddenly off, even though the
user configured it to be on.

the configuration switch can stay on. the DPL will still sanity-check
whether or not solar-passthrough can be used (checks if VE.Direct is
enabled) or it is irrelevant since the DPL is disabled.
2024-09-01 22:14:33 +02:00
Bernhard Kirchen
babb24ab1f Fix: reasonable full solar-passthrough default values
value 100 is not accepted by the webapp as inputs for full
solar-passthrough start and stop volages. 66V is the documented
value to be used if one wants to disable full solar-passthrough
(while keeping (non-full) solar-passthrough).
2024-09-01 22:12:48 +02:00
Bernhard Kirchen
9ebbc58930 Fix: Pylontech/Pytes CAN driver: user interrupts at level 2
we are running out of level 1 interrupts on ESP32-S3 boards. for that
reason, until this prooves to be another sort of problem, we allocate
the TWAI's interrupt for the battery CAN implementation at level 2.
2024-08-28 20:08:47 +02:00
Bernhard Kirchen
119bd3a41e Fix: Increase Huawei CAN task stack size
the stack size was already increased by Andreas Boehm in df53f34b51 in
the context of #1144 (SPI port manager). this change aligns the stack
size to a power of two and adds comments. the commit also serves to
place this change more prominently as a fix in the changelogs.
2024-08-28 15:13:23 +02:00
Bernhard Kirchen
fff0576150 Fix: initialize HTTP(S)+JSON power meter values 2024-08-28 13:32:47 +02:00
Bernhard Kirchen
1cbf18d4a7 Feature: HttpGetter: cache digest challenge
this allows us to add a valid Authorization header to each but the first
GET request, saving us from performing two GET requests every time we
want to perform the GET request. we still need a new client nonce and we
need to increase the nonce counter, so we also need to calculate a whole
new response, as we cannot just reuse the previous Authorization header
(that would be a replay attack).
2024-08-28 13:32:47 +02:00
Bernhard Kirchen
fd3b65f4bd Feature: HttpGetter: support MD5 digest authentication
the MD5 scheme should still be widely deployed, even though it is
deprecated. it is also still the default if not specific algorithm
is requested by the server.
2024-08-28 13:32:47 +02:00
Bernhard Kirchen
63612e9276 Fix: restart TCP connection if HTTP server sends Connection:close
we previously performed a whole new GET request when doing digest
authentication. it seemed beneficial to reuse the TCP connection to
perform the second GET request, which includes the authentication
tokens. however, if the server sends "Connection: close" we must not
requse the TCP connection for another HTTP request.

this broke authentication against Shelly devices (at least those with
original firmware).

now we explicitly set "Connection: keep-alive" in our request, and reuse
the TCP connection only if te server replies with "Connection:
keep-alive" as well.
2024-08-28 13:32:47 +02:00
Bernhard Kirchen
b5785d032e print SMA HomeManager timestamp as unsigned number 2024-08-24 21:26:35 +02:00
Bernhard Kirchen
2127b0b080 Fix: SMA HomeManager PowerMeter must announce new values
the SMA power meter implementation did not announce that it received a
new datum, such that the power meter data age was never reset. this made
the power meter values outdated and hence invalid, even though new
values were received and processed.
2024-08-24 21:12:25 +02:00
Bernhard Kirchen
821bc27562 Fix: VE.Direct data is invalid if no controller has valid data
the original implementation of the isDataValid() method worked by
returning false if any of the charge controllers had invalid data. if no
charge controllers were configured, this function then also needed to
also return false.

the logic was changed in 415c767d such that if at least one charge
controller had valid data, the function would return true. this would
have required to adjust the default return statement as well, but it was
not.

if the user enabled VE.Diret and configured at least one charge
controller in the pin mapping, but this controller never delivered any
data, the function would now errorneously return true, because the
container is not empty.

however, if no controller has valid data, this now means we exit the
loop and then the return value must be false, which is also desired if
the container is empty.
2024-08-24 21:06:44 +02:00
Bernhard Kirchen
dcc8313c8c webapp "About" view: link to opendtu-onbattery.net 2024-08-23 17:16:52 +02:00
Bernhard Kirchen
190e1eb0f9 adjust update badge color: having an update available is no danger 2024-08-23 17:16:52 +02:00
Bernhard Kirchen
b115f946cd Fix: update info: compare to respective branch
official release builds are built from branch master, as the respective
tags point to commits in branch master (only). to check whether or not a
new version is available, we should check branch master for new commits.

checking against HEAD does not work as expected, at least in the
OpenDTU-OnBattery repo, since its default branch is "development", not
"master".

usually, there should be no commits on master in between releases, so we
will now only show "update available" if a new release was made. this is
not foolproof, but should work as long as we keep "master" clean.

for builds from other branches, the comparison is perfomed against the
respective branch. if a user installed a binary built by github actions
based on a development branch, the user will see "update available" if
new commits were added to the development branch since.

for other branches, also pull request builds, the test for updates will
fail as the branch name shown in the system info is not actually a
branch name, e.g., "helgeerbe/OpenDTU-OnBattery/pr1183-202408212043".
2024-08-23 17:16:52 +02:00
Andreas Böhm
65407dbdd6
fix: update mqtt subscriptions when topic changed (#1156)
* update mqtt subscriptions when topic was changed
* DPL/Huawei: manage MQTT subscriptions in map

---------

Co-authored-by: Bernhard Kirchen <schlimmchen@posteo.net>
2024-08-20 23:44:27 +02:00
Andreas Böhm
e7d454ff0b
Feature: support 'use battery at night' without VE.Direct
previously, we needed VE.Direct enabled to know the
solar charge controller output, which solely decided when
it was "nighttime". since this is now determined by the
wall clock, we can offer this feature to users without a
Victron charge controller connected using VE.Direct.
2024-08-19 20:49:37 +02:00
Andreas Böhm
df53f34b51
Feature: SPIPortManager allows simultaneous use of CMT2300 and Huawei charger
* backport SPIPortManager from @skippermeister
* adapt to support ESP32 and ESP32-S3 and ESP32-C3
* use logic to work with SPI numbering as in the official
  documentation: start with SPI0 and go up to SPI3
* increase Huawei CAN controller stack size to 2000
* increase startup delay for USB_CDC enabled builds to be able to
  catch bootlogs over USB
2024-08-19 20:44:59 +02:00
Bernhard Kirchen
f8ad0a4487 disable "based on OpenDTU" badge
disabled as uploading the changed gist failed repeatedly. maybe the
token in secrets.GIST_SECRET has expired? need help from repo owner
@helgeerbe to fix this.
2024-08-18 13:34:25 +02:00
Bernhard Kirchen
5a6fe9d174
Prepare Release 2024-08-18
merge development into master
2024-08-18 12:10:12 +02:00
Bernhard Kirchen
51624669b0 move documentation to opendtu-onbattery.net 2024-08-17 22:16:50 +02:00
Bernhard Kirchen
af4b49917c move documentation to opendtu-onbattery.net 2024-08-17 22:11:51 +02:00
Bernhard Kirchen
2894d15477 remove obsolete #include 2024-08-16 21:38:16 +02:00
Bernhard Kirchen
5d1d071c8a
Fix: Improve DPL nighttime discharging (#1126)
* fix: DPL: start discharging at night logic error

the switch "always start discharging battery at night" would cause to
stop discharging the battery when there was solar power and the battery
was discharged below the start threshold.

this change introduces a nighttime discharging boolean variable, which
is enabled the instant we decide to start a battery discharge cycle due
to nighttime havin arrived. we reset this variable as soon as it is
daytime (solar power available). in that case, we allow discharging the
battery if the start threshold was reached. this can actually be the
case if the battery is charged with cheap electricity during the night.

removed comments as they merely spell out what the if statement already
expresses quite nicely.

* use SunPosition.isDayPeriod() to check for daytime

---------

Co-authored-by: Andreas Böhm <andreas@boehm.cx>
2024-08-15 20:10:32 +02:00
Andreas Böhm
599c5cea7d
adjustment: reduce precision of charged- and discharged energy for pytes batteries (#1165)
Precision is reduced to match the CAN protocol definition.
2024-08-13 17:51:12 +02:00
Andreas Böhm
dacf9d4be1
remove ac-charger target consumption limits (#1159) 2024-08-08 22:41:05 +02:00
Andreas Böhm
df11db1244
fix: add auth check on battery, huawei and powermeter API endpoints (#1155) 2024-08-08 20:58:12 +02:00
Bernhard Kirchen
82b28e3732 Merge upstream tag 'v24.8.5' into development 2024-08-06 21:05:21 +02:00
Bernhard Kirchen
04513d3f22 webapp: disable OTA firmware dialog for unsupported devices
show a warning instead and cancel uploads with a HTPP 500 response.
2024-08-04 22:38:19 +02:00
Bernhard Kirchen
4334e60030 webapp: apply formatter on downstream sources 2024-08-01 20:51:59 +02:00
Bernhard Kirchen
77af085ad3 Merge upstream tag 'v24.8.1' into development 2024-08-01 20:28:11 +02:00
vaterlangen
cb5e7e59a5 Feature: add unit for MQTT battery voltage (#1143) 2024-07-31 15:05:41 +02:00
Bernhard Kirchen
1a19f881aa Feature: support JSON payload in MQTT battery provider
this changeset adds support for parsing the MQTT battery provider's SoC
and voltage topics' payloads as JSON to extract a numeric value at a
configurable path.
2024-07-31 15:05:41 +02:00
Andreas Böhm
accc70dea0
feature: show battery voltage, current, and power in live view (#1131)
* show battery voltage, current, and power in live view header (the "totals")
* show battery current and power in extra card
* use soc and current precision in live view
* BatteryStats: do not knowingly publish invalid data: not all battery
  providers know all values the base class manages. make sure to
  prevent publishing invalid values.

Co-authored-by: Bernhard Kirchen <schlimmchen@posteo.net>
2024-07-27 21:35:58 +02:00
Andreas Böhm
e95b70efeb
Feature: expose 'full solar passsthrough active' via MQTT (#1136) 2024-07-26 20:37:40 +02:00
Andreas Böhm
e3f9da75b9
BREAKING CHANGE: allow multiple OpenDTU-OnBattery instances at same HASS
This breaks existing HASS automation, as entity names and MQTT topics change!

* include dtu-unique-id in DPL MQTT HASS topics to allow multiple DTUs to be controlled
* fix filename "src/MqttHandlVedirectHass.cpp"
* refactor: use values from 'MqttHandleHass', add 'via_device' to all HASS devices
* set step size for power limiter voltage values
2024-07-23 21:27:14 +02:00
Bernhard Kirchen
6ee6eaf0b2
Fix: inverter power limits precision
Hoymiles inverters allow setting relative limits with a precision of 0.1 %.
this changeset allows to utilize this precision.

* preserve accuracy when decoding power limit
* Web API: process floating point limits
* MQTT: process floating point limits
* use appropriate accuracy for limits in web UI
* HASS: step for relative inverter limit is 0.1 %
2024-07-23 21:17:25 +02:00
Bernhard Kirchen
86a49a781a fix: use mutex when writing MQTT power meter value
we previously used the mutex to protect writing the target variable.
however, we would only do that for the old usecase, where a plain float
value in Watts was expected as the topic's payload.
2024-07-22 20:07:38 +02:00
Bernhard Kirchen
5e5a5253f4 fix: initialize MQTT power meter values to zero
avoid using uninitialized memory.
2024-07-22 20:07:38 +02:00
Niko
415c767d1d
Fix: Victron MPPT: be happy with at least one that delivers valid data (#1118) 2024-07-21 19:46:06 +02:00
Sven Sowa
5fee069c65
Remove double call to generateOnBatteryJsonResponse() (#1115)
the 2nd call was added in a merge operation in April
2024-07-21 17:59:57 +02:00
Niko
0013b2c934
Double VE.Direct receive buffer to avoid overflow (#1109) 2024-07-16 08:15:11 +02:00
Bernhard Kirchen
926a0b992d improve error messages when probing JSON path value type 2024-07-15 17:41:30 +02:00
Bernhard Kirchen
9e7a0bca31 fix: handle numeric values disguised as strings in JSON
closes #1104.
2024-07-15 17:41:30 +02:00
Andreas Böhm
b90da210be
Fix: Improve overscaling for shaded inputs (#1089) 2024-07-11 15:03:12 +02:00
Bernhard Kirchen
d9d0141fde Merge upstream tag 'v24.6.29' into development 2024-07-10 21:45:43 +02:00
Bernhard Kirchen
e358513495
Merge PR #1077 from helgeerbe/powermeter-refactoring
this PowerMeter refactoring tackles many issues and prepares to solve many more.
2024-07-10 21:20:39 +02:00
Andreas Böhm
6a3f90ff95
Feature: add support for Pytes batteries using CAN (#1088)
Co-authored-by: Bernhard Kirchen <schlimmchen@posteo.net>
2024-07-10 21:01:49 +02:00
Bernhard Kirchen
0c16652acb make MQTT power meter the default power meter
make the MQTT power meter pre-selected when editing power meter settings
for the first time. the MQTT power meter is the most efficient and hence
preferred power meter implementation.
2024-07-01 22:12:30 +02:00
Bernhard Kirchen
b808e9ab18 HTTP+JSON power meter: value 1 is always enabled
enabling the HTTP+JSON power meter without enabling at least one value
makes no sense, so value 1 has always been treated as "always enabled".
while doing the power meter refactoring, however, a regression was
introduced that would hide the settings for value 1 in the web UI until
the power meter settings would be saved, which they cannot be due to
missing settings for value 1.
2024-07-01 22:07:55 +02:00
Bernhard Kirchen
3fe39d722c PowerMeter admin view: make the linter happy
* do not combine v-if and v-for
* add v-bind:key to for loop of HTTPS+JSON power meter value configs
2024-06-27 22:18:41 +02:00
Bernhard Kirchen
9911dec279 HTTP request config component: do not mutate prop
use an event-driven mutation scheme, i.e., adapt the common v-model
approach to bind data to the child component.
2024-06-27 22:18:41 +02:00
Bernhard Kirchen
6b19b877c6 power meter refactoring: split loop task init from init()
when performing a test request using the web UI, we need to init() the
respective power meter, but we do not want to start the polling task.
hence we move initialization of the polling task to the poll() function.
it will return if the task is setup already, otherwise setup the task.
2024-06-27 22:18:41 +02:00
Bernhard Kirchen
6b09ca056e Feature: SML power meters: reset SML decoder
implement a function which allows to reset the SML decoder. this new
function is used after a datagram ends. for the SML HTTP power meter
this is simple: after all bytes from the request's answer have been
decoded, we reset the decoder. for the SML serial power meter, we
perform the reset after a datagram ended based on timing (no new bytes
have been received for a specific amount of time).
2024-06-27 22:18:41 +02:00
Bernhard Kirchen
ea454972b9 Feature: SML power meter: handle checksum error
cache the values decoded in the SML datagram and only copy them to the
local stash of values if the checksum of the SML datagram matched. also
makes sure that values from incomplete SML datagrams are not used.

moreover, we now only publish values to the MQTT broker that we actually
decoded (successfully) from an SML datagram (we previously published 0.0
as values to topics we never decoded a value for).
2024-06-27 22:18:41 +02:00
Bernhard Kirchen
5be1615f4e SML parser: add new OBIS handlers for Hertz and Degrees
copied from the original library.
2024-06-27 22:18:41 +02:00
Bernhard Kirchen
19a8f6a7bd SML parser: pass SML char by value
there is no use in passing it by reference, especially a non-const
reference.
2024-06-27 22:18:41 +02:00
Bernhard Kirchen
7f724ce561 SML parser: remove commented-out code
the comparison is part of the original library code, but the compiler
rightfully complains that the comparison byte <= 0xFF is always true,
since byte is uint8_t. the comparison was commented out, and is now
removed.
2024-06-27 22:18:41 +02:00
Bernhard Kirchen
1afa9a6d90 SML parser: prevent out-of-bound array access
array nodes has MAX_TREE_SIZE elements, so the maximum index allowed to
use to access an element of nodes is one less, not MAX_TREE_SIZE.
2024-06-27 22:18:41 +02:00
Bernhard Kirchen
a1138a2202 Feature: Serial SML power meter: poll asynchronously 2024-06-27 22:18:41 +02:00
Bernhard Kirchen
7b962f58b0 update EspSoftwareSerial to 8.2.0
also ghostl is now a dependency of EspSoftwareSerial and hence there is
no need to list it explicitly in platformio.ini.
2024-06-27 22:18:41 +02:00
Bernhard Kirchen
fb7b710cb7 SML power meter: improve message output
* when printing a message, tell the name of the derived class.
* print total power only when state SML_FINAL reached.
* tell if a checksum verification error occurred.
2024-06-27 22:18:41 +02:00
Bernhard Kirchen
6c06e71fd0 Feature: SDM power meter: switch to software serial
a 9600 baud serial interface does not need a hardware UART. these
changes switch the SDM power meter implementation to use a software
serial instance instead. this is desirable as hardware UARTs are scarce
and users need them for JK BMS and VE.Direct interfaces.
2024-06-27 22:18:41 +02:00
Bernhard Kirchen
8202bb23cd update SDM power meter library to version 2.2.3
in particular, this brings improved timing (settings). the values are
now read out much quicker in succession.
2024-06-27 22:18:41 +02:00
Bernhard Kirchen
a5ba24fdd8 SDM power meter: check stop flag in between transactions
the destructor will block for way too long if we keep holding the
polling mutex while performing a transcation with the SDM power meter.
when reading, we now release the lock. afterwards, i.e., in between
transactions, we check the stop flag so the task terminates in a timely
manner once asked to do so.
2024-06-27 22:18:41 +02:00
Bernhard Kirchen
ea84bb9229 power meter web UI: remove JSON path placeholder
this causes too much confusion, as many users don't understand that this
is a placeholder, not an actual configured value.
2024-06-27 22:18:41 +02:00
Bernhard Kirchen
40423b0e07 SDM power meter: evaluate error code after polling value 2024-06-27 22:18:41 +02:00
Bernhard Kirchen
24f701020c SDM 1-phase power meter: only publish known values
a 1-phase SDM power meter does not know about power or voltage of phase
2 or 3. do not publish values to the respective MQTT topics when using a
single phase SDM power meter.
2024-06-27 22:18:41 +02:00
Bernhard Kirchen
4ad8e88e77 fix: SDM power meter: free HW serial port 2024-06-27 22:18:41 +02:00
Bernhard Kirchen
db869a1144 powermeter refactor: use HTTP1.0
avoid problems with chunked transfer encoding when using the client's
stream to parse a JSON document. fixes the HTTP+JSON power meter to work
with shelly and hichi (Tasmota).
2024-06-27 22:18:41 +02:00
Bernhard Kirchen
15b6a32b92 Feature: support JSON payload in MQTT power meter
the MQTT power meter can now process the messages published at the
respective topics as JSON and extract a power value using a JSON path
(same as in HTTP+JSON power meter). additionally, selecting a unit for
the power value as well as an option to invert the value's sign was
added as well, similar to the HTTPS+JSON power meter.
2024-06-27 22:18:41 +02:00
Bernhard Kirchen
347dd67684 Feature: SDM power meter: poll asynchronously 2024-06-27 22:18:41 +02:00
Bernhard Kirchen
8a46ba9541 Feature: HTTP+JSON power meter: poll asynchronously 2024-06-27 22:18:41 +02:00
Bernhard Kirchen
f6f62a604d Feature: HTTP+SML power meter: poll asynchronously 2024-06-27 22:18:41 +02:00
Bernhard Kirchen
9520d8d394 powermeter refactor: polish web UI 2024-06-27 22:18:41 +02:00
Bernhard Kirchen
a2a9debd02 Feature: make power meter polling intervals configurable
this change makes the respective setting accessible from the web UI.
2024-06-27 22:18:41 +02:00
Bernhard Kirchen
b891a4c1a3 powermeter refactor: instanciate power meters with config
instead of reading the main config's powermeter struct(s), the
individual power meters now are instanciated using a copy of their
respective config. this allows to instanciate different power meters
with different configs. as a first step, this simplifies instanciating
power meters for test purposes.
2024-06-27 22:18:41 +02:00
Bernhard Kirchen
3f2d9d38fa powermeter refactor: fully structure settings per provider
all power meter providers now have their own configuration struct
defined. a respective method to serialize and deserialize the provider
config is implemented for each provider.
2024-06-27 22:18:41 +02:00
Bernhard Kirchen
e1778eba76 powermeter refactor: use HttpGetter in HTTP SML implementation 2024-06-27 22:18:41 +02:00
Bernhard Kirchen
a08ef4cb43 powermeter refactor: test HTTP+JSON power meter as a whole
apply all config values from the webfrontend, then perform one polling
cycle. display values seperately in the result, and show the resulting
value as well.
2024-06-27 22:18:41 +02:00
Bernhard Kirchen
20ecf2a66b powermeter refactor: add and use HttpGetter class
this new class uses the newly introduced HttpRequestConfig and performs
HTTP requests using this config. it will be reused for other power
meters (SML over HTTP(S)) and may be reused by other features in the
future (battery provider, solar power provider, etc.).
2024-06-27 22:18:41 +02:00
Bernhard Kirchen
4d99e87ef4 powermeter: add example URL with non-default TCP port 2024-06-26 21:25:08 +02:00
Bernhard Kirchen
6da90de765 remove extraction of basic auth params from URL
the extractUrlComponents method did extract username and password
from the URL and encoded it for basic authentication. however, the
respective result string was never used. we only perform basic
authentication if the auth type is "basic" and if username and
password were supplied through the respective inputs.
2024-06-26 21:25:08 +02:00
Bernhard Kirchen
297b149f84 powermeter refactor: generalize HTTP request config
the parameters to peform an HTTP request by the HTTP(S)+JSON power meter
have been generalized by introducing a new config struct. this is now
used for all values which the HTTP(S)+JSON power meter can retrieve, and
also used by the HTTP+SML power meter implementation. we anticipate that
other feature will use this config as well.

generalizing also allows to share serialization and deserialization
methods in the configuration handler and the web API handler, leading to
de-duplication of code and reduced flash memory usage.

a new web UI component is implemented to manage a set of HTTP request
settings.
2024-06-26 21:25:08 +02:00
Bernhard Kirchen
ccba7d8036 move JSON path resolver to Utils class for re-use 2024-06-26 21:25:05 +02:00
Bernhard Kirchen
673b9f4fa8 powermeter refactor: use destructors to de-initialize 2024-06-26 20:51:56 +02:00
Bernhard Kirchen
e78f5849c1 Feature: decode more OBIS values in SML power meters
supersedes #951.
2024-06-26 20:51:56 +02:00
Bernhard Kirchen
75c07c17f2 powermeter refactor: SML lib: replace double by float
avoid additional conversions and avoid double for the fact that
calculations on type double are implemented in software, whereas
float is handled in hardware on ESP32.
2024-06-26 20:51:56 +02:00
Bernhard Kirchen
6108d24795 powermeter refactor: introduce PowerMeterSml
this new class handles SML data. it uses the SML lib to decode values
and manages those. this de-duplicates code as the class is applicable
to all power meters that collect SML data.
2024-06-26 20:51:56 +02:00
Bernhard Kirchen
6e44a6d750 powermeter refactor: allow destruction of httpClient
make sure the wifiClient used by the httpClient lives longer than the
httpClient, as it accesses the pointer to the wifiClient in its
destructor.
2024-06-26 20:51:56 +02:00
Bernhard Kirchen
54c04aed61 SDM power meter: remove baud rate setting
this setting was not used. the baud rate for the SDM is set to 9600 in
the source code. until the baud rate being customizable is actually
required by somebody, we remove the setting altogether.
2024-06-26 20:51:56 +02:00
Bernhard Kirchen
d99cfd5b31 powermeter refactor: publish values to MQTT in base class
"powertotal" is always published and it is published by the base class
directly. other values are still published by the derived classes, but
use a base class method, which takes care that a common base topic is
used in particular.
2024-06-26 20:51:56 +02:00
Bernhard Kirchen
9eb4f1714c powermeter refactor: make timestamp of last update atomic
the timestamp is potentially updated from a different thread, e.g., MQTT
task, than the main loop, which typically reads that timestamp.
2024-06-26 20:51:56 +02:00
Bernhard Kirchen
d4c07836d9 MQTT powermeter: avoid iterating subscriptions
instead of iterating a map with subscriptions, we now bind the target
variable to the callback, which is executed once a message is arrived.
this way, the target variable is already linked to the respective topic
when the callback is executed.

lock the mutex when writing the variable, as the MQTT callback is
executed in a different context (MQTT task) than the main loop task,
which otherwise accesses the variables.
2024-06-26 20:51:56 +02:00
Bernhard Kirchen
5cd6334880 powermeter refactor: avoid reboot on settings change
the current power meter provider will be de-initialized, and a new
instance will be initialized with the new settings.
2024-06-26 20:51:56 +02:00
Bernhard Kirchen
33683d26c8 powermeter refactor: rename providers in enum
the enum values did not change, but their name (only relevant in the
code) are now more expressive.
2024-06-26 20:51:56 +02:00
Bernhard Kirchen
2397e5cdf5 powermeter refactor: split providers into their own classes
it is important to separate the capabilities of each power meter
provider into their own class/source file, as the providers work
fundamentally different and their implementations must not be
intermangled, which made maintenance and improvements a nightmare
in the past.
2024-06-26 20:51:54 +02:00
Marvin Carstensen
8ec1695d1b Feature: support Tibber bridge as power meter interface 2024-06-26 20:50:50 +02:00
Bernhard Kirchen
83c59d7811 convert files with CRLF endings to LF endings
this only changes line endings. inspect this commit with command `git
show <commit-sha> --ignore-space-at-eol` and it will tell you that the
commit appears to be "empty" (since all changes are whitespace changes
near the end of a line, which are ignored in that git show command).

the files to be changed were found and updated using this command:
find lib src include webapp/src -type f | \
    xargs grep --binary-files=without-match --files-with-matches \
    $(printf '\r\n') | xargs dos2unix

the following files were restored afterwards, as they are using CRLF
line endings in the upstream as well:
 - lib/CMT2300a/cmt2300a_defs.h
 - lib/README
 - include/README
2024-06-24 21:57:12 +02:00
Bernhard Kirchen
2cc086335f BREAKING CHANGE: repartition: double sketch size
this updates the partition scheme for devices with 4 MB of flash memory
to have only a single app partition, doubling its size, but sacrificing
their OTA update capability. the replacement environment for
"generic_esop32" is called "generic_esp32_4mb_no_ota".

the new default partition scheme is targeted for devices with at least 8
MB of flash memory. the previous two app partitions are merged into one,
and one additional app partition of the same new size is added to the
back of the table. the change preserves the littlefs partition (position
and size), allowing for upgrades without loosing the configuration.

another new environment is added, called "generic_esp32_8mb", which uses
the new default partition layout.

environment "generic" is removed. it was merely a variant of
"generic_esp32" with some pins pre-defined. we want users to install a
pin_mapping.json and use the generic firmwares.

environments for boards that have no version with at least 8 MB of flash
memory are setup using the 4 MB partition layout (no OTA updates).

all users must flash the factory.bin for their respective environment
using esptool (or compatible software) using the USB port of their
board. in other words: updating to the new partition scheme using an OTA
update is NOT possible.

hint: the ESP32-S3 builds have a smaller code footprint. this means
ESP32-S3 boards can be updated using OTA without updating the partition
layout on the devices for some unspecified time longer, i.e., until
their firmware binary actually becomes too large for the old sketch
partition size.

the non-factory binary for generic_esp32_4mb_no_ota is NOT collected as
an artifact. going forward, users must update using the factory binary
and using the USB connection to their boards.
2024-06-24 21:39:31 +02:00
Andreas Böhm
79214d750f
refactor PylontechCanBattery to be more generic (#1064) 2024-06-23 19:42:04 +02:00
Bernhard Kirchen
cb0f8f20a8 disable CI builds for generic and generic_esp32
these fail due to the overflowing sketch partition. we can re-enable CI
build for these environments once we decided on how to handle ESP32 with
only 4MB of flash.
2024-06-23 13:21:28 +02:00
Bernhard Kirchen
f21f58c67d adjust MessageOutput for changes in espressif/arduino-esp32
the "serial console" over USB would be garbled badly after switching to
"platform = espressif32@6.7.0". this did not happen to the upstream
version of MessageOutput. we used Serial.flush(), which seemed to be
good in the respective context. however, the changes in

github.com/espressif/arduino-esp32/pull/9462

made flush() detrimental. we remove the use of flush(), as it seems not
to be required (in particular, the upstream project does not use it).
2024-06-21 16:57:17 +02:00
Bernhard Kirchen
034026f782 restore JK BMS dummy implementation
broke in be41e6b9. was unusable as the complete type of DummySerial has
to be available in the JK BMS controller header when defining the
unique_ptr managing the dummy instance. this problem is solved by moving
the whole dummy class into its own header.
2024-06-21 16:57:11 +02:00
Bernhard Kirchen
5e3a53d8d3 Merge upstream tag 'v24.6.10' into development 2024-06-21 16:45:41 +02:00
Andreas Böhm
83ac15405e
Feature: Implement DPL 'overscaling' to compensate shading (#956) 2024-06-20 13:32:29 +02:00
cerise21
ccbaf55808
Feature: Set/obtain DPL target power consumption via MQTT 2024-06-20 13:25:41 +02:00
Bernhard Kirchen
95e560bdc7 fix: optimize margins in live view
all total cards at the top of the live view go into the same row.
bootstrap will line-break after every third column/card, as the row
is row-cols-3.

the battery icon to the right of the project name in the header shall
have marging to said project name.

the rows in the live view now use class mt-0 to counteract bootstraps
negative margin for individual rows.
2024-06-17 17:32:26 +02:00
cerise21
1f6fdb7fc0
Feature: Set/obtain DPL upper power limit via MQTT 2024-06-15 20:21:42 +02:00
Bernhard Kirchen
5ab6fe913b
Prepare Release 2024.06.03 (#1024) 2024-06-03 20:10:56 +02:00
Bernhard Kirchen
5a007e5352 fix: make SDM power meter use serial port manager
instead of hard-coding the use of hardware UART 2, the SDM power meter
instance now asks for a free hardware serial port to use and
instanciates the respective HardwareSerial object using said port.
2024-06-02 22:41:07 +02:00
Bernhard Kirchen
be41e6b906 refactor serial port manager: hand out UARTs FCFS
get rid of particular compile-time designations by UART index. just hand
out the next free index of hardware UARTs, or indicate that none is
available any more.

use names as keys to register and free UARTs.
2024-06-02 22:41:07 +02:00
Bernhard Kirchen
5a1c3af31f Feature: add support for a third Victron MPPT
only on ESP32-S3-USB. this fiddles with the available hardware UARTs to
make it possible to use a third Victron MPPT. if three MPPTs are defined
int the pin mapping, you will not be able to use the SmartShunt and JK
BMS battery interfaces.

note that using a second MPPT will also conflict with the SDM power
meter, and that conflict is not detected, yet.
2024-06-02 22:41:07 +02:00
Bernhard Kirchen
90aafe2cbf check FW bin file size when creating factory.bin 2024-06-01 22:19:58 +02:00
Bernhard Kirchen
c22ae2bf8d Feature: show BMS FW and HW version (JK BMS, SmartShunt) 2024-06-01 11:23:22 +02:00
swingstate
561f4be6d6 Feature: SmartShunt: process midpoint voltage and deviation 2024-05-31 21:08:15 +02:00
Bernhard Kirchen
63370e8a83 VE.Direct: prefer strncpy over strcpy
strcpy is not safe.
2024-05-31 20:16:29 +02:00
Bernhard Kirchen
88314f93c2 Fix: properly format Victron MPPT firmware version 2024-05-31 20:16:29 +02:00
eu-gh
90eb25f503
Fix: Huawei PSU: enforce BatterySoC Limit < 100 in UI (#992) 2024-05-30 21:31:46 +02:00
ButterBetzi
27f264e99b feature: JK BMS: HA auto-discovery for battery temperatures 2024-05-30 21:21:14 +02:00
Bernhard Kirchen
19c8866dd4 fix: Victron MPPT HA auto-discovery: fix unit of temperatures 2024-05-30 20:13:47 +02:00
Bernhard Kirchen
a396e7da26 docs: explain usage of MCP2515 module 2024-05-30 17:58:55 +02:00
Bernhard Kirchen
167b1b4b3a docs: mention ADUM1201 to talk to Victron MPPTs 2024-05-30 17:58:24 +02:00
Bernhard Kirchen
6d1ad44672 docs: tell people to buy ESP32 with 8 MB of flash 2024-05-30 17:58:00 +02:00
Bernhard Kirchen
1f3972f08c docs: mention OpenDTU Fusion board as basic hardware 2024-05-30 17:57:13 +02:00
Bernhard Kirchen
0883bf3dce docs: do not tell users to custom-build because of pins assignments 2024-05-30 17:40:51 +02:00
Bernhard Kirchen
d821dc0e00 docs: device profile docs fully moved to wiki 2024-05-30 17:39:50 +02:00
Bernhard Kirchen
286e22b61a README: update documentation links
the canonical link to the upstream docs is "opendtu.solar".
2024-05-30 17:28:38 +02:00
Bernhard Kirchen
6ab1b41f78 fix: project's name is 'OpenDTU-OnBattery' (capital 'O')
there were 47 matches in the repo for "OnBattery", but only 17 for
"onBattery". Also, the repo's name on github uses a capital 'O'.
2024-05-30 13:55:44 +02:00
Bernhard Kirchen
bdc9c09db2 Feature: show ESP32 flash memory size in system info 2024-05-27 21:54:35 +02:00
Bernhard Kirchen
edf493bc6d DPL web UI: add hint to setting "inverter behind power meter" 2024-05-09 13:58:48 +02:00
Bernhard Kirchen
ca3d1da740 power meter web UI: add example for array access + translations
this re-adds an example JSON path accessing an array in the JSON.
also use translatable texts in the example section.
2024-05-09 13:58:44 +02:00
Bernhard Kirchen
ca96ccad42
Prepare Release 2024.05.07 (merge development into master) 2024-05-07 22:23:58 +02:00
Bernhard Kirchen
841523f028 embrace upstream __compiled_constants.h approach 2024-05-07 22:04:36 +02:00
Bernhard Kirchen
255e81e6bd Merge upstream tag 'v24.5.6' into development 2024-05-07 22:03:26 +02:00
Bernhard Kirchen
1109f460cc workflow: ignore v** tags
this is an attempt to exempt the whole build workflow (and respective releases from being created) for upstream tags.
2024-05-07 20:57:56 +02:00
Bernhard Kirchen
e1169d4b11 Fix: restore JSON array access for HTTP power meter
this implements accessing array members in an ArduinoJSON object
following the FirebaseJson syntax. the FirebaseJson lib was previously
removed to save flash memory, and logic was implemented to find a JSON
node using the FirebaseJson path syntax, restoring the functionality.
however, array access was not implemented.

this change also addresses leading and trailing and double forward
slashes in the path expression.

moreover, much more expressive error messages are now generated in case
the path could not be resolved.
2024-05-07 20:56:58 +02:00
Bernhard Kirchen
3ef789c6a2 Fix: restore JSON array access for HTTP power meter
this implements accessing array members in an ArduinoJSON object
following the FirebaseJson syntax. the FirebaseJson lib was previously
removed to save flash memory, and logic was implemented to find a JSON
node using the FirebaseJson path syntax, restoring the functionality.
however, array access was not implemented.

this change also addresses leading and trailing and double forward
slashes in the path expression.

moreover, much more expressive error messages are now generated in case
the path could not be resolved.
2024-05-07 20:53:49 +02:00
Bernhard Kirchen
35491cafed
Prepare Release 2024.05.06 (merge development into master) 2024-05-06 11:54:31 +02:00
Alexander Kukushkin
2f7e1f3f70
Fix: solar_passthrough_losses setting persistence (#957)
Due to a typo the value of `solar_passtrough_losses` was lost after a restart.
2024-05-06 11:12:25 +02:00
Bernhard Kirchen
b7a8bdf07b remove rweather/Crypt lib from firmware (save 2.1k of flash)
mbedtls is already integral part of the firmware. use it in favor of
rweather/Crypto library to calculate a sha256 checksum of a string, as
used in the HTTP power meter implementation.
2024-05-06 11:04:24 +02:00
Bernhard Kirchen
1dd64a57fd remove FirebaseJson lib from firmware (save 17.5k of flash)
we used this library solely to interpret the answer of an HTTP web
server as JSON and find a particular value using a path expression in
the HTTP power meter implementation.

since we ran out of flash memory on non-S3 ESP32, we need to cut some
corners. removing FirebaseJson is the last low-hanging fruit that we
currently know of. we can get rid of it by using ArduinoJson (which is
already integral part of the firmware) and implementing a custom logic
to extract a value based on a path expression.

other than the FirebaseJson path "finder", the new implementation
only knows how to access sub-keys delimited by a forward slash. in
particular, accessing array members is not supported any more. I am
hoping that this is simply not an issue. if so, we will have users
complaining and we can add this functionality in a later release.
2024-05-06 11:04:24 +02:00
Bernhard Kirchen
d2990bd8fd
Prepare Release 2024.05.03 (merge development into master) 2024-05-03 21:48:11 +02:00
SW-Nico
6620ab487a Fix: VE.Direct: take the load current into account
when calculating efficiency, we need to take into account that the load
might also sink a significant amount of current and power, which adds to
the total output of the charge controller.
2024-05-02 21:46:57 +02:00
eu-gh
686b5df64e
Feature: Publish Huawei AC charger mode via MQTT (#876) 2024-05-02 21:19:25 +02:00
Bernhard Kirchen
744df41b01
Merge pull request #945 from helgeerbe/merge-v24.4.24
Merge upstream v24.4.24
2024-05-02 21:07:03 +02:00
Bernhard Kirchen
18dab3cf1c Merge upstream tag 'v24.4.24' into development 2024-05-02 20:49:41 +02:00
Bernhard Kirchen
dae50ec3b7 workflow: ignore v** tags
this is an attempt to exempt the whole build workflow (and respective releases from being created) for upstream tags.
2024-04-29 21:32:37 +02:00
Bernhard Kirchen
4cf596eb5a
Merge pull request #923 from helgeerbe/merge-v24.4.12
merge upstream tag v24.4.12, resolve conflicts (helgeerbe), fix eslint errors (schlimmchen) and adopt new web api method to save code duplication (schlimmchen).
2024-04-29 21:17:14 +02:00
Bernhard Kirchen
4e36c8c9ea
Feature: battery interface: use HW serial 0 on ESP32-C3 or S3 (#933)
this allows to use two VE.Direct interfaces, as there is no conflict
regarding HW serial port 2 after making the battery interfaces use
serial port 0 on devices with USB CDC. on those chips HW serial 0 is
free to be used since serial messages are written through the USB
interface directly.
2024-04-29 20:43:35 +02:00
Bernhard Kirchen
d3b306e2fc appease eslint 2024-04-29 20:31:50 +02:00
Bernhard Kirchen
84e83f2dbb adopt WebApiClass::parseRequestData() method
saves redundant code, reducing flash usage.
2024-04-29 20:31:50 +02:00
MalteSchm
64738a6246 Introducing defines for RX2/TX2 2024-04-25 22:54:12 +02:00
helgeerbe
fdc5054480 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into merge-v24.4.12 2024-04-25 20:59:19 +02:00
MalteSchm
f0df583c13
Feature: support for Huawei target power consumption 2024-04-24 20:26:56 +02:00
Bernhard Kirchen
74330a5617 Feature: restart unresponsive inverter
we found that the inverter sometimes stops responding to commands,
especially to the "start producing" command. we now count the number of
consecutive timeouts when trying to send a new limit or power state
commands. after two timeouts were recorded, every additional timeout
will send a restart command to the inverter.

as a last resort, if the counter keeps climbing, the DTU is restarted.

notice that this only targets unresponsive inverters which are
reachable. unreachable inverters are not restarted and do not cause a
DTU reboot. this is important for solar-driven inverters, which are
unreachable during the night. the DPL will not calculate a new limit and
hence the updateInverter() method will do nothing while the target
inverter is unreachable.

publish the timeout counter to MQTT for monitoring purposes.
2024-04-24 16:15:01 +02:00
Bernhard Kirchen
eb9bfd1ac6 Fix: DPL mode 2 for solar-powered inverters
for solar-powered inverters, the unconditional full solar-passthrough
implementation now sets the upper limit configured in the DPL settings.
2024-04-22 12:51:31 +02:00
Bernhard Kirchen
1d4bea24ff Fix: JK BMS: BMS name is second part of product ID 2024-04-18 16:36:11 +02:00
Bernhard Kirchen
4cd690de66 VE.Direct: publish data retrieved from HEX protocol 2024-04-18 13:06:37 +02:00
Bernhard Kirchen
abe01ae36f Feature: HTTP power meter: support changing sign 2024-04-18 12:10:29 +02:00
Bernhard Kirchen
ede1abb5e6 Feature: HTTP power meter: support mW/kW as units 2024-04-18 12:10:29 +02:00
Bernhard Kirchen
247cfe712e HTTP power meter: prevent out-of-bound array access 2024-04-18 12:10:29 +02:00
Bernhard Kirchen
e92701ccdf reuse power meter's HTTP config struct 2024-04-18 12:10:29 +02:00
Bernhard Kirchen
4bc4defe66 DPL: insist on power meter value more recent than inverter stats
avoid performing a calculation based on a (slightly) outdated power
meter reading, which was aquired just before the limit was actually
applied by the inverter, but which was received by OpenDTU-OnBattery
after the inverter stats.
2024-04-15 23:02:58 +02:00
Bernhard Kirchen
5fcf09d0a0 fix hysteresis hint texts 2024-04-15 23:02:58 +02:00
Bernhard Kirchen
52d7ac9581 Feature: DPL: support setups without power meter
without a power meter configured, the DPL now sets the base load as the
inverter limit if the battery charge allows it. it also takes
solar-passthrough into account, i.e., if the battery is in a charge
cycle but the solar output (Victron MPPT) is significant, the solar
power will be used up until the base load. if the battery reaches the
full solar passthrough threshold, the DPL will match the inverter limit
to the MPPT solar output.
2024-04-15 23:02:58 +02:00
Bernhard Kirchen
7e307114e5 Feature: DPL: introduce base load setting
on power meter issues (usually a timeout), keep the inverter enabled and
make it produce the configured base load limit if the battery can be
discharged. that should be okay since the base load config value is
expected to be small and a little less than the actual household base
load, i.e., if this amount of power is produced, the household will
consume it in any case and no energy is fed into the grid.
2024-04-15 23:02:58 +02:00
Bernhard Kirchen
cf1ea42f8b power limiter: remove obsolete enum 2024-04-15 23:02:58 +02:00
Bernhard Kirchen
4c2822cdbc remove usage of F() macro
frees 888 Bytes of flash.
2024-04-12 15:33:47 +02:00
Bernhard Kirchen
8b3a1bef47 Fix: show AC input power of Huawei AC charger in live view
makes the value match its description. since most values in the top part
of the live view are related to the AC side of the system, it makes
sense to use the correct value rather than to change the description.
2024-04-11 14:27:47 +02:00
Bernhard Kirchen
f634f58788 Fix: DPL: use correct channel type to get inverter efficiency 2024-04-11 08:28:49 +02:00
Bernhard Kirchen
a9c3e05f05 PowerMeter admin: URL examples to the top and hidden if disabled 2024-04-10 20:44:46 +02:00
PhilJaro
165a9bc168
adjust VE.Direct MPPT yield resulotion (#859) 2024-04-09 20:31:05 +02:00
eu-gh
0ed09aeb4c Feature: Huawei: add SoC stop threshold and verbose logging switch 2024-04-07 17:16:50 +02:00
SW-Nico
b9ad1e3054 VE.Direct: process more values and refactor variable names
* process "IL", "AR" and "MON"
* discard "BMV" and (unsolicited) History Data
* simplify isDataValid()
* veMpptStruct, veStruct: new, verbose variable names, including units,
  and replace floats (save values with original integer precision)
* comment on rollover situation in isDataValid()
2024-04-07 17:13:07 +02:00
Bernhard Kirchen
3934906001
Merge pull request #836 from helgeerbe/merge-v24.3.31
Merges v24.3.31 from upstream
2024-04-03 20:08:09 +02:00
Bernhard Kirchen
21cdc69625 Feature: use VE.Direct "network total DC power"
1. makes the DPL use the power generated by all connected charge
   controllers for calculations based on solar passthrough.
2. makes the network total DC power appear as "MPPT Total Power" in the
   live view at the top.
3. shows the network total DC power in the VE.Direct live data card.
2024-04-03 16:33:15 +02:00
Bernhard Kirchen
6b8c93d2e6 polish VE.Direct HEX support
* show charge controller temperature in live view
* send hex requests right after decoding a frame. this seems to have the
  best chance of getting an answer to all requests.
* deem 0xFFFFFFFF value of network total DC power as invalid indicator.
  neither network state, nor network info, nor network mode seem to
  indicate that the charge controller is part of a VE.Smart network. for
  that reason, we revert to always querying the network total DC power
  value, but testing it for max(uin32_t) value, which seems to indicate
  that the charge controller is not part of a VE.Smart network.
* improve (verbose) logging, e.g., use _logId, and print names of
  response codes and known registers, always print error messages,
  add additional tests to prevent overly verbose messages.
* move hex protocol definitions to VeDirectData.h header
  and use enum classes
* define register addresses in enum class
* move values retrieved through hex protocol into main MPPT data struct
* do not send HEX requests if the serial interface cannot send data
* detect whether smart battery sense temperature is available
* web app: make all VE.Direct sub-cards iterable. this makes addind more
  values much simpler and saves a bunch of code in the web app.
* make VeDirectFrameHandler state a type-safe enum class
* unindent MPPT controller loop()
* whitespace cleanup
2024-04-03 16:33:15 +02:00
SW-Nico
aadd7303ac Feature: add support for VE.Direct hex messages 2024-04-03 16:33:15 +02:00
helgeerbe
ff44267e73 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into merge-v24.3.31 2024-04-03 11:53:03 +02:00
Bernhard Kirchen
8c6e925ca4 VE.Direct: make state machine timeout robust against overflow 2024-04-02 21:05:59 +02:00
Bernhard Kirchen
43f553d2d4 VE.Direct MQTT: simplify code
the use of a #define is warranted here since it saves a lot of code
duplication and improves code readability.
2024-04-02 21:05:59 +02:00
Bernhard Kirchen
92a7f27919 VE.Direct: use float rather than double
double precision floating point numbers are not needed to handle
VE.Direct values. handling double is implemented in software and hence
*much* more resource intensive.
2024-04-02 21:05:59 +02:00
Bernhard Kirchen
b299b9dc6c VE.Direct: simplify access to data
hand out const& to the data structs. this is possible now that this
struct is "stable", i.e., not reset regularly.
2024-04-02 21:05:59 +02:00
Bernhard Kirchen
ad125ea804 Fix: properly handle fragmented VE.Direct messages
queue every text event until the frame was checked by it checksum. then
process the data directly into the buffer struct. do not clear the
buffer struct, so it will always include the most recent value of a
particular data point.
2024-04-02 21:05:59 +02:00
Bernhard Kirchen
187f197e32 Feature: add unique prefix to VE.Direct messages 2024-04-02 21:05:59 +02:00
MalteSchm
8abf614047 Feature: BMS initiated emergency charging
This change logically connects the AC-Charger with the BMS to add BMS
initiated emergency charging and respecting BMS current limits.
2024-04-02 21:05:00 +02:00
PhilJaro
da96273085
Fix typo in German locale (#831) 2024-04-02 12:05:45 +02:00
Bernhard Kirchen
91a0992964 fix: VE.Direct MPPT data not always updated in websocket
set the "last published" timestampt after handling *all* MPPTs.
2024-03-26 20:48:39 +01:00
David von Oheimb
811b64adb5 PowerLimiter.cpp: simplification and minor correction of logic table comments 2024-03-25 10:55:00 +01:00
Bernhard Kirchen
0169b29cfd
Merge development into master to prepare release 2024.03.23
Prepare release 2024.03.23
2024-03-23 23:16:34 +01:00
Bernhard Kirchen
06f39f8396 Merge remote-tracking branch 'tbnobody/master' into development 2024-03-23 22:58:21 +01:00
Bernhard Kirchen
12f7caf998 fix: HTTP power meter: retrieve multiple JSON paths
if only a single request was made (switch "Individual HTTP requests per
phase" is off), the user could still enable phase 2 and phase 3 config
and configure a respective JSON path. however, the value was never
extracted from the successful HTTP request for phase 1.

closes #637.
2024-03-23 22:13:59 +01:00
Bernhard Kirchen
b8d0998a49 HTTP power meter: refactor tryGetFloatValueForPhase
unindent code, prepare this method for re-use on the same HTTP response
but with a different JSON path.
2024-03-23 22:13:59 +01:00
Bernhard Kirchen
c41d6f5138 HTTP power meter: remove trailing whitespace
this commit is empty when inspecting it with --ignore-all-space.
2024-03-23 22:13:59 +01:00
Bernhard Kirchen
054a677575 DPL settings in web app: split metadata from config
users are manipulating the DPL using HTTP POST requests. often they are
requesting the current settings using HTTP GET on the respective route,
then change a particular settings, and send all the data back using HTTP
POST. if they failed to remove the metadata node from the JSON,
OpenDTU-OnBattery would not be able to process the JSON due to its size.
the web app does not submit the metadata.

to avoid problems, the metadata is now split from the configuration
data.
2024-03-23 22:13:50 +01:00
Bernhard Kirchen
8bfb5c6523 implement and use Utils::checkJsonOverflow()
this method calls the overflowed() method on the respective
DynamicJsonDocument and prints a respective message if not all
data could be added to the DynamicJsonDocument.
2024-03-23 17:57:13 +01:00
Bernhard Kirchen
1fb2d4262e do not publish live data for broken MPPT controllers
in case the user defines the respective pins, an MPPT controller will be
setup and the websocket would previously publish its invalid data even
if no data at all was received. this lead to using an invalid index and
to publish a useless card. instead, skip those controllers.

if the connection to a controller breaks, it will always present as a
card in the live view with the respective serial number and the last
known data.
2024-03-23 17:56:51 +01:00
Bernhard Kirchen
0154da91ab fix VE.Direct live data response size
PLLIMIT would not (always) be part of the JSON string transmitted to the
web application and trip it. with the help of the ArduinoJson assistant,
new values to use in the response size calculation were introduced.
also, importantly, a constant offset was added for the DPL status and
the JSON structure sourrounding the MPPTs.
2024-03-23 17:56:51 +01:00
Bernhard Kirchen
1c51c2de40 fix and harden BatteryStats update timestamp handling
* updating the SoC or value shall also update the general timestamp, as
  the latter is defined as "any value changed", which includes SoC and
  voltage, of course.
* if the last update is not a valid timestamp at all, the
  updateAvailable method must always return false, obviously.
2024-03-23 17:56:46 +01:00
Bernhard Kirchen
d935283d1f avoid sending null through live data websockets
on the "main" live data websocket this can happen if no
OnBattery-specific data is available to sent but the empty
JSON document is still sent, which trips the web application.

publishing null in the battery live data websocket also trips the web
application, which rightfully assumes a valid object if data is received
through the websocket.
2024-03-23 17:54:45 +01:00
Bernhard Kirchen
13e42051b6 DPL: improve verbose logging (more variables logged) 2024-03-22 21:50:42 +01:00
helgeerbe
03435f66ee Feature: Set max number of supported Inverters to 10 (upstream default)
Due to the latest live view api changes, I revert the maximum number of supported inverters to the upstream default (10).
2024-03-22 13:50:16 +01:00
Snoopy-HSS
bbaed260f5
SMA Homemanager: fix power value calculation
when consuming from the grid, the power meter value shall be positive, and it shall be negative when exporting power.
2024-03-22 10:20:53 +01:00
Bernhard Kirchen
b449dd1196 replace VICTRON_MAX_COUNT
determine the amount of controllers actually in use dynamically,
especially to avoid indices which are invalid, causing an error
to be printed, even though the user did not do anything wrong.
2024-03-20 22:11:11 +01:00
Bernhard Kirchen
8e5e8d169d fix: VE.Direct live data after supporting second MPPT
an error was made when determining whether or not to push a VE.Direct
MPPT's state through the websocket based on its data's age.
2024-03-20 22:11:10 +01:00
Bernhard Kirchen
cffc5b1d26
Merge pull request #778 from helgeerbe/upstream-v24.3.15
merges upstream v24.3.15
2024-03-20 21:00:04 +01:00
helgeerbe
5259fc172a Merge remote-tracking branch 'tbnobody/OpenDTU/master' into v24.3.15 2024-03-20 09:42:35 +01:00
Bernhard Kirchen
cd339a3a14 Feature: implement PowerMeter pin config for serial interfaces
in your pin_mapping.json, add a powermeter object like this:

[
    {
        "name": "My Board",
        ...
        "powermeter": {
            "rx": <num>,
            "tx": <num>,
            "dere": <num>
        },
        ...
    }
]

the SML power meter requires the rx pin to be set. the SDM power meter
requires the rx and tx pins are set. the "dere" pin pin is optional and
if set, this pin controls the driver enable and receiver enable pins of
the RS485 transceiver. the SDM library handles this pin.

closes #771.
2024-03-19 21:27:25 +01:00
Bernhard Kirchen
7d4a30dde4
hardware_flash.md: move some info to wiki
closes #628
2024-03-19 20:01:25 +01:00
PhilJaro
4f0385285c add new Victron SmartShunt values to liveView and MQTT (HASS) 2024-03-17 21:00:32 +01:00
Bernhard Kirchen
45c7243937 polish SMA HomeManager integration
* remove/comment unused variables to avoid compiler warnings

* cleanups: fix indention and style, make variable private, implement
  getters in header and make const.

* optimize message output: respect verbose logging setting. prefix
  output with SMA_HM.

* use newly introduced mutex in PowerMeterClass also for SMA HomeManager

* refactor code for readibility, unindent where possible.
2024-03-17 20:20:29 +01:00
Snoopy-HSS
f6680bd664 Feature: Support SMA HomeManager 2.0 as PowerMeter 2024-03-17 20:20:29 +01:00
Bernhard Kirchen
900326742c fix: prevent getTotalPower() reading intermediate results
the SDM power meter (among others) writes the power consumption of three
phases in multiple steps. this change helps to prevent getTotalPower()
reading intermediate values, e.g., reading a new value for phase 1 but
old values for phase 2 and 3 since phase 2 is currently read.

cache the values, and write them all at once, protected by a mutex,
later.

closes #732.
2024-03-17 16:54:48 +01:00
Bernhard Kirchen
13bc943dd5 Feature: Refactor/Simplify DPL settings
this changeset refactors the web application's DPL settings view. the
DPL settings can be complex, and they shall be presented in a way that
allows users to comprehend their meaning. irrelevant settings are now
hidden or displayed dynamically based on the influencing settings.

* group SoC thresholds into their own card

* hide battery SoC thresholds if battery disabled. if the user did not
  even enable the battery interface, battery SoC values will not be used
  for DPL decisions. in that case we completely hide the respective
  settings from the DPL admin view. this reduces the amount of settings
  for new users and especially users who don't even have a battery in
  their setup or have no BMS connected.

* group voltage thresholds and improve label texts

* fix load correction factor unit

* fix header (wording)

* group solar-passthrough settings in new card

* group inverter-related settings

* hide solar passthrough settings if VE.Direct is disabled. closes #662.

* completely disable form if any requirement is not met

* list available inverters by name and type. this makes it much more
  convenient to select the right inverter, especially since the order of
  the inverters in the web UI is decoupled from their position in the
  internal array, which was used to select them previously. care was
  taken that old configs select the same inverter after an update.
  when editing the DPL settings, the selects an inverter from the newly
  created drow-down list, and the respective old inverter is
  pre-selected.

* disable form if no inverter is configured (config alert)

* make inverter input selection dynamic. adjust selection to actual
  amount of channels for selected inverter. skip selection altogether if
  inverter has only one channel, or if it is solar powered.

* web app: wording adjustments

* group meta data into new property and exclude from submission. saves
  memory when evaluating the submitted settings.

* hide irrelevant settings if inverter is solar-powered

* move restart hour setting to inverter card. translate setting which
  disabled automatic restart.

* simplify "drain strategy" setting into an on/off toggle. care was
  taken that existing configs work the same after an upgrade. the
  respective drain strategy is translated into the new setting when
  reading the config. once the config is written, the new setting is
  persisted and the old is not part of the config any more.

* show more configuration hints, depending on actual configuration

* replace inputs by InputElement components where possible
2024-03-17 16:50:57 +01:00
Bernhard Kirchen
7d6b7252bf polish support for second VE.Direct MPPT charge controller
* fix compiler warning in SerialPortManager.cpp: function must not
  return void

* clean up and simplify implementation of usesHwPort2()
  * make const
  * overrides are final
  * default implementation returns false
  * implement in header, as the implementation is very simple

* rename PortManager to SerialPortManager. as "PortManager" is too
  generic, the static instance of the serial port manager is renamed to
  "SerialPortManager". the class is therefore renamed to
  SerialPortManagerClass, which is in line with other (static) classes
  withing OpenDTU(-OnBattery).

* implement separate data ages for MPPT charge controllers

* make sure MPPT data and live data time out

* do not use invalid data of MPPT controlers for calculations

* add :key binding to v-for iterating over MPPT instances
2024-03-17 16:50:15 +01:00
Arman Vartan
75541be248 Feature: Support for second Victron MPPT charge controller
this change adds support for a second Victron MPPT charge controller
using a second serial connection.

* Add device configuration for a second victron mppt
* Update VedirectView for second victron mppt
* Update MqttHandleVedirect for second victron mppt
* Update MqttHandleVedirectHass for second victron mppt
* Handle nonexisting victron controllers with optionals
* Add bool-function to Battery and inherited classes, if uart port 2 is
  being used
* Introduced a serial port manager. In order to prevent the battery and
  the Victron MPPT to use the same hw serial ports, this class keeps
  track of the used ports and their owners.
2024-03-17 16:50:15 +01:00
Bernhard Kirchen
21c19f4b7f fix: preserve SmartShunt energy values precision 2024-03-17 08:31:54 +01:00
Bernhard Kirchen
7ebd4f4632 clean up defaults.h
* remove duplicated #defines. this is most probably a merge error from
  2024-01-16, as evidenced by 63205f88b, which added these duplicates.

* sort values by upstream and downstream projects. add a comment which
  tells us in the future where OpenDTU-OnBattery-specific values start.
2024-03-16 14:31:07 +01:00
Bernhard Kirchen
2efa1b35b0 live view: do not access undefined data
if the respective feature is disabled, there is no data. do not try to
access it.
2024-03-15 08:55:21 +01:00
Bernhard Kirchen
5c6b4a8f12 disable restarting solar-powered inverters
now that users can tell the DPL that their inverter is not powered by a
battery but powered by solar panels, we shall not restart inverters to
reset the daily yield value, if they are solar powered. these inverters
will reboot every night by themselves.
2024-03-14 21:37:47 +01:00
Bernhard Kirchen
8895791145 live data: exclude data if respective feature disabled 2024-03-14 21:37:47 +01:00
Bernhard Kirchen
cf27bd29d7 adjust (new) about texts to OpenDTU-OnBattery 2024-03-14 16:27:32 +01:00
Bernhard Kirchen
56353e4f00 implement Battery::needsCharging()
currently this is only supported by the Pylontech battery provider, as
it reports a "charge battery immediately" alarm. this will also be
implemented by the JK BMS provider, and possibly also by the smart shunt
provider.

the method will be used to determine whether or not to start charging
the battery using the (Huawei) charger.
2024-03-13 17:07:05 +01:00
Bernhard Kirchen
784e369482 optimize DPL thresholds MQTT integration
* fix logic in HomeAssistent handler
* also publish voltage thresholds (not just SoC thresholds)
* do not publish irrelevant thresholds to MQTT. if the inverter is
  solar-powered, no thresholds are effectively in use by the DPL and it
  therefore makes no sense to publish them to the broker. similarly, if
  no battery interface is enabled or the SoC values are set to be
  ignored, the SoC thresholds are effectively not in use and will not be
  published to the broker.
* make HA auto-discovery expire. this makes auto-dicovered items
  disappear from Home Assistent if their value is no longer updated.
  changes to settings which cause other thresholds to be relevant will
  then be reflected in Home Assistent even if some thresholds are no
  longer maintaned in MQTT.
* force HA update when related settings change enabling VE.Direct shall
  trigger an update since solar passthrough thresholds become relevant.
  similarly, enabling the battery interface makes SoC thresholds become
  relevant. there are more settings in the power limiter that also
  influence the auto-discoverable items.
* break very long lines
2024-03-10 22:10:02 +01:00
LukasK13
fba5c02346 Feature: Set powerlimiter thresholds via MQTT
publish DPL thresholds to MQTT, add support for setting powerlimiter
thresholds via MQTT, and make these auto-discoverable for Home
Assistent.
2024-03-10 22:10:02 +01:00
Bernhard Kirchen
803b30ca11 fix: wrong unit in battery HomeAssistent exp_aft
the exp_aft value is in seconds.
2024-03-10 21:20:56 +01:00
Bernhard Kirchen
19859ed601
Merge pull request #733 from helgeerbe/dpl-make-shutdown-partially-optional
DPL: Explicit Support for solar-powered inverters
2024-03-10 17:25:30 +01:00
Bernhard Kirchen
c46980d6af fix: more memory for onBattery live data
the allocated memory to create the JSON with onBattery-specific totals
for the live view was too little to contain all values, which are sent
regularly.
2024-03-10 15:57:50 +01:00
Bernhard Kirchen
80edbec769 Feature: DPL: keep inverter running if solar powered
avoid shutting down the inverter at all if the calculated power limit
falls below the lower power limit or if the power meter value is
outdated. do this only if the inverter is setup to be solar powered.
2024-03-09 15:42:09 +01:00
Bernhard Kirchen
91f8f61e63 Feature: DPL: explicit support for solar powered inverters
by default and until this change, we assumed that the inverter
controlled by the DPL is powered by a battery. not all users have a
battery in their system. they still use the DPL to achieve net-zero
export. those users can now tell the DPL that their inverter is powered
by solar modules rather than a battery and the DPL will behave
accordingly.
2024-03-09 15:40:13 +01:00
Bernhard Kirchen
be15050aed DPL: refactor code determining battery charge cycle state 2024-03-09 15:40:13 +01:00
Bernhard Kirchen
c6f81806d6 DPL: make "IsInverterSolarPowered" configurable through web app 2024-03-09 15:40:13 +01:00
Bernhard Kirchen
b11b1dbcba DPL: define IsInverterSolarPowered config switch 2024-03-08 22:53:34 +01:00
Bernhard Kirchen
490a38f909
Merge pull request #726 from helgeerbe/development
Prepare Release 2024.03.07
2024-03-07 22:23:46 +01:00
Bernhard Kirchen
c42d68812c DPL limit scaling: prevent division by zero
this check was removed on error when moving the scaling code into its
own function.
2024-03-07 21:18:11 +01:00
Bernhard Kirchen
b0795a2131 DPL limit scaling: only for supported models 2024-03-07 21:18:09 +01:00
Bernhard Kirchen
64ad4bded1 fix: DPL: limit scaling sanity checks
do not scale limit if inverter is not producing, as DC channel power is
expected to be close to zero anyways.

do not scale limit if current inverter limit is small, such that
channels might produce very little power exactly because the limit is so
low.

move the calculation out of setNewPowerLimit and into a new function, so
that we can make use of return statements there.
2024-03-07 20:54:16 +01:00
Bernhard Kirchen
50635ee2ce Feature: live view: update with respective frequency
the update frequency of Victron MPPT charger data, the battery Soc, the
huawei charger power, and the power meter differ from one another, and
differ in particular from the inverter update frequency.

the OnBattery-specific data is now handled in a new method, outside the
upstream code, which merely call the new function(s). the new function
will update the websocket independently from inverter updates. also, it
adds the respective data if it actually changed since it was last
updated through the websocket.

for the webapp to be able to recover in case of errors, all values are
also written to the websocket with a fixed interval of 10 seconds.
2024-03-05 11:31:44 +01:00
Bernhard Kirchen
e432f0eca3 make BateryStats::updateAvailable wrap-around-safe 2024-03-05 11:31:44 +01:00
Bernhard Kirchen
fe7e622e2d pull requests: use a meaningful branch when building
This change makes the build runner switch to a meaningful branch name, which will then appear as the "Firmware Branch" in the System Info of the web application. This helps users testing pull-request builds identify that they are actually using the changes from the respective pull request.
2024-03-05 11:26:38 +01:00
helgeerbe
78e70cc6c5
Merge pull request #708 from schlimmchen/dpl-manage-inverter-state
Fix: DPL: ensure inverter reaches requested state
2024-03-05 10:21:16 +01:00
Bernhard Kirchen
8b6e57cda7 Fix: DPL: ensure inverter reaches requested state
we previously only called commitPowerLimit() if the desired limit
changed such that the change was bigger than the hysteresis. we found
that if the limit update was not received and the desired limit would
not change much, the limit of the inverter was wrong for a long time.

to mitigate this, we introduced re-sending the limit update every 60
seconds, regardless of what the limit reported by the inverter was at
that time.

if the power-up command was not received, we also would repeat it only
once every 60 seconds.

this leads to a new kind of staleness and the actual inverter state was
still not matching the desired state.

this new approach effectively adds an additional control loop at the
start of the DPL loop(). that new function compares the requested
inverter state to the actual reported state. it sends updates (limit
update or power on state) until the desired inverter state is reached,
or until a (hard-coded) timeout occurs.

this approach also allows us to send power-up, power-down, and limit
update commands independent from one another and in a particular order.

this should make sure that the inverter is in the desired state even if
conditions change slowly and commands were not received as expected.
2024-03-02 23:39:43 +01:00
Bernhard Kirchen
c560d1d90e
fix: copy OnBattery-specific data from live view websocket (#696)
closes #685.
closes #682.
2024-02-29 17:09:18 +01:00
helgeerbe
f0f8702b4b Merge branch 'development' 2024-02-19 16:05:33 +01:00
helgeerbe
c72ae561c7 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2024-02-19 16:03:56 +01:00
helgeerbe
4e7dfba85c add webapp 2024-02-19 13:51:59 +01:00
helgeerbe
1eb75c322d
Merge pull request #679 from schlimmchen/dpl-voltage-features
DPL Voltage Features
2024-02-19 13:48:12 +01:00
Bernhard Kirchen
9240663552
Feature: show power grid usage on display (#658)
* make efficient use of available display area

fix calculation of the text baselines, using getAscent() in favor of
getMaxCharHeight(), which includes ascent and descent. this moves the
first text up and allows to insert margin between the lines until the
display area is fully utilized.

on large displays, if the small diagram is selected, keep the first line
rather low to avoid collision with the diagram y-axis label. in this mode,
there is still more space between the text lines as before, allowing for
improved readability.

* Feature: show power grid usage on display

if the power meter is enabled, the display will use two of three out
of every three-second time slot to show the grid consumption.

closes #620.
2024-02-19 13:38:57 +01:00
Bernhard Kirchen
c930018764 Feature: DPL: use best available voltage value
the DPL is interested in the battery's voltage to make decisions about
draining the battery or letting it charge (if the user opts to use
voltage thresholds rather than SoC thresholds). using the DC input
voltage reported by the inverter under control has disadvantages:

* the data might be quite old due to the communication protocol
  implementation. more inverters being polled means even more lag. the
  connection being wireless makes this even worse, due to the need
  to retry the occasional lost packet, etc.
* the data is not very accurate, since the DC input of the inverter is
  actually some cabling and a couple of junctions away from the actual
  battery. this voltage drop can mostly only be estimated and is worse
  with higher load. the load correction factor is there to mitigate
  this, but it has its own problems and is cumbersome to calibrate.

instead, this change aims to use more accurate battery voltage readings,
if possible. the DPL now prefers the voltage as reported by the BMS,
since it is for sure the closest to the battery of all measuring points
and measures its voltage accurately regardless of the load (the voltage
reading will still drop with higher loads, but this will be only due to
the battery's internal resistance, not that of cabling or junctions). if
no BMS voltage reading is available, the DPL will instead use the charge
controller's voltage reading, as it is available with much higher
frequency and is assumed to be more accurate as it offers a resolution
of 10mV. only if none of these two sources can be used, the inverter DC
input voltage is assumed as the battery voltage.

closes #655.
2024-02-18 22:17:15 +01:00
Bernhard Kirchen
6df358242c Feature: know and use SoC precision
the Victron SmartShunt communicates the SoC value in permille. this
should be displayed in the web UI accordingly. this is a good excuse to
fully move ownership of the SoC value to the BatteryStats base class and
add a precision indicator variable. this is required to be set each time
a derived class (a battery provider) wants to update the SoC value. the
precision is then used when populating the JSON data for the web UI
(live view).

related to #573.
2024-02-18 22:17:15 +01:00
Bernhard Kirchen
7c069b1cc4 replace BatteryStats::isValid() method
in the respective context, the DPL only needs to be sure that the SoC
value is not outdated. it should not even care about other values
reported by the battery interface. hence, the isValid() method shall be
concerned with the SoC value timestamp only. the method is renamed for
clarity.
2024-02-18 22:17:15 +01:00
Bernhard Kirchen
3595725f8a Feature: implement subscription to battery voltage MQTT topic
this extends the MqttBattery implementation by an additional topic which
allows to subscribe to receive battery voltage readings through the MQTT
broker. similar to the battery SoC topic, this allows to import a
critical battery data point for the DPL, in case the user chooses to use
voltage thresholds rather than SoC thresholds to control the DPL. if an
otherwise incompatible BMS is available which publishes the battery pack
voltage through MQTT, this can now be used to feed accurate voltage
readings to the DPL.
2024-02-18 22:17:15 +01:00
Bernhard Kirchen
30bfffb848 BatteryStats: manage battery pack voltage in base class
the BatteryStats base class shall be able to tell the total battery pack
voltage. for that reason, and to avoid code duplication, the voltage is
now handled in the base class and treated as a datum that is common to
all battery providers.
2024-02-18 21:49:12 +01:00
Bernhard Kirchen
921302bf73 Feature: DPL: add switch allowing to ignore SoC
unfortunately, the battery SoC values reported by battery BMSs are
unreliable, at least for some users, or at least without regular
(manual) full charge cycles to calibrate the BMS. it offers great
advantages to connect OpenDTU-OnBattery to a BMS (MQTT publishing of
values, Home Assistent integration, etc.), but previously the users
were then forced to configure the DPL by SoC values.

this change allows to configure the DPL such that SoC values are
ignored. instead, the voltage limits are used to make DPL decisions, as
if no SoC was available in the first place.

the SoC related setting are hidden from the DPL settings view if SoC
values are configured to be ignored.

closes #654.
2024-02-18 21:46:42 +01:00
helgeerbe
b3d8e984bc Merge branch 'development' 2024-02-09 20:35:46 +01:00
helgeerbe
b794f46ef0 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2024-02-09 20:14:58 +01:00
Bernhard Kirchen
7c84621ea9
update VE.Direct product IDsfixes typos and errors, and adds previously unknown PIDs.closes #641.
* update VE.Direct product IDs

fixes typos and errors, and adds previously unknown PIDs.

closes #641.
2024-02-09 19:50:36 +01:00
helgeerbe
df5b416b3f
Merge pull request #640 from schlimmchen:jkbms-home-assistent-pr
JK BMS Home Assistent Integration
2024-02-09 19:46:59 +01:00
Bernhard Kirchen
1865113842 Feature: JK BMS Home Assistent integration
* pylontech HA integration: remove unused method/variable

* make MqttHandlePylontechHassClass::publishConfig() private.
  there are no outside users of that method.

* rename to MqttHandleBatteryHass

* battery HA integration: merge methods and bring back forceUpdate().
  even though the forceUpdate() method was not in use before, it makes
  sense to implement it and use it when the battery config changes.
  rather than controlling a separate flag, it now changes the _doPublish
  flag of the class, which also triggers publishing the device config to
  Home Assistant when an MQTT connection problem was detected. since
  both situations are now handled similarly, we can merge the loop() and
  publishConfig() methods.

* battery: provider specific sensors for HA

* move Battery MQTT loop to BatteryStats
  the BatteryStats class should handle the MQTT publishing, including the
  interval. for the calculation of a reasonable Home Assistent expiration
  value this class now also knows the maximum publish interval.

* JK BMS: fix publishing values for Home Assistent
  Home Assistent values expire, because we set them to expire after three
  MQTT publish durations. for that reason, we need to re-publish all
  values after our self-inflicted full publish interval.

* define JK BMS sensors for Home Assistent

closes #482.
2024-02-05 21:22:07 +01:00
Bernhard Kirchen
c2b49931be Fix: must call Pylontech Home Assistent init() method
previously, the Pytlontech Home Assistent class implementation had an
init() method, that was never called, as it did nothing. the class
relied on its loop() method being called from the main loop(). after
switching to the TaskScheduler approach, the Pylontech Home Assistent
class init() method was adjusted to register a task that calls the
loop() method periodically. however, the init() method was still not
called.
2024-02-05 20:54:34 +01:00
helgeerbe
db51680712 Merge branch 'development' 2024-01-26 14:42:24 +01:00
helgeerbe
1916d6d6a8 check twice a second for new vedirect data on live viw 2024-01-26 14:32:44 +01:00
helgeerbe
7094bca7db fix: Inverter Labels in InverterTotalInfo 2024-01-26 13:34:03 +01:00
helgeerbe
ebacc2f25f Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2024-01-25 23:36:32 +01:00
helgeerbe
f709bb7a78 Merge branch 'development' 2024-01-17 14:18:14 +01:00
helgeerbe
f305b40be0 Merge pull request #612 from Fribur:development
- fix to HttpPowermeter not using explicitly specified non standard ports
- Revert back to using FirebaseJson instead of ArduinoJson
2024-01-17 13:26:52 +01:00
helgeerbe
7439ad0934
Merge pull request #612 from Fribur:development
- fix to HttpPowermeter not using explicitly specified non standard ports
- Revert back to using FirebaseJson instead of ArduinoJson
2024-01-17 13:24:08 +01:00
Fribur
75e3d03ea4 Revert back to using FirebaseJson instead of ArduinoJson
There is no convenient way to access arrays of Json objects and nested keys such as testarray/[2]/myvalue using ArduinoJson. It would at the very least to split the path into a number of individual strings and then access the value like json[testarray][2][myvalue] More details in https://arduinojson.org/v6/doc/deserialization/ . Conclusion: too complicated.
2024-01-16 19:32:02 -05:00
Fribur
8392c7cd8c fix to HttpPowermeter not using explicitly specified non standard
should resolve the connection refused issues reported in https://github.com/helgeerbe/OpenDTU-OnBattery/issues/611
2024-01-16 16:34:46 -05:00
helgeerbe
7c66965ced Merge branch 'development' 2024-01-16 17:48:46 +01:00
helgeerbe
63205f88be Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2024-01-16 17:39:45 +01:00
helgeerbe
b8fcdf3998 build: add manual lib dependency that ESPSoftwareSerial 8.0.1 can compile
Add lib dependency
https://github.com/dok-net/ghostl @ ^1.0.1
manually. ESPSoftwareSerial 8.0.1 has incorrect debendency configuration.
2024-01-16 12:55:38 +01:00
Fribur
e136e096f4
Merge branch 'development' into development 2024-01-08 11:00:59 -05:00
helgeerbe
ffd189c1f5 Merge branch 'development' 2024-01-07 19:09:26 +01:00
Bernhard Kirchen
a012d81427
avoid too frequent SmartShunt data copies (#596)
currently the whole SmartShunt data structure is copied to the
BatteryStats instance in every loop, even though the data cannot
possibly have changed. this is quite an expensive task to do in every
loop. this change tracks the last update timestamp and only does the
copy operation if an actual updated data structure was received from
the smart shunt.
2024-01-07 18:30:02 +01:00
helgeerbe
2806b6db86 set dependency for plerup/EspSoftwareSerial to 8.0.1
8.2.0 has unresolvable dependencys
2024-01-07 18:00:57 +01:00
helgeerbe
642f38ce51 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2024-01-07 11:01:30 +01:00
helgeerbe
44d207a5f5 add webapp 2024-01-07 10:31:22 +01:00
helgeerbe
dd5d5ce9fd Prevent empty HASS auto discovery topics if memory allocation fails (onBattery) 2024-01-07 10:31:06 +01:00
helgeerbe
d0e1da7b1d Prevent config corruption by checking whether memory allocation was successfull. (onBattery) 2024-01-07 10:30:12 +01:00
helgeerbe
d6d274f078 Prevent empty HASS auto discovery topics if memory allocation fails (onBattery) 2024-01-06 22:51:35 +01:00
helgeerbe
ec93004724 Better handling of out of memory situations in live data websocket (onBattery) 2024-01-06 22:42:34 +01:00
helgeerbe
210fce67ce Use auto keyword and references more often (onBattery) 2024-01-06 22:10:07 +01:00
Fribur
92de3e9f87 fixed a bug where under one condition DNS was not tried for resolving host IP 2024-01-05 20:54:53 -05:00
helgeerbe
67e2134f7e add webapp 2024-01-05 23:03:09 +01:00
helgeerbe
d5155a07be Remove F macro from onBattery extensions 2024-01-05 23:02:26 +01:00
Thomas Basler
bfcce16bc9 webapp: update dependencies 2024-01-05 22:04:54 +01:00
Thomas Basler
6573c51052 Use auto keyword and references more often 2024-01-05 22:04:54 +01:00
Thomas Basler
24c8a40fad Use Utils::checkJsonAlloc in ConfigurationClass 2024-01-05 21:56:34 +01:00
Thomas Basler
f968179b60 Better handling of out of memory situations in live data websocket 2024-01-05 21:56:34 +01:00
Thomas Basler
f00cd1bd61 Prevent empty HASS auto discovery topics if memory allocation fails 2024-01-05 21:55:15 +01:00
Thomas Basler
88d75673fc Prevent config corruption by checking whether memory allocation was successfull. 2024-01-05 21:55:15 +01:00
Thomas Basler
a65f1e48a5 Fix: Access Point not working after firmware update
Fixes #1613
2024-01-05 21:55:15 +01:00
Thomas Basler
2a15677923 Fix: Optimize network connection handling
This should provide a more reliable connection to several AP types. See #576
2024-01-05 21:55:15 +01:00
Bernhard Kirchen
2ed66eb992 Fix: define _TASK_THREAD_SAFE for TaskScheduler
the TaskScheduler runs in the context of a FreeRTOS thread/task. there
are other such threads (MQTT client and web server in particular). to
allow changing TaskScheduler task properties from different threads, we
need to enable the use of a mutex to protect the TaskScheduler.
2024-01-05 21:55:15 +01:00
Bernhard Kirchen
377406f10c
Feature: add heap details to system info and prometheus (#595)
this change adds the values of ESP.gteMaxAllocHeap() and
ESP.getMinFreeHead() to the prometheus metrics and the system
information object. the web UI uses these values to diplay the size of
the largest free contiguous block, calculate a rough estimate for the
level of fragmentation, and the maximum usage of heap memory since boot
in absolute and relative amounts.
2024-01-05 21:46:31 +01:00
Fribur
e09ffcbb53 shorter parameter names 2024-01-05 14:39:32 -05:00
Fribur
85d0f2a8fb HttpPowerMeterClass: change order of resolving hostname
OpenDTU console gets spammed with "WifiGeneric::hostByName() error when first trying to resolve the hostname via DNS. So reverse order: first try mDNS, if that fails try DNS. Also ensure that https bool is passed correctly to HTTPClient::begin(). Lastly, concatenate strings for building Digest authorization using "+" and not via snprintf.
2024-01-05 14:36:19 -05:00
Fribur
9ed5a78818 Reverted changes to PowerLimiter, adapted DNS and mDNS handling in HttpPowerMeter
For non IP address URLs, HttpPowerMeter now first tries DNS for resolution as done in WifiClient::connect, and only if that fails tries mDNS. For the latter to work mDNS needs to be enabled in settings. Log in console if mDNS is disabled. String building for Digest authorization still tries to avoid "+" for reasons outlined in https://cpp4arduino.com/2020/02/07/how-to-format-strings-without-the-string-class.html This should also be saver than just concatenating user input strings in preventing format string attacks. https://owasp.org/www-community/attacks/Format_string_attack
2024-01-05 10:13:16 -05:00
Fribur
bc38ce344f remove FirebaseJson from platfromio.ini, fix unintended change in PowerLimiter 2024-01-04 18:22:58 -05:00
Bernhard Kirchen
3c8b8d4427
use frozen::string and frozen::map where reasonable (#593)
this change utilizes some of the features from library "frozen", which
was included upstream for the grid profile parser. to improve code
maintainability, a couple of std::maps mapping strings to values or the
other way around were introduced in OpenDTU-OnBattery-specific code at
the expense of some flash and computing overhead.

library "frozen" offers constexpr versions of map and string, which
saves initialization code and offers slightly faster lookups. this
brings the binary size down by ~25kB and should provide a small
performance improvement at runtime.
2024-01-04 23:28:34 +01:00
Fribur
d5eba2392c fixed long/float parsing bug 2024-01-04 16:32:42 -05:00
Fribur
f5c69060f5 re-factoring of HttpPowerMeter
Added ability to deal with local host names (mDNS), remove use of FirebasedJson to save ~20kB build size, some changes to PowerLimiter to avoid setting new inverter power limits when not needed (=current limit as reported by inverter is within hysteresis)
2024-01-04 16:20:32 -05:00
helgeerbe
aa5a762d2a Merge branch 'development' 2024-01-04 15:47:46 +01:00
helgeerbe
e9def28f3e add webapp 2024-01-04 15:43:08 +01:00
Bernhard Kirchen
e7a005839b
Feature: implement MQTT-driven battery provider (#589)
this battery provider implementation subscribes to a user-configurable
MQTT topic to retrieve the battery SoC value. the value is not
re-published under a different topic. there is no card created in the
web app's live view, since the SoC is already part of the totals at the
top of the live view. that is the only info this battery provider
implements.

closes #293.
relates to #581.
2024-01-04 15:42:10 +01:00
helgeerbe
65319ed07e log if memory allocation for live view fails
Explained in #591
2024-01-04 15:38:28 +01:00
Bernhard Kirchen
4548fc2a00
remove description of DPL from README (#588)
the DPL is described in more detail in the Wiki, which is the canonical
source of information.
2024-01-04 15:17:43 +01:00
helgeerbe
7946dfc0c2 add webapp 2024-01-03 13:10:50 +01:00
helgeerbe
2c1e145575 Merge branch 'development' of https://github.com/helgeerbe/OpenDTU into development 2024-01-01 17:20:57 +01:00
helgeerbe
bbf0003d1c Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2024-01-01 17:20:27 +01:00
MalteSchm
5bd3ce5a8f
Reducing lower limit for AC-charger (#574) 2024-01-01 17:06:58 +01:00
helgeerbe
cbf7680836 merge v23.12.31 2024-01-01 14:50:51 +01:00
Bernhard Kirchen
8f5c4878c5
Fix: switch context when processing DPL MQTT requests (#572)
MQTT message callbacks are executed in the MQTT thread context. when
processing topics that control the DPL, we must avoid executing methods
that are not thread-safe. this change binds the methods to be called to
the respective parameters and executes them in the TaskScheduler
context, such that they no longer need to be thread-safe.
2023-12-31 14:49:39 +01:00
helgeerbe
ef1aec3b26
Merge pull request #571 from schlimmchen/switch-context-on-huawei-mqtt-message
Fix: switch context when handling AC charger MQTT messages
2023-12-31 14:49:00 +01:00
Bernhard Kirchen
463226082f clean up Huawei MQTT handler
* bind the callback to a topic (enum value) such that there is no need
  to tokenize the full topic (string) to find out what value is being
  processed. tokenizing is expensive.
* get rid of using the config in the callback, which improves
  thread-safety since the MQTT callback is running in the MQTT thread.
* prefer C++ method stof to convert MQTT value to a float, which saves
  us from using new and delete for a buffer in particular.
* prefer switch statements over if-else-trees.
* split long lines.
* get rid of topic #defines.
* fix indention.
2023-12-30 18:41:57 +01:00
Bernhard Kirchen
fe2f82e303 Fix: switch context when handling AC charger MQTT messages
MQTT message callbacks are executed in the MQTT thread context. when
processing topics that control the huawei AC charger, we must avoid
executing methods that are not thread-safe. this change bound the
methods to be called to the respective parameters and executes them
in the TaskScheduler context, such that they no longer need to be
thread-safe.
2023-12-30 18:08:06 +01:00
helgeerbe
08bc181a5e add webapp 2023-12-30 16:50:02 +01:00
Bernhard Kirchen
7928f2f8cf
Feature: JK BMS: add more values to live view (#552)
there are more interesting values available to display in the live view.
however, adding them made the list of values very long. this can be
mitigated by using a new column/card, which uses the available screen
space nicely on bigger screens.
2023-12-30 16:46:44 +01:00
Bernhard Kirchen
bb34fa74fd
Fix: use FormFooter in OnBattery-specific forms (#569)
the upstream project introduced a new Vue component "FormFooter", which
is used to end an input form, namely all settings forms. we should not
only use this component as well, but the save button on our forms
actually broke since the text dtuadmin.Save is replaced by base.Save.

also replace the use of dtuadmin.Seconds with base.Seconds, such that an
upstream change to dtuadmin.Seconds will not break the battery admin an
AC charger views.
2023-12-30 16:46:16 +01:00
Bernhard Kirchen
c7098b6c42
Fix: thread-safety and dynamic memory for MessageOutput (#567)
this commit re-introduces the changes from #418, which were effectively
reverted with d49481097 (merge commit introducing TaskScheduler).

these adjustments are important to guarantee unmangled log messages and
more importantly, to guarantee that all messages from a particular
component are printed to the web console, which most people use to copy
messages from when reporting issues.

* use dynamic memory to allow handling of arbitrary message lenghts.
* keep a message buffer for every task so no task ever mangles the
  message of another task.
* every complete line is written to the serial console and moved to
  a line buffer for sending them through the websocket.
* the websocket is always fed complete lines.
* make sure to feed only as many lines as possible to the websocket
  handler, so that no lines are dropped.
* lock all MessageOutput state against concurrent access.
* respect HardwareSerial buffer size: the MessageOutput class buffers
  whole lines of output printed by any task in order to avoid mangling
  of text. that means we hand over full lines to the HardwareSerial
  instance, which might be too much in one call to write(buffer, size).
  we now check the return value of write(buffer, size) and call the
  function again with the part of the message that could not yet be
  written by HardwareSerial.
2023-12-30 16:45:56 +01:00
helgeerbe
f89f9da67a Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-12-29 10:54:41 +01:00
Bernhard Kirchen
d769cdd30a
Fix: move battery's lock_guard to updateSettings() (#566)
the updateSettings method is called from the web server's context and
therefore accesses _upProvider in a different context than the
TaskScheduler. the lock_guard needs to protect _upProvider.
2023-12-29 10:48:25 +01:00
helgeerbe
3bf4d35db8 Merge branch 'development' 2023-12-27 13:37:09 +01:00
helgeerbe
6ab706c87d clean up merge conflicts 2023-12-27 13:16:37 +01:00
helgeerbe
367e0f9b6e Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-12-27 13:11:25 +01:00
helgeerbe
d494810975
merge V23.12.16 (#556)
* Optimize Sun data calculation

* Remove not required enum

* Split config struct into different sub structs

* Feature: Allow configuration of LWT QoS

* Made resetreason methods static

* Feature: Implement offset cache for "YieldDay"

Thanks to @broth-itk for the idea!
Fix: #1258 #1397

* Add Esp32-Stick-PoE-A

* remove broken LilyGO_T_ETH_POE config, use device profile instead

* Feature: High resolution Icon and PWA (Progressive Web App) functionality

Fix: #1289

* webapp: Update dependencies

* Initialize TaskScheduler

* Migrate SunPosition to TaskScheduler

* Migrate Datastore to TaskScheduler

* Migrate MqttHandleInverterTotal to TaskSchedule

* Migrate MqttHandleHass to TaskScheduler

* Migrate MqttHandleDtu to TaskScheduler

* Migrate MqttHandleInverter to TaskScheduler

* Migrate LedSingle to TaskScheduler

* Migrate NetworkSettings to TaskScheduler

* Migrate InverterSettings to TaskScheduler

* Migrate MessageOutput to TaskScheduler

* Migrate Display_Graphic to TaskScheduler

* Migrate WebApi to TaskScheduler

* Split InverterSettings into multiple tasks

* Calculate SunPosition only every 5 seconds

* Split LedSingle into multiple tasks

* Upgrade espMqttClient from 1.4.5 to 1.5.0

* Doc: Correct amount of MPP-Tracker

* Added HMT-1600-4T and HMT-1800-4T to DevInfoParser

Fix #1524

* Adjusted inverter names for HMS-1600/1800/2000-4T

* Add channel count to description of detected inverter type (DevInfoParser)

* Adjust device web api endpoint for dynamic led count

* Feature: Added ability to change the brightness of the LEDs

Based on the idea of @moritzlerch with several modifications like pwmTable and structure

* webapp: Update dependencies

* Update olikraus/U8g2 from 2.35.7 to 2.35.8

* Remove not required onWebsocketEvent

* Remove code nesting

* Introduce several const statements

* Remove not required AsyncEventSource

* Doc: Added byte specification to each command

* Feature: Added basic Grid Profile parser which shows the used profile and version

Other values are still outstanding.

* Optimize AlarmLogParser to save memory

* Add libfrozen to project to create constexpr maps

* Feature: First version of GridProfile Parser which shows all values contained in the profile.

* webapp: Update dependencies

* Apply better variable names

* Remove not required casts

* Add additional compiler flags to prevent errors

* Add const statement to several variables

* Replace NULL by nullptr

* Update bblanchon/ArduinoJson from 6.21.3 to 6.21.4

* Add const keyword to method parameters

* Add const keyword to methods

* Use references instead of pointers whenver possible

* Adjust member variable names in MqttSettings

* Adjust member variable names in NetworkSettings

* webapp: Update timezone database to latest version

* webapp: Beautify and unify form footers

* Feature: Allow setting of an inverter limit of 0% and 0W

Thanks to @madmartin in #1270

* Feature: Allow links in device profiles

These links will be shown on the hardware settings page.

* Doc: Added hint regarding HMS-xxxx-xT-NA inverters

* Feature: Added DeviceProfile for CASmo-DTU

Based on #1565

* Upgrade actions/upload-artifact from v3 to v4

* Upgrade actions/download-artifact from v3 to v4

* webapp: add app.js.gz

* Gridprofileparser: Added latest known values

Thanks to @stefan123t and @noone2k

* webapp: Fix lint errors

* Feature: Add DTU to Home Assistant Auto Discovery

This is based on PR 1365 from @CFenner with several fixes and optimizations

* Fix: Remove debug output as it floods the console

* Fix: Gridprofileparser: Add additional error handling if profile is unknown

* webapp: add app.js.gz

* Fix: Offset cache for "YieldDay" did not work correctly

* webapp: update dependencies

* webapp: add app.js.gz

* Fix: yarn.lock was outdated

* Fix: yarn build error

* Fix: Reset Yield day correction in combination with Zero Yield Day on Midnight lead to wrong values.

* Fix: Allow negative values in GridProfileParser

* Correct variable name

* Fix #1579: Static IP in Ethernet mode did not work correctly

* Feature: Added diagram to display

This is based on the idea of @Henrik-Ingenieur and was discussed in #1504

* webapp: update dependencies

* webapp: add app.js.gz

---------

Co-authored-by: Thomas Basler <thomas@familie-basler.net>
Co-authored-by: Pierre Kancir <pierre.kancir.emn@gmail.com>
2023-12-27 11:49:57 +01:00
helgeerbe
9ff39fde26 Merge branch 'development' 2023-12-15 12:00:57 +01:00
helgeerbe
fb2ca28692 add webapp 2023-12-15 11:03:48 +01:00
Bernhard Kirchen
6e78c5bd1c
Feature: JK BMS: export (more) data to live view and MQTT (#549)
* add more values to web app live view. this should add all interesting
  values for the web app live view. those include important values and
  values that change frequently.

* add more interesting JK BMS dummy messages: one has 0% SoC and an
  alarm (discharge undervoltage) set. the other has the undertemperature
  alarm set.

* add alarms and warnings to live view

* publish alarm and status bits through MQTT individually

* publish cell voltages to MQTT broker

* remove trailing spaces in BatteryStats class
2023-12-15 10:59:07 +01:00
helgeerbe
0c829a5947 Merge branch 'development' 2023-11-17 09:31:24 +01:00
helgeerbe
dd8446df0a Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-11-17 09:30:04 +01:00
helgeerbe
e44328bb13 Merge branch 'development' 2023-11-16 11:10:38 +01:00
helgeerbe
0085970567 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-11-16 09:20:08 +01:00
helgeerbe
a1d0dad128 Merge branch 'development' 2023-11-15 14:29:52 +01:00
helgeerbe
8cccce5875 add weapp 2023-11-15 14:26:54 +01:00
helgeerbe
6f7470feb4 fix: ModuleNotFoundError: No module named 'pkg_resources': 2023-11-15 14:07:27 +01:00
helgeerbe
28f46471bf set unique name for test_build action 2023-11-15 13:59:22 +01:00
helgeerbe
a004e94e57 create test_build action 2023-11-15 13:59:09 +01:00
helgeerbe
6e336f1117 set unique name for test_build action 2023-11-15 13:54:42 +01:00
helgeerbe
7637dc591b create test_build action 2023-11-15 13:52:35 +01:00
MalteSchm
3dceddfe49
Split Huawei setValue in private/public implementation. Prevent setting values when internal power control mode is enabled (#521) 2023-11-15 12:38:35 +01:00
helgeerbe
d4d42167ec Merge branch 'pr/philippsandhaus/510' into development 2023-11-15 12:33:50 +01:00
Al3x Zamponi
3af0437857
Update MqttHandlVedirectHass.cpp
Fix HASS unit
2023-11-13 17:12:19 +01:00
helgeerbe
eb578f08c5 Merge branch 'development' 2023-10-24 16:40:47 +02:00
helgeerbe
49a10305e2 fix: liveData is not updated
- due to roll back of upstream commit, duplicated code allocated json buffer twice
- enlarge json buffer to 4200
2023-10-24 15:25:31 +02:00
helgeerbe
9ca3655de6 add webapp 2023-10-23 13:32:12 +02:00
Bernhard Kirchen
254b95cb0f
Fix: replace links to upstream project where applicable (#514) 2023-10-23 13:24:05 +02:00
Bernhard Kirchen
0fa2745ace
Fix: VE.Direct refactor issues from #505 (#516)
* VE.Direct: return non-nullptr as a fallback

the changed return statement was supposed to return a shared_ptr to a
new and valid MPPT data struct as a fallback. however, it did return a
new shared_ptr that was initialized to nullptr.

* VE.Direct: make liveview total use total MPPT values

this change makes the call to VictronMppt.getData() obsolete, which in
turn will therefore not cause an error message on the console if
VE.Direct (MPPT) is not enabled. this change also takes care that once
multiple VE.Direct MPPT charge controllers are supported, the sums of
the respective total values are used in the web app totals.
2023-10-23 13:23:06 +02:00
Philipp Sandhaus
8ba9048f99
Show battery temperature when sensor is present (#511) 2023-10-23 13:22:10 +02:00
Al3x Zamponi
c5427dedce
Add calculated values to hass auto discovery (#509)
Add battery P and E as well as panel I to auto discovery values of HA
2023-10-23 13:18:24 +02:00
Philipp Sandhaus
b159c31258 Display efficiency measuremnt for Huawei AC charger in % in web interface 2023-10-20 22:14:45 +02:00
helgeerbe
b833d5ab52 add webapp 2023-10-19 16:24:19 +02:00
Bernhard Kirchen
4d4aadf8de
Feature: AC charger: configurable CAN controller frequency (#500) 2023-10-19 16:16:01 +02:00
Bernhard Kirchen
d23b991f5c
VE.Direct: Fix design issues and prepare support for multiple instances (#505)
* introduce VictronMpptClass

this solves a design issue where the loop() method of a static instance
of VeDirectMpptController, which is part of library code, is called as
part of the main loop() implementation. that is a problem because the
call to this loop() must be handled differently from all other calls:
the lib does not know whether or not the feature is enabled at all.
also, the instance would not be initialized when enabling the feature
during normal operation. that would even lead to a nullptr exception
since the pointer to the serial implementation is still uninitialized.

this new intermediate class is implemented with the support for multiple
Victron charge controllers in mind. adding support for more charge
controllers should be more viable than ever.

fixes #481.

related to #397 #129.

* VE.Direct: move get.*AsString methods to respective structs

those structs, which hold the data to be translated into strings, know
best how to translate them. this change also simplifies access to those
translation, as no parameter must be handed to the respective methods:
they now act upon the data of the instance they are called for. adds
constness to those methods.

* VE.Direct: simplify and clean up get.*AsString methods

use a map, which is much easier to maintain and which reads much easier.
move the strings to flash memory to save RAM.

* DPL: use VictronMpptClass::getPowerOutputWatts method

remove redundant calculation of output power from DPL. consider
separation of concern: VictronMpptClass will provide the total solar
output power. the DPL shall not concern itself about how that value is
calculated and it certainly should be unaware about how many MPPT charge
controllers there actually are.

* VE.Direct: avoid shadowing struct member "P"

P was part of the base struct for both MPPT and SmartShunt controller.
however, P was also part of the SmartShunt controller data struct,
shadowing the member in the base struct.

since P has slightly different meaning in MPPT versus SmartShunt, and
since P is calculated for MPPT controllers but read from SmartShunts, P
now lives in both derived structs, but not in the base struct.

* VE.Direct: isDataValid(): avoid copying data structs

pass a const reference to the base class implementation of isDataValid()
rather than a copy of the whole struct.

* VE.Direct: unify logging of text events

* VE.Direct: stop processing text event if handled by base

in case the base class processed a text event, do not try to match it
against values that are only valid in the derived class -- none will
match.

* VE.Direct MPPT: manage data in a shared_ptr

instead of handing out a reference to a struct which is part of a class
instance that may disappear, e.g., on a config change, we now manage the
lifetime of said data structure using a shared_ptr and hand out copies
of that shared_ptr. this makes sure that users have a valid copy of the
data as long as they hold the shared_ptr.

* VE.Direct MPPT: implement getDataAgeMillis()

this works even if millis() wraps around.

* VE.Direct: process frame end event only for valid frames

save a parameters, save a level of indention, save a function call for
invalid frames.
2023-10-19 16:15:29 +02:00
Bernhard Kirchen
917242909b
Fix: PowerMeter: update _lastPowerMeterUpdate for SOURCE_SML (#506)
closes #498.
2023-10-19 16:14:36 +02:00
helgeerbe
5bbbe6bfcb Merge branch 'development' 2023-10-10 09:27:21 +02:00
helgeerbe
b461aff622 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-10-10 09:26:43 +02:00
Bernhard Kirchen
116da05114
DPL: (re-)send power limits periodically (#483)
avoid staleness in case the same power limit is calculated over and over
again, hence no new power limit value is calculated and hence no power
limit command is sent to the inverter. staleness occurs in this case if
the first power limit command to establish the respective limit was not
received by the inverter. one can easily simulate a situation where the
same power limit is caluclated over and over again: with a battery above
the start threshold, set a very low upper power limit for the inverter
(DPL setting). that value will be used as the limit as long as the power
meter reading is larger than that.

we could also check the limit reported by the inverter. however, that
value is in percent of the inverter's max AC output, and is often not
the same value as we requested as the limit, but slightly off. we then
would have to decide how much deviation is okay, which is unreasonably
complicated.

closes #478.
2023-10-09 09:51:40 +02:00
helgeerbe
4324ae3081 Merge branch 'development' 2023-10-04 14:10:19 +02:00
Bernhard Kirchen
f7abbdbe06
Publish calculated MPPT metrics (#475)
* VE.Direct MPPT MQTT: remove trailing whitespace

* VE.Direct MPPT MQTT: publish P, IPV and E to MQTT

those values are calculated by OpenDTU-OnBatery and are part of the web
application live view, but were previously not published through MQTT.

closes #376.
2023-10-04 13:26:22 +02:00
helgeerbe
7fb26e1e81
Channel for AC must always be CH0 (#472) 2023-10-02 11:44:00 +02:00
MalteSchm
b1164d6c69
Move Huawei CAN bus communication to separate thread (#454) 2023-10-02 11:22:10 +02:00
helgeerbe
f0a55ea32b Merge branch 'development' 2023-09-28 13:17:59 +02:00
Philipp Sandhaus
4ee49a6ecb
Removed checking for valid tx pin for Victron (#455)
Please check wiki documentation, and change/extend it accordingly.
2023-09-28 13:13:32 +02:00
helgeerbe
933345d659 remove manual builds from action 2023-09-22 18:05:49 +02:00
helgeerbe
e937fd1cb8 add webapp 2023-09-22 17:27:17 +02:00
Philipp Sandhaus
7142921021
Integration of Victron SmartShunt via VE.Direct (#452)
* Move Mppt logic to subclass

* Added Definitions for Shunts and restructering

* First integration of SmartShunt data into Web Interface

* Code cleanup

* VE.Direct: whitespace cleanup

* VE.Direct: manage HardwareSerial in unique_ptr

* VE.Direct: _efficiency is only needed by MPPT

* VE.Direct: keep as many members private as possible

* VE.Direct: use int8_t for pins (as before)

* VictronSmartShunt: _verboseLogging is not used

* VE.Direct: OR (off reason) is MPPT specific

it also applies to Phoenix inverters and Smart BuckBoost, but since
there is no support for those, the code is moved to the MPPT controller.

* Added Shunt alarms to liveview
Changed from double to int for several readings

* Update build.yml to allow manual builds

---------

Co-authored-by: Philipp Sandhaus <philipp.sandhaus@cewe.de>
Co-authored-by: Bernhard Kirchen <schlimmchen@posteo.net>
2023-09-22 17:24:57 +02:00
helgeerbe
160d3f23bd fix: #450 power meter request fails when username or password contains @
user name and password is now encoded in the authorization header
2023-09-19 14:16:29 +02:00
helgeerbe
ae392329ea Merge branch 'development' 2023-09-19 13:06:46 +02:00
helgeerbe
b081845b95 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-09-19 12:28:00 +02:00
helgeerbe
ed2a189a61 Merge branch 'helgeerbe/issue438' into development 2023-09-18 17:56:58 +02:00
helgeerbe
f2893220a5 fix: #438 2023-09-18 17:56:22 +02:00
helgeerbe
4d0f958943 Merge branch 'development' 2023-09-18 10:48:16 +02:00
helgeerbe
9d6b459dc6 merge of v23.9.13 2023-09-18 10:46:43 +02:00
Bernhard Kirchen
b501d25ab6
JK BMS: fix SoC last update timestamp (#439)
for the MQTT integration, JkBms::DataPointContainer::updateFrom() was
changed such that the data points timestamp reflects the last change of
the data point value. that timestamp was used to set the SoC last update
timestamp. however, that timestampt must reflect the last time the SoC
was successfully received from the JK BMS so we could make sure the
value was up to date.
2023-09-18 10:30:03 +02:00
Bernhard Kirchen
954a98dbc8 JK BMS: Support for MQTT (#432)
* JK BMS: avoid trailing whitespace in debug output

* JK BMS: publish data points through MQTT

* JK BMS: updateFrom: skip data points with equal value

this changes the interpretation of the timestamp in data containers that
are merely updated from other data containers: this is the oldest
timestamp known where the value was as recorded by the data point in its
respective container.

the data container constructed from an answer will -- naturally -- have
the timetamps of its data points set to the time they were constructed.

* JK BMS: only publish changed values to MQTT broker

all values are still published once every minute if the MQTT retain flag
is NOT set. otherwise, the constant values are only published once on
startup.
2023-09-15 10:06:30 +02:00
helgeerbe
2f9539e4b3 print out debug messages 2023-09-15 09:57:25 +02:00
helgeerbe
f7bd4a40d8 revert Revert "Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development"
merge of v23.9.11 broke the system. As a workaround upgrade espressif32 from 6.3.2 to 6.4.0 is skipped. See #440
2023-09-14 13:45:23 +02:00
helgeerbe
4e489febfe Merge branch 'development' 2023-09-13 12:17:16 +02:00
Bernhard Kirchen
24018a1432
JK BMS: Support for MQTT (#432)
* JK BMS: avoid trailing whitespace in debug output

* JK BMS: publish data points through MQTT

* JK BMS: updateFrom: skip data points with equal value

this changes the interpretation of the timestamp in data containers that
are merely updated from other data containers: this is the oldest
timestamp known where the value was as recorded by the data point in its
respective container.

the data container constructed from an answer will -- naturally -- have
the timetamps of its data points set to the time they were constructed.

* JK BMS: only publish changed values to MQTT broker

all values are still published once every minute if the MQTT retain flag
is NOT set. otherwise, the constant values are only published once on
startup.
2023-09-13 12:14:29 +02:00
Bernhard Kirchen
88a5117007
VE.Direct: do not use debug buffer at all if verbose logging disabled (#437)
this avoids the debug buffer being overrun if verbose logging is
disabled in particular. that would happen because the buffer would
only be reset if verbose logging was enabled but filled in any case.
2023-09-13 12:13:56 +02:00
helgeerbe
2eeb5f1d19 Revert "Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development"
This reverts commit eb1c2dbd8c, reversing
changes made to 0cb42a6424.

merge of v23.9.11 broke the system
2023-09-12 20:18:10 +02:00
helgeerbe
a7ea15cde9 add missing Arduino.h 2023-09-12 19:43:30 +02:00
helgeerbe
eb1c2dbd8c Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-09-12 19:27:32 +02:00
MalteSchm
0cb42a6424
Reworked wifiClient handling in Power Meter httpRequest and smaller update to Power Meter updateValue method (#430) 2023-09-12 19:14:55 +02:00
Bernhard Kirchen
d984912d7c
Vedirect Cleanups (#417)
* VE.Direct: remove unused #defines

* VE.Direct: remove unused mStop member variable

* VE.Direct: whitespace cleanups

remove trailing whitespace and fix mixed indentation.
2023-09-12 19:13:28 +02:00
helgeerbe
8dd96c450b Merge branch 'development' 2023-09-04 14:26:22 +02:00
helgeerbe
3df47d1fee add webapp 2023-09-04 14:15:41 +02:00
Bernhard Kirchen
68783b450f
Messages: thread-safety and dynamic memory (#418)
* thread-safety and dynamic memory for MessageOutput

* use dynamic memory to allow handling of arbitrary message lenghts.
* keep a message buffer for every task so no task ever mangles the
  message of another task.
* every complete line is written to the serial console and moved to
  a line buffer for sending them through the websocket.
* the websocket is always fed complete lines.
* make sure to feed only as many lines as possible to the websocket
  handler, so that no lines are dropped.
* lock all MessageOutput state against concurrent access.

* MessageOutput: respect HardwareSerial buffer size

the MessageOutput class buffers whole lines of output printed by any
task in order to avoid mangling of text. that means we hand over full
lines to the HardwareSerial instance, which might be too much in one
call to write(buffer, size). we now check the return value of
write(buffer, size) and call the function again with the part of the
message that could not yet be written by HardwareSerial.
2023-09-04 14:08:30 +02:00
Bernhard Kirchen
ba303da742
VE.Direct: Reset state machine on timeout, fix and extend logging (#416)
* VE.Direct: reset state machine on timeout

there will never be a large gap between two bytes of the same frame.
if such a large gap is observed, reset the state machine so it tries
to decode a new frame once more data arrives.

this is helpful in case of corrupted data that prevents the state
machine of transitioning to the final state even though the VE.Direct
data producer is done sending bytes that belong to the same frame.

* VE.Direct: print problems to MessageOutput

this includes the web console in particular, where many users have
access to while the serial console is not attached or monitored.

* VE.Direct: collect serial input into buffer and print

should help debug issues for users.

* VE.Direct: implement and use verbose logging switch
2023-09-04 14:07:48 +02:00
Martin
a7a38e74a1
Feature: add nice Icons for HA autoconfiguration (#413)
* add Icons for Battery and Victron device sensors in Home Assistant
  overriding the boring default icon for many sensors

Signed-off-by: Martin Dummer <martin.dummer@gmx.net>
2023-09-04 14:07:24 +02:00
helgeerbe
8c36532cea add webapp 2023-08-31 16:23:06 +02:00
Bernhard Kirchen
f744629b0b
Support for Jikong JK BMS using serial connection (#319) 2023-08-31 16:21:32 +02:00
helgeerbe
2ba7ea2744 add filter for build action
- run build action only on master and development branch
- ignore v* tags from tbnobody
2023-08-30 09:54:30 +02:00
helgeerbe
d5308b1029 Merge branch 'development' 2023-08-29 10:16:33 +02:00
Bernhard Kirchen
929b477275
vite config: also proxy vedirect- and batterylivedata (#408) 2023-08-29 09:27:22 +02:00
helgeerbe
d4afc5940a Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-08-29 09:24:25 +02:00
Martin
88744bfa38
Fixes for HA autoconfig issues 378 379 (#394)
* fix: homeassistant autodiscovery topics

homeassistant autodiscovery topics contain not allowed characters, which
are now fixed.
Sidenote: when autodiscovery messages were retained, the badly formatted messages
must be removed from the mqtt server manually. Otherwise the error
messages in homeassistant will persist.

Closes: https://github.com/helgeerbe/OpenDTU-OnBattery/issues/378
Signed-off-by: Martin Dummer <martin.dummer@gmx.net>

* Fix: some homeassistant autoconf messages are wrong

Misc fixes for HA autoconfiguration:
* Entity sensor.panel_yield_today now uses state class "total"
* Entity sensor.panel_yield_yesterday now uses state class "total"
* Entity sensor.mppt_day_sequence_number_0364 now shows correct value
* sensor.panel_power duplicate removed

Closes: https://github.com/helgeerbe/OpenDTU-OnBattery/issues/379
Signed-off-by: Martin Dummer <martin.dummer@gmx.net>

---------

Signed-off-by: Martin Dummer <martin.dummer@gmx.net>
2023-08-28 13:21:20 +02:00
Bernhard Kirchen
96ee78156d
Fix DPL Mode 2 MQTT Status (#402)
* DPL MQTT handler: modernize

* there is no need to tokenize and check the topic of a received MQTT
  message if we only subscribe to a single topic. all messages will be
  for that topic. avoid testing the topic in the callback alltogether.
* use std::string and std::stoi over allocating and deleting a buffer
  and copying charactes around.
* use a switch statement to process the actual payload.
* break a long line.

* DPL: fix getMode() return value

getMode() returned a bool. probably its return type was not adjusted
when the third mode was introduced. this lead to mode 2 being cast to
true implicitly, which in turn was used to construct a String, such that
"1" was published as the DPL mode when in fact it was 2.

make the mode an enum class to avoid such problems in the future.

inline getMode() and setMode().

fix indention.
2023-08-28 13:20:56 +02:00
helgeerbe
ca308d0895 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-08-28 13:12:07 +02:00
helgeerbe
84647d80e2 dummy commit to check system info 2023-08-10 16:40:17 +02:00
helgeerbe
026bca9fe3 Merge branch 'development' 2023-08-10 14:49:33 +02:00
helgeerbe
be2846a07a on error assume branch is master 2023-08-10 14:48:52 +02:00
helgeerbe
487304125c Merge branch 'development' 2023-08-10 13:46:53 +02:00
Bernhard Kirchen
7c7a15e016
VE.Direct: websocket and status API response size (#371)
the size allocated for the HTTP request response was too little, while
the size for the buffer of the websocket output was increased already.

add a new member variable and use it in both context, such that
increasing the buffer size to accomodate more space (for the JSON data
in particular) will benefit both contexts in the future.
2023-08-10 13:40:09 +02:00
Bernhard Kirchen
b7214161b8
live view: remove disabled -OnBattery-specifc views from DOM (#372)
instead of hiding views, we can also avoid adding them to the DOM. this
has a couple of advantages:

* no HTTP request for data is sent and no websocket connection is
  established for disabled features.
* JavaScript that causes errors due to incomplete or incompatible data
  of features that are disabled anyways do not trigger the browser
  debugger.
2023-08-10 13:39:21 +02:00
helgeerbe
1100f10c99 fix use dulwich befor installation 2023-08-10 13:27:45 +02:00
helgeerbe
8e4f234517 action build on each push 2023-08-10 13:21:35 +02:00
helgeerbe
6601e9a44b catch exception during git access 2023-08-10 13:17:39 +02:00
helgeerbe
801fe5d027
Update test_build.yml 2023-08-10 13:08:16 +02:00
helgeerbe
c8a561dbd7 fix linting errors 2023-08-10 13:03:51 +02:00
helgeerbe
cc3ab9b14b
Create test_build.yml 2023-08-10 13:02:42 +02:00
helgeerbe
3702fb3eef
Delete test_build.yml 2023-08-10 13:01:49 +02:00
helgeerbe
bbc1319090
Create test_build.yml 2023-08-10 12:58:52 +02:00
helgeerbe
9475a78211 catch error if git repo can't be accessd 2023-08-10 10:53:33 +02:00
helgeerbe
5a72d74982
fix #362 system Info checks git head, instead of build branch (#374) 2023-08-09 17:45:54 +02:00
helgeerbe
40c720aa57 add webapp 2023-08-09 16:01:03 +02:00
helgeerbe
c06299878b compare git hash to branch 2023-08-09 15:59:11 +02:00
helgeerbe
940d1a6145 fix label for branch info 2023-08-09 15:47:31 +02:00
helgeerbe
401a3b86a8 show branch in system info 2023-08-09 15:32:08 +02:00
helgeerbe
21bbed9b8e Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-08-09 12:37:33 +02:00
helgeerbe
ec67fe1ff7 fix: Power meter value not displayed #355
Enlarge JsonBuffer for webservice response to 2048
2023-08-08 15:58:24 +02:00
Bernhard Kirchen
3db237c109
DPL: save verbose logging switch value to config (#363) 2023-08-07 18:20:45 +02:00
helgeerbe
c950eb7245 Merge branch 'development' of https://github.com/helgeerbe/OpenDTU into development 2023-08-06 17:02:53 +02:00
helgeerbe
3cb30b14cd add webapp 2023-08-06 17:02:44 +02:00
Bernhard Kirchen
e6eaa001e7
DPL: use testThreshold() in useFullSolarPassthrough() (#357) 2023-08-06 17:00:27 +02:00
Bernhard Kirchen
aff7924411
Inhibit solar passthrough while battery below stop threshold (#354)
* DPL: improve verbose logging

* shorten DPL log prefix
* canUseDirectSolarPower() was printed two times
* _batteryDischargeEnabled was printed two times
* convert boolean values to human-readable strings
* add units where possible
* split messages into block "before calculating new limit" and "after
  calculating new limit", as the latter cannot rely on _inverter being
  available.
* order messages such that variables whose value is derived from other
  variables are printed later than their dependencies.
* merge output into blocks (one instance near "Printout some stats")
* remove more redundant info (produced in functions outside loop())
* print target grid consumption

* DPL: inhibit solar passthrough while stop threshold reached

* DPL: implement and use isBelowStopThreshold()

we only want to inhibit solar passthrough if the SoC is *below* the stop
threshold, not if it is equal to the stop threshold. otherwise, when
discharging, we would discharge until the battery reached the stop
threshold, then we would also inhibit solar passthrough, until the
battery is charged to the SoC stop threshold plus one percent.
2023-08-04 12:35:37 +02:00
helgeerbe
5335ec1bde Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-08-04 12:21:16 +02:00
helgeerbe
69456affce Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-08-02 19:46:35 +02:00
Bernhard Kirchen
81864b3420
execute MQTT client synchronously in main loop() (#350)
processing a published valued on a subscribed topic is currently running
in a task that is not the task executing the main loop(). that's because
the espMqttClient(Secure) was constructed without arguments, which
selects the constructor with two arguments priority and core, both of
which have default values. that constructor selects
espMqttClientTypes::UseInternalTask::YES, causing a task to be created
in which context the MQTT client loop is executed.

MQTT subscribers assume they are running in the same context as the main
loop(). most code assumes exactly that. as the scheduler is preemptive
and very little (none at all?) code is interlocked, we have to make sure
to meet the programmer's expectations.

this changeset calls the MQTT client loop in the context of the main
loop() and enforces the use of espMqttClientTypes::UseInternalTask::NO.
2023-08-01 09:20:04 +02:00
helgeerbe
587b2dc553 add webapp 2023-07-31 14:21:24 +02:00
Bernhard Kirchen
2bce8311a7
MQTT verbose logging fixes (#341)
* MQTT verbose logging: fix typo

this typo caused that verbose logging was always disabled for MQTT and
could not be enabled.

* webapp status: show MQTT verbose logging setting
2023-07-31 14:16:35 +02:00
Bernhard Kirchen
6b425d96b0
PowerMeter fixes (#342)
* PowerMeter: gracefully handle non-float MQTT values

* PowerMeter: update _lastPowerMeterUpdate conservatively

update the timestampt only if the topic actually matched any
subscription and if the value could be parsed as a float.

* PowerMeter: unsubscribe before subscribing

* PowerMeter: organize subscriptions in a map

this allows for a slightly more elegant code and reduced amount of code
overall.

* PowerMeter: clean up header

* move private methods to private section of class declaration.
* remove unused member variable.
2023-07-31 14:16:06 +02:00
Bernhard Kirchen
9bc334e368
reset docs/MQTT_Topics.md to upstream version (#343)
MQTT topics that are specific to OpenDTU-OnBattery shall only be
documented once. They are documented in the project's wiki already.
2023-07-31 14:14:32 +02:00
helgeerbe
43e836ac41 resolve merge conflict with upstream/master 2023-07-24 13:23:50 +02:00
helgeerbe
2440028d38 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-07-24 13:16:15 +02:00
MalteSchm
18c464e524
SoC based threshold detection fix (#320) 2023-07-24 13:03:12 +02:00
helgeerbe
8b01fa07cc Replace Readme with wiki home page 2023-07-24 13:02:12 +02:00
Phantomias2006
e06740fbb8
Add data age at battery MQTT (#322)
* Add data age at battery MQTT

* Update README.md

typo

* Update MQTT_Topics.md

typo

* Update PylontechCanReceiver.cpp

typo
2023-07-19 09:47:37 +02:00
helgeerbe
cea1f94b5e add webapp 2023-07-18 10:01:36 +02:00
Bernhard Kirchen
f0def2ae89
Selective verbosity (#318)
* DPL: implement verbose logging switch

* MQTT: implement verbose logging switch

* power meter: implement verbose logging switch

* Hoymiles lib: implement verbose logging switch

* cpp linting: "final" makes "virtual" and "override" redundant

... however, using only "final" is not as verbose.
2023-07-18 09:57:03 +02:00
Phantomias2006
a7da000345
Feature: set Huawei offline parameters via MQTT (#315)
* Add Huawei offline parameters via MQTT

* Update README.md

Correction of the docu

* Update MQTT_Topics.md

Correction of the docu
2023-07-18 09:52:31 +02:00
Martin
0dd1566dc6
Fix: WebApp Live-View: adjust window-title and header (#317)
adjust window-title and header from OpenDTU to OpenDTU-onBattery
Additionally change orientation of battery symbol to vertically centered
(looks nicer)

Signed-off-by: Martin Dummer <martin.dummer@gmx.net>
2023-07-18 09:51:59 +02:00
Martin
c393e52185
Feature: add Home Assistant MQTT discovery for Pylontech battery (#314)
When OpenDTU has a Pylontech CAN Bus Battery connected and enabled, this
patch adds the discovery routine for Home Assistant

Signed-off-by: Martin Dummer <martin.dummer@gmx.net>
2023-07-17 09:50:58 +02:00
Bernhard Kirchen
cbc99d715f
DPL: increase backoff while inverter is kept shut down (#310)
if the new calculated power limit is below the minimum power limit
setting, the inverter is shut down. the shutdown() function is called
every time this condition is detected, which is also true if the
inverter is kept shut down for longer. that happens while the battery
is charging in particular (solar passthrough off). there are other
cases.

in such cases we still want to get into the DPL status "stable". to be
able to determine this stable state, we must know if the call to
shutdown did actually initiate a shutdown or if the inverter is already
shut down.

we then can forward this "changed" or "not changed" info up the call
chain, where the loop() will know that the system is actually stable.
2023-07-17 09:40:18 +02:00
helgeerbe
f68f68be77 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-07-13 12:19:21 +02:00
helgeerbe
633ef88296 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-07-12 13:45:19 +02:00
Bernhard Kirchen
f3297930b5
DPL: account for solar passthrough losses (#307)
* fix another fixable "passtrough" typo

the typo in the config's identifier is not changed to preserve
compatibility while not spending the effort to migrate the setting.

* webapp language: prefer SoC over SOC

* DPL: implement solar passthrough loss factor

in (full) solar passthrough mode, the inverter output power is coupled
to the charge controler output power. the inverter efficiency is already
accounted for. however, the battery might still be slowly discharged for
two reasons: (1) line losses are not accounted for and (2) the inverter
outputs a little bit more than permitted by the power limit.

this is undesirable since the battery is significantly drained if solar
passthrough is active for a longer period of time. also, when using full
solar passthrough and a battery communication interface, the SoC will
slowly degrade to a value below the threshold value for full solar
passthrough. this makes the system switch from charging the battery
(potentially rapidly) to discharging the battery slowly. this switch
might happen in rather fast succession. that's effectively
trickle-charging the battery.

instead, this new factor helps to account for line losses between the
solar charge controller and the inverter, such that the battery is
actually not involved in solar passthrough. the value can be increased
until it is observed that the battery is not discharging when solar
passthrough is active.
2023-07-12 13:20:37 +02:00
MalteSchm
95d7ac7adf
Disable debug mode, increasing power threshold for active channel detection (#301) 2023-07-12 13:19:24 +02:00
helgeerbe
1d559c1c40 add webapp 2023-07-09 17:11:09 +02:00
MalteSchm
475aab1e9a
floating point input (#302) 2023-07-09 17:09:33 +02:00
Martin
cdf5c85510
fix: Home Assistant MQTT-Auto-Discovery with VE.Direct (#297)
In Home Assistant, when Home Assistant MQTT-Auto-Discovery is active,
almost all Sensors of the auto-discovered Victron device in Home
Assistant become "unavailable" after a short time - except those
Sensors with frequent changes like battery voltage or panel voltage.

This patch introduces regular mqtt updates for all VE.Direct sensors
when MQTT-Auto-Discovery is enabled.

Signed-off-by: Martin Dummer <martin.dummer@gmx.net>
2023-07-09 17:08:50 +02:00
Bernhard Kirchen
23ff4ef22a
DPL: do not use nullptr when printing debug messages (#303) 2023-07-09 16:42:50 +02:00
helgeerbe
2a858e096b fix: typo 2023-07-07 17:22:04 +02:00
helgeerbe
3fb062b5cc fix: typo 2023-07-07 17:08:51 +02:00
helgeerbe
ffa9be0835 fix: #294 Passthrough Spelling 2023-07-07 17:05:44 +02:00
helgeerbe
344498d440 fix: linting error 2023-07-04 14:55:55 +02:00
helgeerbe
d3adc65d11 add webapp 2023-07-04 12:07:50 +02:00
Bernhard Kirchen
107182f948
DPL: check more requirements and fix backoff initialization (#290)
* DPL: wait for valid time information

we know that the Hoymiles library refuses to send any message to any
inverter until the system has valid time information. until then we can
do nothing, not even shutdown the inverter.

* DPL: wait for device info to be ready

a calculated power limit will always be limited to the reported
device's max power. that upper limit is only known after the first
DevInfoSimpleCommand succeeded. wait for that information to be
available.

* DPL: fix initial calculcation backoff

if the calculation backoff is initialized to zero, the backoff will be
doubled to zero until a new, different power limit was calculated for
the first time. this lead to the DPL recalculating a power limit
hundreds of times without a backoff after startup.
2023-07-04 12:05:10 +02:00
Bernhard Kirchen
e457ab73f9
Fix vedirect polling (#291)
* VE.Direct: remove polling interval

the polling interval was meant to limit the amount of MQTT updates.
however, that is already controlled by the global MQTT publish interval.
the removed interval was instead used to limit polling of the VE.Direct
UART for incoming data.

the Victron device sends data unsolicited. the VeDirectFrameHandler does
not implement any polling mechanism. no data is ever sent to the Victron
device.

what the removed polling interval did was cause a buffer overrun of the
HardwareSerial class, since the incoming data was not processed in time.
so every five seconds, we read a whole valid VE.Direct frame, plus some
old data, which was not a whole frame, leading to VE.Direct error
messages to pop up.

with the polling interval removed, no framing errors are reported, and
instead we gain new data from the charge controller approximately ever
two seconds -- for free.

* VE.Direct: change texts to correct VE.Direct capital letters

* VE.Direct: improve "UpdatesOnly" switch labels

especially since the publish interval setting is gone, the label makes
it hard to comprehend what the switch does. update the texts to better
explain what the switch is used for.

use the same text on the VE.Direct info view.

* VE.Direct: use StatusBadge on info view

there were custom badges to indicate the VE.Direct settings. replace
those by the common StatusBadge to make then look the same as the other
badged on the info views.
2023-07-04 12:04:38 +02:00
helgeerbe
006f63ed02 feature: add digest auth on power meter
Power Meter -> HTTP(S) + Jason configuration allows now basic and digest authentication (all Shelly Gen2 devices)
2023-07-04 11:54:46 +02:00
helgeerbe
9a4eb75160 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-07-02 14:32:12 +02:00
helgeerbe
1f39ed7b9b Merge branch 'pr/MalteSchm/288' into development 2023-07-02 14:28:51 +02:00
helgeerbe
afd8790c3c Merge branch 'pr/schlimmchen/287' into development 2023-07-02 14:20:25 +02:00
Bernhard Kirchen
99876d7c6b
DPL: fix the configured upper limit not being respected (#286)
the requested newPowerLimit was indeed limited to the configured maximum
inverter output and the result was stored in effPowerLimit. later,
however, effPowerLimit must be scaled up (if necessary), not
newPowerLimit. the latter variable is not respecting the configured
maximum inverter output.
2023-07-02 14:11:44 +02:00
Bernhard Kirchen
97f58eeba7
explain DPL modes set through MQTT in more detail (#282)
* explain DPL modes set through MQTT in more detail

* docs: DPL mode change through MQTT does not enable DPL
2023-07-02 14:11:23 +02:00
Bernhard Kirchen
097d464bbb
DPL: use vedirect isdatavalid (#283)
* DPL: use VeDirect.isDataValid()

in case the communication to the Victron charger is disrupted, the
respective values in VeDirect.veFrame are not invalidated such that we
would notice a problem. instead, isDataValid() should be used to make
sure that the Victron charger is actually alive and that we can trust to
use the reported values.

* DPL: simplify canUseDirectSolarPower return statement
2023-07-02 14:10:47 +02:00
MalteSchm
e279ce08c4 Dynamic power control and more power control modes on the Huawei PSU 2023-07-01 12:37:55 +02:00
Bernhard Kirchen
9aeb1583b5 DPL: consider the system stable when reusing the old limit
a new status is needed to communicate that no update was sent to the
inverter because its power limit is still valid. in this case,
calculating a new power limit is delayed by an exponentially increasing
backoff. the maximum backoff time is ~1s, which is still plenty fast.

the backoff is actually necessary for another reason: at least
currently, a lot of debug messages are printed to the console. printing
all that information in every DPL loop() is too much.
2023-06-30 21:00:51 +02:00
Bernhard Kirchen
461fce8ff4 DPL: do not repeat being disabled 2023-06-30 21:00:51 +02:00
Bernhard Kirchen
9bab740c43 DPL: replace _plState enum with a simple boolean switch 2023-06-30 21:00:51 +02:00
Bernhard Kirchen
b2d58af5e8 DPL: separate unconditional solar passthrough mode
the unconditional solar passthrough mode, configured using MQTT, works
differently than the normal mode of operation. it is also independent
from the power meter reading. if this mode is active, a shortcut is
taken to a function that implements the actions for this mode. this is
convenient since we don't have to consider special cases in the code
that handles normal mode of operation.
2023-06-30 21:00:51 +02:00
Bernhard Kirchen
71079fa0cc DPL: work on internal copy of pointer to inverter
the DPL already took care to shut down the inverter if anything fishy
was going on, mainly to make sure that the battery is not drained.
however, some cases were missed:

* if the configuration changed such that another inverter is now
  targeted, the one the DPL controlled previously was not shut down.
* if the configuration changed such that another inverter (different
  serial number) was configured at the same index, the previous one
  was not shut down.

this change corrects these problems by making the DPL keep a copy of the
shared_ptr to the inverter. the shared_ptr is only released once the DPL
shut the respective inverter down.
2023-06-30 20:04:39 +02:00
Bernhard Kirchen
2970e84193 DPL requirement: disabled inverter commands
if the inverter is not configured to be sent commands to, the DPL is
unable to control it, so the loop is aborted.
2023-06-30 20:04:39 +02:00
Bernhard Kirchen
18b1076660 DPL: improve responsiveness
this implementation checks all requirements for a new power limit to be
calculated, one after the other. if any requirement is not met, a
respective status is announced.

status messages are communicated on the (serial) console. these can also
be displayed easily on the web app in the future. the status texts
explain clearly what the DPL is currently doing, which aids
understanding how the DPL works. the status is only announced if it
changes, or after a fixed interval.

as each requirement is checked individually, the code readability is
improved as well. previously, all the respective conditions had to be
checked as well, but the statements were more complex.

the DPL loop is now executed with high frequency, i.e., it does not wait
for a fixed timespan to pass before checking requirements. it always
aborts on the first unmet requirement. this should improve responsiveness,
as the DPL checks all requirements more often.

the DPL now waits for all power commands and power limit updates to
complete. when that is the case, a settling time elapses. after the
settling phase, the DPL waits for a new update from the inverter and
from the power meter. now it can be assumed that the values are in sync.
it then makes sense to calculate a new power limit immediately, which
the DPL then does.
2023-06-30 20:04:39 +02:00
Bernhard Kirchen
0b0bcf1dfb fix typo: getLastRequestedPowe*w*rLimit() 2023-06-30 20:04:39 +02:00
Bernhard Kirchen
fd208cf6bb DPL: remove unused member variables 2023-06-30 20:03:21 +02:00
Bernhard Kirchen
8b23324693 ensure only one PowerCommand and ActivePowerCommand is queued
neither a PowerCommand nor an ActivePowerCommand shall be enqueued if
another one is already pending.
2023-06-30 20:03:21 +02:00
Martin
8433820529
Fix typo in error messages (#280)
Signed-off-by: Martin Dummer <martin.dummer@gmx.net>
2023-06-29 13:06:28 +02:00
helgeerbe
0caffe9411 fix: Allow use GPIO0 as onBattery pins 2023-06-29 13:04:35 +02:00
helgeerbe
6ffb5bb897 fix: undefined pin is shown as -1 instead of 255
change pin type for onBattery pins from uint8_t to int8_t.  Negativ pin values means not defined
2023-06-29 13:02:38 +02:00
helgeerbe
2223afac62 add webapp 2023-06-26 12:30:30 +02:00
helgeerbe
acbf80d196 Merge branch 'helgeerbe/issue274' into development 2023-06-26 12:28:28 +02:00
helgeerbe
6ed3ce7417 Fix: #274
Iverter is behind power meter - switch does not work
2023-06-26 12:27:26 +02:00
Bernhard Kirchen
d6163ddb7d
set default values for pin mappings if not provided (#275) 2023-06-26 11:57:29 +02:00
Bernhard Kirchen
07bb0b03f7
DPL hysteresis fix and refactor of setNewPowerLimit() (#264) 2023-06-26 11:52:33 +02:00
helgeerbe
7d73ae3c20 Fix: linting error 2023-06-22 21:55:58 +02:00
Bernhard Kirchen
4d05035661
fix DPL not reading full solar passthrough voltage thresholds (#265)
the defaults for solar passthrough voltage thresholds shall be floats,
so the user can store float values to the config. otherwise, float
values can and will be stored, but when reading them, the defaults will
be applied as the defaults are of an incompatible type.
2023-06-22 21:33:38 +02:00
Bernhard Kirchen
9995c1172e
VE.Direct live view enhancements (#269)
* add calculated values to VE.Direct data

solar current, battery output power, and the charger's efficiency can be
calculated from the values reported by the charger. the efficiency must
be taken with a grain of salt. it seems that the solar power value and
the battery output voltage/current are not always in sync. for that
reason a moving average is used to smooth out the calculated efficiency
value.

* show calculated VE.Direct values in web live view

order the values and translations similarly for the input and output,
starting with power at the top, then voltage, then current as the last
of these three.

* VE.Direct live view: use 'd' as unit for days

'd' is the SI unit symbol for days and does not need translation, which
is desirable as units are not translated throughout the project.

* refactor VE.Direct live view

* move Dynamic Power Limiter data into its own type.
* split VE.Direct data into three types: "device", "input", and
  "output". hence all input and output values are now ValueObject, which
  allows to iterate over them using a loop without typing issues.
* generate the tables with input and output values using a loop, rather
  than defining each row individually.
* localize numbers using $n (vue method), which fixes switching the
  number format (dot vs. comma) when switching the language.
* use no decimal point for power values (they are integers), three
  decimal points for kWh values (charger only reports two decimal
  places, but three are easier to read since the unit is *kilo* Wh), one
  decimal point for the efficiency, and two for voltage and current.
* update language tokens to avoid mapping JSON keys to language keys
  (use the JSON keys to access the language tokens).
* re-structure language tokes so the brief keys took over from
  VeDirectFrameHandler always make sense (nest into "input" and
  "output").
* order values similarly from top to bottom: power, then voltage, then
  current. this is following the order of the inverters' details.
* group values by type/unit (yield and max. power) and order them
  "newest" to "oldest" from top to bottom.
* increase the DynamicJsonDocument as it was too small to hold the newly
  added data.

* update webapp_dist to include VE.Direct live view refactoring
2023-06-22 21:32:20 +02:00
Bernhard Kirchen
016e30ec00
DPL: fix efficiency calculation (#270)
there is no need to assume and hardcode a fixed efficiency for the
Victron solar charger. the charger reports the voltage and current at
its battery terminal, which can be used to calculate the charger's
actual power output.

the fallback to 100% for the efficiency of the Hoymiles inverter, in
case it is not producing power, is too optimistic. this commit proposes
to use 96.7% as the efficiency for that case, which is the peak
efficiency for many (all?) Hoymiles inverters as per datasheet. that
value should be closer to the real efficiency that will be achieved once
the inverter is turned on.
2023-06-22 21:30:33 +02:00
helgeerbe
cd1db49a98 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-06-22 21:14:12 +02:00
helgeerbe
f018a0136e Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-06-21 16:52:02 +02:00
helgeerbe
fd58ad2003 feat(add ability to do a polite power meter read): 2023-06-14 16:21:58 +02:00
helgeerbe
080a3eb29e Merge branch 'PM_onDemand' into development 2023-06-14 13:22:19 +02:00
helgeerbe
da7628dafc Fix: Respect minimum on demand poll rate for power meter 2023-06-14 13:21:50 +02:00
helgeerbe
ceb28030a4 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-06-14 13:02:00 +02:00
helgeerbe
ded5ceec58 Fix: Make power limiter intervall configurable
Allows to change the power limiter intervall settings in the config file.
2023-06-14 12:49:54 +02:00
helgeerbe
86ee7e1a64 perf(PowerMeter): query PowerMeter on demand
In addition to the cyclic query of the power meters, they will be queried on demand when total power is requested and last update is older than 1 second.
2023-06-14 11:25:54 +02:00
helgeerbe
8dac88e7b9 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-06-12 13:07:44 +02:00
helgeerbe
0fd7b75e83 Fix(Power Limiter): hysteresis is not repected properly 2023-06-12 13:06:31 +02:00
helgeerbe
51a21de189 Fix: broken github build action, due to merge with core 2023-06-09 13:37:57 +02:00
helgeerbe
e68baa3086 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-06-09 13:16:56 +02:00
MalteSchm
e0a8da84d7 full solar passthrough 2023-06-09 13:09:58 +02:00
helgeerbe
a6f5e8a3a2 add webapp 2023-06-05 10:13:18 +02:00
helgeerbe
8298a0c36f
Merge pull request #255 from madmartin:inverter_restart
Feature: add daily restart for the inverter
2023-06-05 10:10:14 +02:00
Martin Dummer
c727e21a49
Feature: add daily restart for the inverter
Add a configurable restart option for the inverter to set the "YieldDay"
values to zero.

Signed-off-by: Martin Dummer <martin.dummer@gmx.net>
2023-06-04 09:14:42 +02:00
helgeerbe
91ce844b36 fix(linter warining): use snprintf istead of strcopy 2023-06-02 13:12:42 +02:00
MalteSchm
e7c8a89bd3 inital version of full solar passthrough
Webinterface change to set full solar passthrough values

Adding webapi and config changes to enable full solar passthrough over certain battery Soc

inital version of full solar passthrough in power limiter

Passthrough mode can be enabled via MQTT

translations

re-enable comment

remove unused variable
2023-06-02 12:49:24 +02:00
helgeerbe
2e811b7ab1 fix(action): ignore tags starting with v 2023-05-24 12:30:37 +02:00
Martin
52af52eb3a
Pylontech enhancement (#239) 2023-05-24 09:17:38 +02:00
helgeerbe
a3e7439181 fix(action): sort tags in chronological order 2023-05-24 09:13:04 +02:00
helgeerbe
9f511fb985 fix(action): sort tags in chronological order 2023-05-23 11:43:26 +02:00
helgeerbe
8b64671151 doc: warning, that only 5 inverters are supported at max 2023-05-23 11:04:56 +02:00
helgeerbe
13b318690d fix: linting errors 2023-05-23 10:24:06 +02:00
helgeerbe
47f81f2579 breaking change: Reduce maximum number of invertes to 5 to keep livedata working
livedata json is now calculated as 4096 * MaxInvertes
2023-05-23 10:15:37 +02:00
Martin
3a991708d0
docs/Web-API.md: fix vedirectlivedata typo (#238)
- fix vedirectlivedata typo
- add some more api urls

Signed-off-by: Martin Dummer <martin.dummer@gmx.net>
2023-05-23 09:25:36 +02:00
helgeerbe
b7ac70b1ca Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-05-23 09:19:57 +02:00
helgeerbe
1c7b7d3cdf Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-05-10 18:16:25 +02:00
helgeerbe
556f3e0abf docs: Show as badge in readme original openDTU release number
Badge shows the openDTU release which is used as core to build openDTU-onBattery's release
2023-05-10 15:27:03 +02:00
helgeerbe
40a65198fe add webapp 2023-05-08 13:27:24 +02:00
MalteSchm
065c169b20
Set initial PL state in init() to avoid inverter shutdown on reboot... (#224) 2023-05-08 13:25:02 +02:00
helgeerbe
178d40d5b4 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-05-04 20:24:29 +02:00
MalteSchm
569edbe69e
static casts set last requested limit for all cases where inverter power is changed (#213) 2023-05-01 21:20:49 +02:00
helgeerbe
33423dfc2f Merge branch 'pr/MalteSchm/199' into development 2023-04-30 14:11:57 +02:00
MalteSchm
6fe19f00a7 drafted description of power limiter operation
Rework to include start threshold for solar passthrough off case, simplfied text as there are now more similarities

adding discharge at night text
2023-04-30 13:23:01 +02:00
helgeerbe
d1885b6177 workaround for faild builds on github actions 2023-04-29 23:19:08 +02:00
helgeerbe
8021052cfd PL remove debug messages 2023-04-29 22:53:50 +02:00
helgeerbe
e5af5be70a remove comment 2023-04-29 21:53:05 +02:00
MalteSchm
db4125ae7a debug printouts, removed unnecessary check causing inverter to toggle 2023-04-29 14:28:24 +02:00
helgeerbe
dae4c6fbf5 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-04-28 22:38:39 +02:00
helgeerbe
2d14d9f69e Merge branch '198-enhancement-simplifying-the-settings-menu' into development 2023-04-28 20:11:07 +02:00
helgeerbe
3f0291dce0 : Enhancement - Simplifying the settings menu #198 2023-04-28 20:08:36 +02:00
helgeerbe
5d4c6866da
fix: ignore MQTT messages for disabled components #203 (#204) 2023-04-28 19:26:58 +02:00
MalteSchm
c621f2d3e3
Power limiter fixes (#201) 2023-04-28 13:59:18 +02:00
helgeerbe
80f1af32f8 Fix: Link to git hash references onBattery repo now 2023-04-27 21:55:07 +02:00
helgeerbe
46c733ca31 Revert "Fix: Link to git hash references onBattery repo"
This reverts commit 23b35d9b00.
2023-04-27 21:45:50 +02:00
helgeerbe
32913c2b2e Revert "fix: change url for firmware info to"
This reverts commit dfa0a1c98b.
2023-04-27 21:45:50 +02:00
helgeerbe
dfa0a1c98b fix: change url for firmware info to
openDTU-onbattery
2023-04-27 21:31:38 +02:00
helgeerbe
cc32e3973e Fix: Link to git hash references onBattery repo
now

add webapp
2023-04-27 20:32:41 +02:00
helgeerbe
23b35d9b00 Fix: Link to git hash references onBattery repo
now
2023-04-27 20:15:57 +02:00
helgeerbe
4abc89d43c build release only if tag starts with 2 2023-04-27 19:59:18 +02:00
MalteSchm
70060559da
Bring back the sun indicator (#195)
* Adding states to display in UI

* Adding states to display in UI
2023-04-27 19:10:12 +02:00
MalteSchm
6b437b5ea1
Inverter toggle fix (#196)
* updating the interface to calcPowerLimit to include both energy sources

* Fixing definition
2023-04-27 19:09:48 +02:00
helgeerbe
9ff9a8a6d6
Merge pull request #192 from madmartin/build-test
Fix: github build create opendtu-onbattery.* files
2023-04-27 19:09:21 +02:00
Martin Dummer
00490b80af build test 2023-04-27 06:50:30 +02:00
helgeerbe
89209b6bf7 Actions and badges reflects openDTU-onBattery now 2023-04-26 15:23:17 +02:00
helgeerbe
83b42a36b9 Merge branch 'development' 2023-04-26 15:02:40 +02:00
helgeerbe
e65b2196bf add webapp 2023-04-26 12:53:50 +02:00
helgeerbe
d4a5570806 Feature: Add battery Icon on Navbar, to
indicate openDTU-onBattery is running
2023-04-26 12:52:28 +02:00
helgeerbe
c337df605c Merge branch 'pr/MalteSchm/172' into development 2023-04-26 12:37:31 +02:00
helgeerbe
e91935ab38 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-04-26 11:47:19 +02:00
helgeerbe
37b5edb010 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-04-25 18:51:18 +02:00
helgeerbe
71dda4b89c Merge branch 'pr/qubeck/156' into development 2023-04-25 18:39:02 +02:00
MalteSchm
c7505eaae6 useSolarPowerOnly did not take current consumption into account. fixed 2023-04-25 12:07:24 +02:00
MalteSchm
0a0488f73a refactored use solar power code 2023-04-25 12:03:41 +02:00
MalteSchm
a8554f97b0 refactored use solar power code 2023-04-25 11:05:08 +02:00
MalteSchm
322f532ac0 Proper handling of use solar power only case 2023-04-25 10:41:35 +02:00
helgeerbe
8f386c8611
Merge pull request #182 from MalteSchm:readme_refactor
Documentation updates / Refactored Readme
2023-04-24 10:54:26 +02:00
MalteSchm
e2a6468304 Webapp build 2023-04-23 18:50:24 +02:00
MalteSchm
8764809259 Webapp update do display power values 2023-04-23 18:49:32 +02:00
MalteSchm
3861ab89f1 WebApi update to include power values 2023-04-23 18:49:32 +02:00
helgeerbe
75a59f5d1d
Merge pull request #175 from MalteSchm:huawei_spi_interface_fix
Changing Huawei SPI interface
2023-04-23 18:32:36 +02:00
MalteSchm
8c9afbcdc0 fix an issue if inverter is behind power meter 2023-04-23 11:30:08 +02:00
MalteSchm
422ad3f909 reverting to old Readme, moving documentation to new Readme 2023-04-22 12:24:24 +02:00
MalteSchm
71da704d38 Refactor readme, factored out hardware and flashing in separate document. Integrated MQTT, Webapi, PinMappings related text in the correct sections. Updated documentation / diagrams to reflect the hardware required 2023-04-22 10:47:00 +02:00
MalteSchm
1e30323915 Renaming readme 2023-04-22 10:43:45 +02:00
helgeerbe
38b990fbbc Fix: PowerLimiter is now casted to signed int to allow negativ values. 2023-04-20 20:02:59 +02:00
helgeerbe
41cc5ff4f9 add webapp 2023-04-19 14:34:07 +02:00
helgeerbe
84a6ed540d Merge branch 've.direct-hex' into development 2023-04-19 14:33:00 +02:00
helgeerbe
943a0eb58e Fix: [Request] rename "Erlaubter Stromverbrauch" to "Angestrebter Netzbezug" helgeerbe/OpenDTU-OnBattery#178 2023-04-19 14:25:24 +02:00
helgeerbe
6a9585cd1e Fix: Power Limiter Settings / cannot set permitted current below zero helgeerbe/OpenDTU-OnBattery#171 2023-04-19 14:19:26 +02:00
helgeerbe
545a18db6f Merge branch 'development' 2023-04-18 12:09:48 +02:00
helgeerbe
a1b3bdfee8 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-04-18 12:09:22 +02:00
helgeerbe
8cc548d13e add pid 0XA110 SmartSolar MPPT RS 450|100 2023-04-17 22:33:11 +02:00
helgeerbe
b8e06bf824 ve.diect with hex asnync messages
ignore async hex messeage on older devices
2023-04-17 22:18:20 +02:00
helgeerbe
1e16eca16e Merge branch 'development' 2023-04-17 11:14:03 +02:00
helgeerbe
40cee1f9ca Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-04-17 11:08:55 +02:00
MalteSchm
3504924bb0 Changing SPI interface 2023-04-16 18:34:09 +02:00
MalteSchm
82699b1c88 Merge branch 'mqtt_power_limiter_enable' of https://github.com/MalteSchm/OpenDTU-OnBattery into mqtt_power_limiter_enable 2023-04-16 18:30:47 +02:00
MalteSchm
4ddaa7643b make sure that PL is enabled if user uses webinterface 2023-04-16 18:30:21 +02:00
MalteSchm
0d0a624fe2 make sure that PL is enabled if user uses webinterface 2023-04-16 18:28:49 +02:00
MalteSchm
a306bc1351 Bugfix for Mqtt enable / disable (was float, uses int now) 2023-04-16 18:28:09 +02:00
helgeerbe
b00ca02aac fix layout for vedirect live data on small devices 2023-04-13 11:38:20 +02:00
helgeerbe
1a0f44dac4 Merge branch 'development' 2023-04-13 10:21:23 +02:00
helgeerbe
1dc73f91ee Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-04-13 10:18:49 +02:00
MalteSchm
3c5082287e remove a leftover debug message 2023-04-13 10:07:25 +02:00
MalteSchm
690025e5fd fixing a bug from merging and remove a leftover debug message 2023-04-13 10:06:06 +02:00
MalteSchm
be7a43fbfb Removing un-necessary timestamp, commenting code and cleanups 2023-04-13 09:47:12 +02:00
MalteSchm
01849dc90a adding Mqtt handling to main.cpp 2023-04-13 09:47:10 +02:00
MalteSchm
ee376827fd merging functionality from PL refactor 2023-04-13 09:47:07 +02:00
MalteSchm
9999fa28e8 refactor state machine
merging
2023-04-13 09:46:35 +02:00
MalteSchm
ee82c8c9b8 adding option to disable power limiter via mqtt
adding option to disable power limiter via mqtt - adding missing file
2023-04-13 09:44:48 +02:00
MalteSchm
9efe076cc2 resolving merge conflict
adding missing statement from merge

fixing a bug introduced in merge
2023-04-13 09:39:04 +02:00
MalteSchm
fc5089e70b resolving merge conflict 2023-04-13 09:36:00 +02:00
MalteSchm
9ff1885d5a Removing un-necessary timestamp, commenting code and cleanups 2023-04-13 07:45:26 +02:00
qubeck
b79619bf8b add explicit checks to avoid potential div. by zero on application of artificially increased power limit if channel power becomes zero 2023-04-12 20:44:38 +02:00
helgeerbe
1970b939e0 add webapp 2023-04-12 17:50:39 +02:00
helgeerbe
1d62e36303 Merge branch 'development' 2023-04-12 17:49:31 +02:00
MalteSchm
03ce71519d Merge remote-tracking branch 'upstream/development' 2023-04-12 16:16:21 +02:00
MalteSchm
74dcddaa1a merging linting error fixes 2023-04-12 16:14:33 +02:00
Bernhard Kaszt
616d0425db
Merge pull request #170 from MalteSchm/only_linting_error_fix
webapp / yarn linting issues
2023-04-12 16:13:19 +02:00
MalteSchm
43f7553cdd removing commented lines 2023-04-12 15:56:33 +02:00
MalteSchm
72289cda1a fixed linting issues, pushing to github for test run 2023-04-12 13:21:32 +02:00
MalteSchm
d2fc00b7d6 Merge branch 'master' of https://github.com/MalteSchm/OpenDTU-OnBattery 2023-04-12 13:06:21 +02:00
MalteSchm
f99f80159d Merge remote-tracking branch 'upstream/development' 2023-04-12 13:05:53 +02:00
MalteSchm
c430b69322
Merge branch 'helgeerbe:master' into master 2023-04-12 13:02:10 +02:00
MalteSchm
79834e4d47 adding Mqtt handling to main.cpp 2023-04-12 12:51:24 +02:00
MalteSchm
1b29133ee0 merging functionality from PL refactor 2023-04-12 12:21:52 +02:00
Bernhard Kaszt
1d29781804
Merge pull request #167 from MalteSchm/only-typo-fix
Typo fix
2023-04-12 11:00:35 +02:00
Bernhard Kaszt
d28c9dbc4b
Merge pull request #168 from MalteSchm/only-mppt-150-fix
Mppt string for SmartSolar 150/48 is wrong
2023-04-12 10:59:51 +02:00
MalteSchm
515bb1c7ce merging mqtt enable/disable with new refactored state machine code 2023-04-12 10:35:55 +02:00
MalteSchm
1e4337e900 merging 2023-04-12 10:12:47 +02:00
MalteSchm
a893260de0 refactor state machine 2023-04-12 10:05:17 +02:00
MalteSchm
97a8545d78 merging branches to prepare mqtt enablement 2023-04-12 09:41:40 +02:00
MalteSchm
b6edc11eb2 adding option to disable power limiter via mqtt - adding missing file 2023-04-12 06:46:38 +02:00
MalteSchm
4bff31e3b1 adding option to disable power limiter via mqtt 2023-04-12 06:45:41 +02:00
MalteSchm
06f6a4da8b fixing mppt string for SmartSolar 150/48 2023-04-11 19:23:49 +02:00
MalteSchm
2d7115e1e8 typo 2023-04-11 18:58:18 +02:00
MalteSchm
f6d0b0997f typo 2023-04-11 18:57:57 +02:00
helgeerbe
9da5be7fd8 Merge branch 'development' 2023-04-11 10:22:27 +02:00
helgeerbe
71128e5a55 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-04-11 09:37:56 +02:00
MalteSchm
406332f6cd fixed linting issues, pushing to github for test run 2023-04-09 23:09:16 +02:00
MalteSchm
a1252c5701 Merge branch 'typo_fix' 2023-04-09 22:10:05 +02:00
MalteSchm
28e204fd80 typo 2023-04-09 22:06:47 +02:00
MalteSchm
86ecc62b33 fixing mppt string for SmartSolar 150/48 2023-04-09 21:58:41 +02:00
MalteSchm
e3964f8bbe typo 2023-04-09 21:55:45 +02:00
MalteSchm
869d8e6d8b fixing a bug introduced in merge 2023-04-07 20:37:26 +02:00
berni2288
19b2dd4c7a
PowerMeter: Whitespace and {} fixes 2023-04-07 20:22:35 +02:00
qubeck
00def1d8d1
Generic SML based power meters support (#146)
* add support for energy & power readings on SML based power meters, taking OBIS 16.7.1 for power (using mod. SML Parser lib. by olliiiver)

* switched SML read to use software serial

* made total power meter response controled by meter source to obtain either the sum of phase powers or explicit total power provided by meter

* made mqtt subscriptions to power meter topics meter source dependend

* simplified SML read loop and OBIS handler registration, + minor refactoring

* minor cleanup/style changes and optim. PowerMeter

* fixed build, add SOURCE_SML == 4

* removed optional usage of HW serial for SML power meter

* switched to usage of _powerMeter1Power for SML power reading to allign better with existing code

---------

Co-authored-by: helgeerbe <helge@erbehome.de>
2023-04-07 20:20:00 +02:00
MalteSchm
734d34b7a8 make calcPowerLimit respect DTU poll interval 2023-04-07 19:08:27 +02:00
MalteSchm
f84bdf7287 adding missing statement from merge 2023-04-07 19:01:00 +02:00
MalteSchm
cb6b98499a merging 2023-04-07 18:26:44 +02:00
MalteSchm
9521deea25 refactoring calcPowerLimit and setPowerLimit 2023-04-07 18:16:26 +02:00
helgeerbe
fd94a69ff8
Update Readme
add badge, which shows the original OpenDTU release number
2023-04-06 19:07:25 +02:00
helgeerbe
e4822f00ec Merge branch 'development' 2023-04-06 18:33:19 +02:00
helgeerbe
e29708f871 fix infinite state switch if battery is above
startThreshold and solar passtrhough is enabled
2023-04-06 18:32:50 +02:00
helgeerbe
724f1cf713 Merge branch 'development' 2023-04-06 13:57:57 +02:00
helgeerbe
4eec055f84 fix lint errors 2023-04-06 12:08:10 +02:00
helgeerbe
ef51d75f2c Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-04-06 10:46:02 +02:00
tbnobody
d6ff90260e Undo latest change 2023-04-06 10:37:05 +02:00
tbnobody
7f17176462 Test multpile commits 2023-04-06 10:37:05 +02:00
berni2288
0ff9ebfac9
Rebuild webapp 2023-04-05 22:28:20 +02:00
berni2288
09fb0618b4
Merge branch 'MalteSchm-webinterface_summary_updates' into development 2023-04-05 22:27:17 +02:00
helgeerbe
2a5f9776a3 add webapp 2023-04-05 11:58:11 +02:00
Thomas Basler
52deea3033 Added config for blinkyparts kit 2023-04-05 11:56:59 +02:00
Thomas Basler
ede572f6e3 Status LED's: Implemented
They can be activated using device profiles.
2023-04-05 11:56:59 +02:00
Thomas Basler
ca8c9b55ae TimeoutHelper: Allow resetting the last value 2023-04-05 11:54:50 +02:00
Thomas Basler
ca0b8e9afc Remove not required F() macro. Frees ~20kb flash. 2023-04-05 11:54:50 +02:00
Thomas Basler
dab5b4d723 webapp: Show inverter status in grey if polling is disabled (e.g. at night) 2023-04-05 11:52:35 +02:00
Thomas Basler
9eb15274a2 webapp: Update dependencies 2023-04-05 11:52:34 +02:00
Thomas Basler
e0150a8962 Increase platform package version from 6.0.1 to 6.1.0 2023-04-05 11:52:34 +02:00
Thomas Basler
f37b23b706 webapp: Update dependencies 2023-04-05 11:51:09 +02:00
Thomas Basler
53b965651d webapp: Implemented reload button for info views 2023-04-05 11:51:09 +02:00
Thomas Basler
d501c4b836 Uses VSPI instead of HSPI and compatibility for C3, S2 and S3 MCUs 2023-04-05 11:51:09 +02:00
Thomas Basler
7d50fa373e Allow use GPIO0 as NRF pins 2023-04-05 11:51:09 +02:00
Thomas Basler
f45de7ba36 Added additional compatible inverter 2023-04-05 11:51:09 +02:00
Thomas Basler
3e208219b6 Fix #753: Only apply offset if data is in the buffer to prevent negative numbers 2023-04-05 11:51:09 +02:00
Thomas Basler
edaa223856 Fixed typo in comment 2023-04-05 11:51:09 +02:00
Thomas Basler
d8a15c39cb webapp: Fix typos 2023-04-05 11:51:09 +02:00
MalteSchm
a1b63b61d7 webapp build 2023-04-05 10:51:33 +02:00
MalteSchm
801ad469c5 corrected day yield 2023-04-05 10:51:07 +02:00
Bernhard Kaszt
8ca664a8fe
Use AC power for limit calculation (= Support directly connected PV panels with Power Limiter) (#154)
* Power limiter: Use the actual AC power for limit calculation

instead of the last set limit.

In order support setups without battery connected (sources that don't exhaust the limit)
2023-04-05 10:30:03 +02:00
helgeerbe
c0dff1e7df catch bad_alloc for Huawei and Pylontech WebApi_ws 2023-04-05 09:48:38 +02:00
MalteSchm
459f9ffc2c merging branches 2023-04-04 21:43:24 +02:00
MalteSchm
2950f55879 Webinterface updates 2023-04-04 21:32:40 +02:00
MalteSchm
169ea3d5d5 Api extensions 2023-04-04 21:31:32 +02:00
MalteSchm
abffc38c11 fixing typos 2023-04-04 21:30:53 +02:00
helgeerbe
0c34554b9c don't set newPowerlimit if newLimit AND lastLimit in target window 2023-04-04 18:20:06 +02:00
helgeerbe
160b5b5b01 handle bad_alloc for vedirect status 2023-04-04 18:17:53 +02:00
qubeck
cd4a327671 limiting the artificialy increased power limit to inverter maximum power 2023-04-04 17:11:59 +02:00
berni2288
98faffc3ca
Build app.js.gz 2023-04-03 21:38:26 +02:00
Bernhard Kaszt
e35254c8f2
New Power meter support: HTTP(S) + JSON (Shelly 3EM, Tasmota, Volkszähler etc.) (#153)
* Implement HTTP(s) + JSON type Power Meter support

---------

Co-authored-by: Bernhard Kaszt <berni@bcserv.eu>
2023-04-03 21:36:20 +02:00
qubeck
bd57d0f19a fixed casting issues 2023-04-02 22:26:44 +02:00
qubeck
a1da3f9842 producing DC channel aware artificial increase of applied power limit to mitigate fixed distribution of applied power limit across all channels 2023-04-02 22:13:43 +02:00
MalteSchm
20bb7fc372
Show (Pylontech) battery infos in Live View
* adding data age to battery data

* Add battery enabled flag

* Webapi and websocket api for Battery

* Webinterface for battery

* fixed bug due to naming inconsistencies

* cleaned up rounding

* dist update

* change typename to uppercase

* reverting to original file
2023-04-02 20:58:28 +02:00
MalteSchm
90f5ed4251 reverting to original file 2023-04-02 14:43:45 +02:00
MalteSchm
4a664a7b3d change typename to uppercase 2023-04-02 14:41:50 +02:00
MalteSchm
6b85b8d4a2 Merge remote-tracking branch 'upstream/development' into battery_webinterface 2023-04-02 14:28:37 +02:00
MalteSchm
a4053dcf19 dist update 2023-04-02 14:18:37 +02:00
MalteSchm
7dee289b5b cleaned up rounding 2023-04-02 14:18:13 +02:00
MalteSchm
78838585f7 fixed bug due to naming inconsistencies 2023-04-02 14:17:23 +02:00
MalteSchm
fa5b52210a Webinterface for battery 2023-04-02 13:03:03 +02:00
MalteSchm
59c84bcb85 Webapi and websocket api for Battery 2023-04-02 13:00:46 +02:00
Bernhard Kaszt
4de043f3d4 Fix crash when AC charger is enabled but initialization fails 2023-04-02 10:46:07 +02:00
Bernhard Kaszt
c86c5133f0 Fix code style in Huawei files 2023-04-02 10:42:36 +02:00
Bernhard Kaszt
c7ef661db7 platformio.ini: Fix indentation 2023-04-02 10:14:34 +02:00
MalteSchm
a0bbf61db2 Add battery enabled flag 2023-04-02 10:11:48 +02:00
Bernhard Kaszt
ebaf5c4565
Merge pull request #151 from MalteSchm/huawei_lint_pin_mgr_fixes
Huawei lint pin mgr fixes
2023-04-02 10:04:53 +02:00
MalteSchm
e514ef744b adding data age to battery data 2023-04-01 15:34:32 +02:00
MalteSchm
d1e43c11b9 Run Huawei code only when enabled 2023-04-01 14:43:24 +02:00
MalteSchm
1f6301c2c0 Check for empty MQTT topic
Please enter the commit message for your changes. Lines starting
2023-04-01 14:02:09 +02:00
MalteSchm
f4455ccb93 reverting 2023-04-01 13:50:04 +02:00
MalteSchm
a091e80ed0 deal with un-initialized values 2023-03-29 19:07:16 +02:00
MalteSchm
a968f09d73 Pin config 2023-03-29 19:06:41 +02:00
MalteSchm
5f42f66c02 fix indent error 2023-03-29 19:05:31 +02:00
MalteSchm
ece131995a linting errors 2023-03-29 19:04:41 +02:00
helgeerbe
b3c17c8ee8 Merge branch 'pr/MalteSchm/144' into development 2023-03-27 22:16:53 +02:00
helgeerbe
6f2901b324 Merge branch 'development' 2023-03-27 21:14:40 +02:00
helgeerbe
e0a80734f3 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-03-27 21:10:03 +02:00
MalteSchm
2aad13dc72 Changing SPI interface 2023-03-27 20:47:35 +02:00
helgeerbe
7ba2058625 Merge branch 'development' 2023-03-27 10:59:51 +02:00
MalteSchm
07ea03d12b fixing linting issue 2023-03-26 15:46:33 +02:00
MalteSchm
06d5da50a2 the implementation has used HSPI with the default VSPI pins for a long time. Change VSPI and HSPI to use the right pins in order to avoid confusing 2023-03-26 12:36:21 +02:00
MalteSchm
d80f62d1b9 disable Mqtt output when disabled 2023-03-26 12:13:06 +02:00
MalteSchm
26eedc9701 align mqtt format with the other sources 2023-03-26 11:56:41 +02:00
MalteSchm
89be653a51 inverse logic for power pin (active high) 2023-03-26 11:56:11 +02:00
MalteSchm
a4767827b4 merging master 2023-03-26 11:25:08 +02:00
MalteSchm
0be574809b adding power pin 2023-03-26 11:09:08 +02:00
MalteSchm
56e52156e5 webapp dist 2023-03-26 11:07:33 +02:00
MalteSchm
0e2b7767c7 Webapp changes to display Huawei PSU values and to enable/disable the unit 2023-03-26 11:06:51 +02:00
MalteSchm
3b57550ead adding basic mqtt support 2023-03-26 11:02:40 +02:00
MalteSchm
bbf3d44d69 working with a reference 2023-03-26 11:01:32 +02:00
MalteSchm
0b5c47cd2e Adding enable/disable option and pin to control a switch/relais to power the Huawei PSU 2023-03-26 11:00:37 +02:00
helgeerbe
7d48e426dc free JsonDocument for livedata as soon as possible 2023-03-24 15:39:03 +01:00
MalteSchm
db7ad52a4d platformio ini file changes 2023-03-24 10:05:46 +01:00
MalteSchm
1aab888bf2 Documentation updates 2023-03-24 10:04:08 +01:00
MalteSchm
8576034b77 Adding Huawei CAN interface, web-api, websocket and Mqtt extensions to access the data 2023-03-24 10:03:22 +01:00
helgeerbe
3f8226c36c Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-03-23 19:14:14 +01:00
helgeerbe
0832ef86e4 reboot after power meter settings 2023-03-23 19:10:29 +01:00
helgeerbe
55dc4dbdfc reinitialize mqqt subscription after reconnect 2023-03-22 17:47:15 +01:00
helgeerbe
5d5124dd5b fix last power meter update time 2023-03-22 14:53:36 +01:00
helgeerbe
a441a6eaf7 fix mqqt supscibe in power meter 2023-03-22 14:11:55 +01:00
helgeerbe
aaa9f5cd98 power meter settings were not restored. On reboot only default were set 2023-03-22 12:34:47 +01:00
helgeerbe
cf7f815eba add webapp 2023-03-22 12:08:23 +01:00
helgeerbe
d96be997ac hide power meter settigs if disabled 2023-03-22 12:07:43 +01:00
helgeerbe
080ef03eec correct labels for power meter 2023-03-22 11:47:18 +01:00
helgeerbe
41da6d489d add missing webapi init for powermeter 2023-03-22 11:34:55 +01:00
helgeerbe
c06be1c56c add webapp 2023-03-22 10:17:13 +01:00
helgeerbe
e6963822e9
Merge pull request #140 from Adminius:development
PowerMeter Class + SDM PowerMeter support
2023-03-22 10:14:52 +01:00
Eugen
effd4e89ab
BF: get powerTotal from PowerMeter 2023-03-22 08:21:34 +01:00
Adminius
05a5b2367b PowerMeter Class + SDM PowerMeter support 2023-03-21 23:46:54 +01:00
helgeerbe
dbb548e731
Update README.md 2023-03-21 10:53:13 +01:00
helgeerbe
caeff77dae update state diagram 2023-03-21 10:50:27 +01:00
helgeerbe
a4c8e85be5 add webapp 2023-03-20 15:39:57 +01:00
helgeerbe
19252629cb minimum panel DC power 20W 2023-03-20 15:17:07 +01:00
helgeerbe
a6c852a82c if new limit too low, turn inverter off
within state
2023-03-20 12:35:29 +01:00
helgeerbe
2dc7089aa6 Start at night 2023-03-19 22:53:24 +01:00
helgeerbe
b9acbe6f2c off when newPowerLimit < lowerLimit 2023-03-18 23:50:34 +01:00
helgeerbe
06370baa0e Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-03-18 22:42:05 +01:00
helgeerbe
c95468f972 fix comparison between int and double 2023-03-18 14:44:28 +01:00
helgeerbe
46ce6ad50f Implement battery drain strategies:
- empty when full
- empty at night
2023-03-16 17:48:22 +01:00
helgeerbe
32a96bbd06 change all watt related vars to
int32_t to avoid cast problems with negativ values.
2023-03-16 12:34:14 +01:00
helgeerbe
04c7e4fa01 fix cast error with unsigned int
which results in wrong power limit settings if values become < 0 due
 to power export to the grid.
2023-03-16 12:17:14 +01:00
helgeerbe
9214897245 for watt use uint32_t as std type to
avoid cast problems to uint16_t
2023-03-13 11:38:26 +01:00
Bernhard Kaszt
e95acbec46 PylontechCanReceiver.cpp: Cosmetic changes 2023-03-12 18:47:29 +01:00
Bernhard Kaszt
c7f6aea763
Merge pull request #135 from MalteSchm/pylontech_can_bus_lib_change
swap CAN bus library to better support newer ESPs
2023-03-12 18:24:00 +01:00
MalteSchm
63c956af15 swap can library to better support newer ESPs 2023-03-12 17:27:41 +01:00
helgeerbe
6bc6796c23 add webapp 2023-03-10 16:29:51 +01:00
helgeerbe
01a2ffaed5 [Request] Show actual power limiter state in live view helgeerbe/OpenDTU-OnBattery#134 2023-03-10 16:29:00 +01:00
helgeerbe
7952becd17 New Dark Mode does not look nice for victron live view
helgeerbe/OpenDTU-OnBattery#133
2023-03-10 09:48:58 +01:00
helgeerbe
fe9959cc97
Update README.md 2023-03-09 13:05:20 +01:00
helgeerbe
a043d55b01 typos PowerLimiterStates diagram 2023-03-09 13:05:06 +01:00
helgeerbe
1b2d1afb9b add webapp 2023-03-09 12:44:31 +01:00
helgeerbe
43dc10b868 Merge branch 'inverter-settings' into development 2023-03-09 12:43:03 +01:00
helgeerbe
26dc262641 add Powerlimiter State diagram to docs 2023-03-09 12:20:18 +01:00
helgeerbe
a79f7b2026 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-03-09 10:52:46 +01:00
helgeerbe
a3f35f2491 add webapp 2023-03-09 10:49:28 +01:00
helgeerbe
4cf4594945
Merge pull request #131 from helgeerbe/hide-vedirect-livestatus-when-disabled 2023-03-09 10:34:51 +01:00
helgeerbe
87794e0793
Merge pull request #132 from helgeerbe/fix-battery-pinmapping-missing-in-ui 2023-03-09 10:31:40 +01:00
helgeerbe
8df2c16cce ensure STATE_DISCOVER if PowerLimter is enabled 2023-03-09 10:22:08 +01:00
helgeerbe
418fea2cfc fix veStruct
* PPV is int not double
* fix: initialize veStruct to 0 to avoid random data on startup
2023-03-09 10:16:55 +01:00
helgeerbe
6f3e33c0b1 exit loop after turn inverter on or off 2023-03-08 22:11:01 +01:00
Bernhard Kaszt
5b0e627f6d Fix battery pin mapping not being shown in device manager UI
(Pylontech Battery CAN Bus)
2023-03-08 20:29:08 +01:00
Bernhard Kaszt
a66f818e75 Hide Victron Ve.direct UI elements from Live page when disabled
Resolves #91
2023-03-08 19:54:19 +01:00
helgeerbe
1e6e40a3ab add Pylontech pins to d1 mini config 2023-03-07 21:27:51 +01:00
helgeerbe
83d61b1b80 mention in readme that pin
mapping is supported for Pylontech
2023-03-07 21:24:10 +01:00
helgeerbe
08cf6a523d add webapp 2023-03-07 21:18:05 +01:00
helgeerbe
6a89ae986e Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-03-07 21:17:48 +01:00
helgeerbe
6dd34a8401
Merge pull request #125 from helgeerbe/powerlimiter
Translations for powerlimiter admin; Pylontech CAN Pins in Device manager; other fixes & enhancements
2023-03-07 21:13:48 +01:00
helgeerbe
716fc867a1 powerLimiter with state machine 2023-03-07 21:04:19 +01:00
Bernhard Kaszt
44a770be0e Add Pylontech PIN numbers to platformio.ini 2023-03-05 21:22:10 +01:00
Bernhard Kaszt
06a0f76fed Fix local build working but Github build failing 2023-03-05 20:45:27 +01:00
Bernhard Kaszt
06fbdf1f12 Add Pylontech battery to device pin manager 2023-03-05 19:55:56 +01:00
Bernhard Kaszt
43436e19b7 Translate all remaning Powerlimiter settings 2023-03-05 19:17:30 +01:00
Bernhard Kaszt
304d90062d Revert broken change in condition that sets _consumeSolarPowerOnly
6709338dbd
2023-03-05 16:30:53 +01:00
Bernhard Kaszt
a6e720f154 Powerlimiter: Remove MQTT Topic debug message 2023-03-05 15:41:21 +01:00
Bernhard Kaszt
804f225908 Fix _consumeSolarPowerOnly sometimes being set to false when it shouldn't 2023-03-05 13:32:18 +01:00
helgeerbe
b70407d7fe Initilise variables 2023-03-04 13:02:30 +01:00
helgeerbe
3ffc1c947d Merge branch 'inverter-settings' into development 2023-03-04 12:44:19 +01:00
helgeerbe
9391b11403 fix start inverter 2023-03-04 11:59:10 +01:00
helgeerbe
b7dda83545 use efficiency factor if limit is set to
victronChargePower
2023-03-03 20:34:11 +01:00
helgeerbe
cf4a35e148 fix starting and stopping inverter 2023-03-03 19:51:04 +01:00
helgeerbe
9f2d79f2dc Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-03-03 10:53:43 +01:00
helgeerbe
6709338dbd fix setting consumeSolarPowerOnly 2023-03-02 16:18:44 +01:00
helgeerbe
67a5217482 start inverter if power consumption greater or equal lower power limit 2023-03-02 14:27:42 +01:00
helgeerbe
260f4ccdb8
Update README.md 2023-03-01 15:28:54 +01:00
helgeerbe
c36e4cc3da
Update README.md 2023-03-01 15:23:25 +01:00
helgeerbe
028b43f4d1
Update README.md 2023-03-01 15:14:07 +01:00
helgeerbe
c0735c5b1f
Update README.md 2023-03-01 14:46:06 +01:00
helgeerbe
3e38941b57 start inverter only if not exporting to grid 2023-03-01 13:34:27 +01:00
helgeerbe
6241a31e8c comment out full wifi scan 2023-03-01 12:09:53 +01:00
helgeerbe
4a51ae5038 victron charge power is integer 2023-03-01 10:23:37 +01:00
helgeerbe
26a8809121 stop inverter, if exporting power to grid 2023-02-28 13:09:10 +01:00
helgeerbe
4a49f48969 update webapp 2023-02-27 20:12:31 +01:00
helgeerbe
4feaa1db98 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-02-27 20:11:06 +01:00
helgeerbe
3b62d5708a Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-02-27 07:42:46 +01:00
helgeerbe
d146adcc3b add webapp 2023-02-25 22:33:29 +01:00
helgeerbe
e9fce49fee fix lint error 2023-02-25 22:23:05 +01:00
helgeerbe
32342dcd5d fix voltage threshhold to 2 decimal digits 2023-02-25 22:12:44 +01:00
helgeerbe
17dd9db946 set min target power consumption to 0 2023-02-25 20:17:25 +01:00
helgeerbe
3ed61319ad fix config read for power consumption 2023-02-25 18:58:41 +01:00
helgeerbe
43815f6711 power limiter make fields mandtory 2023-02-25 17:25:02 +01:00
helgeerbe
c2ba4a334e build webapp 2023-02-25 16:29:25 +01:00
helgeerbe
1e968a1713 Add target for power consumption 2023-02-25 16:28:21 +01:00
helgeerbe
3f3540bd33 print ve.direct rx, tx pins on init 2023-02-25 15:29:00 +01:00
helgeerbe
28b24d01ad implement UI for target power consumption 2023-02-24 16:13:32 +01:00
helgeerbe
cd7ece7caf fix hard coded channel settings 2023-02-24 10:45:59 +01:00
helgeerbe
10f907477d fix name for inverter id and channel id 2023-02-23 22:41:37 +01:00
helgeerbe
56151b0d12 make inverter and channel selectable 2023-02-23 21:46:59 +01:00
helgeerbe
73fe3bfb96 Add missing can lib 2023-02-23 12:05:50 +01:00
helgeerbe
a2473645a5 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-02-23 12:01:34 +01:00
helgeerbe
1caeb1d88b fix logging for out of ressources 2023-02-23 09:46:24 +01:00
helgeerbe
a758d894f6 LiveService if out of ressources close websocket 2023-02-22 15:46:52 +01:00
helgeerbe
0f80a25937 send http 429 "Too Many Requets",
if prometheus api is out of resources
2023-02-22 14:13:17 +01:00
helgeerbe
9e1df83a87 remove map.h include 2023-02-21 22:12:15 +01:00
helgeerbe
1e7f6b8f0f first version 2023-02-21 22:06:47 +01:00
helgeerbe
30440472f7 fix lint errors 2023-02-20 21:04:43 +01:00
helgeerbe
f973de4ab6 aktivate prometheus interface 2023-02-20 21:00:07 +01:00
helgeerbe
f560f25302 initial merge of power_limiter * missing is inverter and channel setting in gui
* due to bug _webApiPrometheus.init is commented out
2023-02-20 15:56:02 +01:00
helgeerbe
09942e8e18 Connect Wifi to strongest AP
* do full channel scan
  * connect to strongest AP
2023-02-20 09:52:40 +01:00
helgeerbe
cb7874ac8d remove serial ports from platformio
* use auto detect
2023-02-20 09:43:31 +01:00
helgeerbe
ce752f0d75 Dont print that VeDirect data is unvalid 2023-02-17 15:08:31 +01:00
helgeerbe
6e0ae6d152 Extent JSON_BUFFER_SIZE 7000 2023-02-17 15:06:43 +01:00
helgeerbe
27f20a76f0 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-02-16 12:35:18 +01:00
helgeerbe
1a4a8cc921 fix age critical 2023-02-16 12:24:11 +01:00
helgeerbe
dc44cc8e1f create VeDirect.isDataValid() 2023-02-16 12:17:47 +01:00
helgeerbe
0ac529146e VedirectView becomes component
* move Vedirect in the component folder
* VedirectView is now component of BasePage
* /vedirectlivedata connectable through vue proxy
2023-02-15 15:01:59 +01:00
helgeerbe
a529c91254 Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development 2023-02-14 10:10:10 +01:00
helgeerbe
7022ed95b6 copy vedirect data if send only
updated data is true
2023-02-14 09:54:40 +01:00
helgeerbe
43bfee4d55 Stopp publishing vedirect if data becomes too old 2023-02-13 14:01:10 +01:00
helgeerbe
957bc91828 Force hass discovery update for vedirect
if mqtt settings changes
2023-02-13 12:00:25 +01:00
helgeerbe
17e564a094 simplify main for vedirect 2023-02-13 11:41:51 +01:00
helgeerbe
f89ccdd2ee
Update README.md 2023-02-08 16:27:44 +01:00
helgeerbe
e5fa0050cd
Update README.md 2023-02-08 16:06:13 +01:00
helgeerbe
ed12f814dd mqqt hass discovery complete 2023-02-08 14:09:16 +01:00
helgeerbe
997023e52f Add Hass sensors Battery voltage and Current 2023-02-07 16:51:30 +01:00
helgeerbe
4f0a45c902 Add serial number to deviced name 2023-02-07 13:59:03 +01:00
helgeerbe
82ecf6cd6d Add victron serial to mqtt topics 2023-02-07 13:36:37 +01:00
helgeerbe
37d3bb0eb0 vedirect hass autodiscovery:
load output state as binary sensor
2023-02-06 17:35:01 +01:00
helgeerbe
1c01e927f9 Add victron pins to device manager 2023-02-02 23:01:22 +01:00
helgeerbe
9e79f02787 a 2023-02-02 21:00:26 +01:00
helgeerbe
9e70d2dfc6 Merge remote-tracking branch 'tbnobody/OpenDTU/master' 2023-02-02 20:58:06 +01:00
helgeerbe
bec6c20531 Merge remote-tracking branch 'tbnobody/OpenDTU/master' 2023-01-26 10:30:55 +01:00
helgeerbe
9a7a0d293e Merge remote-tracking branch 'tbnobody/OpenDTU/master' 2023-01-20 14:54:04 +01:00
helgeerbe
ddb6346087 Merge remote-tracking branch 'tbnobody/OpenDTU/master' 2023-01-03 10:51:56 +01:00
helgeerbe
035251c04c Ui text for vedirect 2022-12-28 14:26:07 +01:00
helgeerbe
e49bbe0faf Merge remote-tracking branch 'tbnobody/OpenDTU/master' 2022-12-27 14:17:24 +01:00
helgeerbe
df5cde2e82 Merge remote-tracking branch 'tbnobody/OpenDTU/master' 2022-12-05 11:46:31 +01:00
helgeerbe
a213082c4d add webapp 2022-11-26 17:49:54 +01:00
helgeerbe
f740dceb78 removed unused isLoggedIn function 2022-11-26 17:49:00 +01:00
helgeerbe
0a0cb9905e live view - change order of mppt columns 2022-11-26 17:40:14 +01:00
helgeerbe
ae414c42c1 merge with remote master 2022-11-26 16:31:41 +01:00
helgeerbe
2109520bde Merge remote-tracking branch 'tbnobody/OpenDTU/master' 2022-11-26 16:31:16 +01:00
helgeerbe
1fc0e76c41 Merge remote-tracking branch 'tbnobody/OpenDTU/master' 2022-11-22 17:10:53 +01:00
helgeerbe
2c6dff3714 Password protection for vedirect settings API 2022-11-19 14:34:59 +01:00
helgeerbe
f35395e76f Merge remote-tracking branch 'tbnobody/OpenDTU/master' 2022-11-16 16:39:01 +01:00
helgeerbe
407e0ee8c1 vedirect vue code follows master 2022-11-03 19:07:57 +01:00
helgeerbe
b3295f5f33 Merge remote-tracking branch 'tbnobody/OpenDTU/master' 2022-11-03 09:27:56 +01:00
helgeerbe
6b4129c400 Merge remote-tracking branch 'tbnobody/OpenDTU/master' 2022-10-31 14:37:44 +01:00
helgeerbe
a6d734018a Merge remote-tracking branch 'tbnobody/OpenDTU/master' 2022-10-26 14:20:44 +02:00
helgeerbe
61fd54b026 png of component diagram 2022-10-26 13:09:40 +02:00
helgeerbe
38e159d5e6 add missing icons to component diagram 2022-10-26 12:56:20 +02:00
helgeerbe
cbc64936e6 components as png 2022-10-26 12:41:26 +02:00
helgeerbe
8a5d027374 add component diagram 2022-10-26 12:34:04 +02:00
helgeerbe
596f1f1183 webapp 2022-10-20 23:39:17 +02:00
helgeerbe
a9336968c7 Merge remote-tracking branch 'tbnobody/OpenDTU/master' 2022-10-20 23:39:05 +02:00
helgeerbe
48ef4c6c04 vedirect live view without button 2022-10-20 23:33:01 +02:00
helgeerbe
cd219e4fa8 vedirect with button 2022-10-20 23:10:07 +02:00
helgeerbe
3617e9a260 add vedirect to config import / export 2022-10-20 16:59:23 +02:00
helgeerbe
732ab3e5c6 remove button in live view ve.direct 2022-10-20 16:39:59 +02:00
helgeerbe
c3df1e7328 Changes to keep ve.direct working 2022-10-20 13:35:14 +02:00
helgeerbe
905dc359a5 Merge remote-tracking branch 'tbnobody/OpenDTU/master' 2022-10-20 13:34:31 +02:00
helgeerbe
d5740e4851 clear tmp ve.direct buffer on checksum error 2022-10-17 14:58:33 +02:00
helgeerbe
9f2bbe3c03 webapp 2022-10-17 10:10:53 +02:00
helgeerbe
5de35ee353 Merge remote-tracking branch 'tbnobody/OpenDTU/master' 2022-10-17 10:10:34 +02:00
helgeerbe
c8b9288f1c webapp 2022-10-10 11:08:50 +02:00
helgeerbe
3febc28c78 Merge remote-tracking branch
'tbnobody/OpenDTU/master'
2022-10-10 11:08:36 +02:00
helgeerbe
366e7dc409 Merge remote-tracking branch
'tbnobody/OpenDTU/master'
2022-10-07 10:32:51 +02:00
helgeerbe
25285b10ee fix cpplint errors 2022-10-04 12:00:29 +02:00
helgeerbe
ba3183e10b Clear map for recieved key, value pairs 2022-10-04 11:34:30 +02:00
helgeerbe
9c71652b2c weapp 2022-10-03 13:34:31 +02:00
helgeerbe
fb638cba43 type 2022-10-03 13:34:16 +02:00
helgeerbe
df7c821bd4 Merge remote-tracking branch 'tbnobody/OpenDTU/master' 2022-10-03 13:34:05 +02:00
helgeerbe
6587282ac2 Fix typo in victron error message 2022-09-30 10:40:41 +02:00
helgeerbe
4b02426ab2 Merge remote-tracking branch 'tbnobody/OpenDTU/master' 2022-09-26 09:38:14 +02:00
helgeerbe
3843a46de9 Merge remote-tracking branch 'tbnobody/OpenDTU/master' 2022-09-22 09:36:36 +02:00
helgeerbe
e6b2be6fdf Change WebSocketMessageBuffer to String 2022-09-19 10:58:56 +02:00
helgeerbe
bee600bfd8 Merge remote-tracking branch 'tbnobody/OpenDTU/master' 2022-09-19 10:49:41 +02:00
helgeerbe
5a25ce50db Merge remote-tracking branch 'tbnobody/OpenDTU/master' 2022-09-06 10:14:18 +02:00
helgeerbe
41eb94e5d9 Merge remote-tracking branch 'tbnobody/OpenDTU/master' 2022-09-01 10:09:50 +02:00
helgeerbe
f8e7db1ba7
Update README.md 2022-08-29 16:32:22 +02:00
helgeerbe
b21990e1d1
Update README.md 2022-08-29 16:29:30 +02:00
helgeerbe
12fc34d4de add victrons rx, tx pins 2022-08-29 16:19:47 +02:00
helgeerbe
723fc96ec5 Merge remote-tracking branch 'tbnobbuody/OpenDTU/master' 2022-08-29 15:55:52 +02:00
helgeerbe
3e4e1bcea4 Merge remote-tracking branch 'tbnobody/OpenDTU/master' 2022-08-24 15:59:21 +02:00
helgeerbe
ece592287c Add ve.direct live view 2022-08-23 22:55:47 +02:00
helgeerbe
63ddbed359 ve.direct as jason array 2022-08-23 14:42:31 +02:00
helgeerbe
044931c08e Add LOAD to rest api 2022-08-20 17:18:07 +02:00
helgeerbe
48e5b567cb frame handler with string and map 2022-08-20 17:06:56 +02:00
helgeerbe
7140574c37 sk 2022-08-19 15:53:18 +02:00
helgeerbe
b8ffa37e97 end poll latest at 500ms 2022-08-17 20:25:46 +02:00
helgeerbe
17abb57ed6 Stop polling after one successful frame read 2022-08-17 18:39:22 +02:00
helgeerbe
f78561cef7 remove # from serial 2022-08-17 16:25:10 +02:00
helgeerbe
82c5fbcf46 fix order in json 2022-08-17 13:41:19 +02:00
helgeerbe
0d08c6a136 Check poll intervall 2022-08-17 13:40:38 +02:00
helgeerbe
6bdc1c5d3f update weapp after merger from master 2022-08-17 12:27:41 +02:00
helgeerbe
865c9cdac5 Merge branch 'master' into ve.direct 2022-08-17 12:25:07 +02:00
helgeerbe
25094ae5b6 last update is now set after poll full frame 2022-08-17 12:15:12 +02:00
helgeerbe
d9bf0ab2e9 ve.direct:
- add poll rate
- add data_age and age_critical to rest api
2022-08-16 16:26:08 +02:00
helgeerbe
72c0e8579a ve.direct rest api 2022-08-16 14:02:19 +02:00
helgeerbe
2ff8f84387 loop für ve.direct 2022-08-15 10:56:37 +02:00
helgeerbe
70136e20aa yield 100ms 2022-08-11 20:40:52 +02:00
helgeerbe
47dda553d9 vue changes 2022-08-11 17:33:38 +02:00
helgeerbe
0eeafe69d9 Merge branch 've.direct' of
https://github.com/helgeerbe/OpenDTU into ve.direct
2022-08-11 17:31:58 +02:00
helgeerbe
e43a45b979 Timeout for HardwareSerial 500ms 2022-08-11 17:26:00 +02:00
helgeerbe
c889efef7c vue changes 2022-08-11 17:26:00 +02:00
helgeerbe
c7c1506e42 First version 2022-08-11 17:26:00 +02:00
helgeerbe
9142dd9930 vue changes 2022-08-11 10:41:43 +02:00
helgeerbe
6a802d607c Merge branch 've.direct' of https://github.com/helgeerbe/OpenDTU into ve.direct 2022-08-11 10:38:41 +02:00
helgeerbe
5aa27a8dc8 Timeout for HardwareSerial 500ms 2022-08-11 10:35:20 +02:00
helgeerbe
a4752e1379 vue changes 2022-08-11 10:35:20 +02:00
helgeerbe
15c1717290 First version 2022-08-11 10:34:41 +02:00
helgeerbe
0793856259 Timeout for HardwareSerial 500ms 2022-08-11 09:42:36 +02:00
helgeerbe
6ca3fb61ce vue changes 2022-08-08 16:11:36 +02:00
helgeerbe
0e4edc9571 First version 2022-08-08 11:16:06 +02:00
287 changed files with 23574 additions and 401 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

View File

@ -4,19 +4,19 @@ labels: ["bug"]
body: body:
- type: markdown - type: markdown
attributes: attributes:
value: > value: |
### ⚠️ Please remember: issues are for *bugs* ### ⚠️ Please remember: issues are for *bugs*⚠️
That is, something you believe affects every single user of OpenDTU, not just you. If you're not sure, start with one of the other options below. That is, something you believe affects every single user of OpenDTU-OnBattery, not just you. If you're not sure, start with one of the other options below.
- type: markdown - type: markdown
attributes: attributes:
value: | value: |
#### Have a question? 👉 [Start a new discussion](https://github.com/tbnobody/OpenDTU/discussions/new) or [ask in chat](https://discord.gg/WzhxEY62mB). #### Have a question? 👉 [Start a new discussion](https://github.com/helgeerbe/OpenDTU-OnBattery/discussions/new/choose) or [ask in chat](https://discord.gg/WzhxEY62mB).
#### Before opening an issue, please double check: #### Before opening an issue, please double check:
- [Documentation](https://www.opendtu.solar). - [Documentation](https://opendtu-onbattery.net)
- [The FAQs](https://www.opendtu.solar/firmware/faq/). - [The FAQs](https://opendtu-onbattery.net/firmware/faq/)
- [Existing issues and discussions](https://github.com/tbnobody/OpenDTU/search?q=&type=issues). - [Existing issues and discussions](https://github.com/helgeerbe/OpenDTU-OnBattery/search?q=&type=issues)
- type: textarea - type: textarea
id: what-happened id: what-happened
attributes: attributes:
@ -45,7 +45,7 @@ body:
id: install_format id: install_format
attributes: attributes:
label: Install Method label: Install Method
description: How did you install OpenDTU? description: How did you install OpenDTU-OnBattery?
options: options:
- Pre-Compiled binary from GitHub releases - Pre-Compiled binary from GitHub releases
- Pre-Compiled binary from GitHub actions/pull-request - Pre-Compiled binary from GitHub actions/pull-request
@ -55,17 +55,25 @@ body:
- type: input - type: input
id: version id: version
attributes: attributes:
label: What git-hash/version of OpenDTU? label: What git-hash/version of OpenDTU-OnBattery?
description: You can find this in by going to Info -> System description: You can find this in the Web UI at Info -> System.
placeholder: "e.g. 359d513" placeholder: "e.g. 359d513"
validations: validations:
required: true required: true
- type: input - type: dropdown
id: environment id: environment
attributes: attributes:
label: What firmware variant (PIO Environment) are you using? label: What firmware variant (PIO Environment)?
description: You can find this in by going to Info -> System description: You can find this in the Web UI at Info -> System.
placeholder: "generic_esp32s3_usb" options:
- "generic_esp32s3_usb"
- "generic_esp32s3"
- "generic_esp32_8mb"
- "generic_esp32_4mb_no_ota"
- "generic_esp32"
- "generic"
- "opendtufusionv2"
- "other (tell us in 'Anything else?')"
validations: validations:
required: true required: true
- type: textarea - type: textarea
@ -87,7 +95,7 @@ body:
attributes: attributes:
label: Please confirm the following label: Please confirm the following
options: options:
- label: I believe this issue is a bug that affects all users of OpenDTU, not something specific to my installation. - label: I believe this issue is a bug that affects all users of OpenDTU-OnBattery, not something specific to my installation.
required: true required: true
- label: I have already searched for relevant existing issues and discussions before opening this report. - label: I have already searched for relevant existing issues and discussions before opening this report.
required: true required: true

View File

@ -4,5 +4,5 @@ contact_links:
url: https://discord.gg/WzhxEY62mB url: https://discord.gg/WzhxEY62mB
about: Discuss with us on Discord about: Discuss with us on Discord
- name: 🤔 Have questions or need support? - name: 🤔 Have questions or need support?
url: https://github.com/tbnobody/OpenDTU/discussions url: https://github.com/helgeerbe/OpenDTU-OnBattery/discussions
about: Use the GitHub Discussions feature about: Use the GitHub Discussions feature

View File

@ -1,12 +1,12 @@
name: ✨ Request a feature name: ✨ Request a feature
description: Suggest an improvement idea for OpenDTU! description: Suggest an improvement idea for OpenDTU-OnBattery!
title: "[Request]" title: "[Request]"
labels: ["enhancement"] labels: ["enhancement"]
body: body:
- type: markdown - type: markdown
attributes: attributes:
value: > value: >
**Thank you for wanting to request a feature in OpenDTU!** **Thank you for wanting to request a feature in OpenDTU-OnBattery!**
Before you go ahead with your request, please first consider if it wouldn't be Before you go ahead with your request, please first consider if it wouldn't be
better suited in a external home automation software like OpenHAB, ioBroker, Home Assistant etc. better suited in a external home automation software like OpenHAB, ioBroker, Home Assistant etc.

View File

@ -1,10 +1,15 @@
name: OpenDTU Build name: OpenDTU-OnBattery Build
on: on:
push: push:
paths-ignore: paths-ignore:
- docs/** - docs/**
- '**/*.md' - '**/*.md'
branches:
- master
- development
tags-ignore:
- 'v**'
pull_request: pull_request:
paths-ignore: paths-ignore:
- docs/** - docs/**
@ -55,6 +60,15 @@ jobs:
- name: Get tags - name: Get tags
run: git fetch --force --tags origin run: git fetch --force --tags origin
- name: Create and switch to a meaningful branch for pull-requests
if: github.event_name == 'pull_request'
run: |
OWNER=${{ github.repository_owner }}
NAME=${{ github.event.repository.name }}
ID=${{ github.event.pull_request.number }}
DATE=$(date +'%Y%m%d%H%M')
git switch -c ${OWNER}/${NAME}/pr${ID}-${DATE}
- name: Cache pip - name: Cache pip
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
@ -105,27 +119,48 @@ jobs:
run: pio run -e ${{ matrix.environment }} run: pio run -e ${{ matrix.environment }}
- name: Rename Firmware - name: Rename Firmware
run: mv .pio/build/${{ matrix.environment }}/firmware.bin .pio/build/${{ matrix.environment }}/opendtu-${{ matrix.environment }}.bin run: mv .pio/build/${{ matrix.environment }}/firmware.bin .pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.bin
- name: Rename Factory Firmware - name: Rename Factory Firmware
run: mv .pio/build/${{ matrix.environment }}/firmware.factory.bin .pio/build/${{ matrix.environment }}/opendtu-${{ matrix.environment }}.factory.bin run: mv .pio/build/${{ matrix.environment }}/firmware.factory.bin .pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.factory.bin
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
name: opendtu-${{ matrix.environment }} name: opendtu-onbattery-${{ matrix.environment }}
path: | path: |
.pio/build/${{ matrix.environment }}/opendtu-${{ matrix.environment }}.bin .pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.bin
.pio/build/${{ matrix.environment }}/opendtu-${{ matrix.environment }}.factory.bin !.pio/build/generic_esp32_4mb_no_ota/opendtu-onbattery-generic_esp32_4mb_no_ota.bin
.pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.factory.bin
release: release:
name: Create Release name: Create Release
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [get_default_envs, build] needs: [get_default_envs, build]
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/2')
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Get tags
run: git fetch --force --tags origin
- name: Get openDTU core release
run: |
echo "OPEN_DTU_CORE_RELEASE=$(git for-each-ref --sort=creatordate --format '%(refname) %(creatordate)' refs/tags | grep 'refs/tags/v' | tail -1 | sed 's#.*/##' | sed 's/ .*//')" >> $GITHUB_ENV
# disabled as uploading the changed gist failed repeatedly.
# maybe the token in secrets.GIST_SECRET has expired?
# need help from repo owner @helgeerbe to fix this.
# - name: Create openDTU-core-release-Badge
# uses: schneegans/dynamic-badges-action@v1.6.0
# with:
# auth: ${{ secrets.GIST_SECRET }}
# gistID: 68b47cc8c8994d04ab3a4fa9d8aee5e6
# filename: openDTUcoreRelease.json
# label: based on original OpenDTU
# message: ${{ env.OPEN_DTU_CORE_RELEASE }}
# color: lightblue
- name: Build Changelog - name: Build Changelog
id: github_release id: github_release
uses: mikepenz/release-changelog-builder-action@v4 uses: mikepenz/release-changelog-builder-action@v4
@ -144,7 +179,7 @@ jobs:
run: | run: |
ls -R ls -R
cd artifacts cd artifacts
for i in */; do cp ${i}opendtu-*.bin ./; done for i in */; do cp ${i}opendtu-onbattery-*.bin ./; done
- name: Create release - name: Create release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2
@ -154,4 +189,4 @@ jobs:
files: | files: |
artifacts/*.zip, artifacts/*.bin artifacts/*.zip, artifacts/*.bin
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}

View File

@ -36,7 +36,7 @@
} }
], ],
"template": "${{CHANGELOG}}", "template": "${{CHANGELOG}}",
"pr_template": "- [${{TITLE}}](https://github.com/tbnobody/OpenDTU/commit/${{MERGE_SHA}})", "pr_template": "- [${{TITLE}}](https://github.com/helgeerbe/OpenDTU-OnBattery/commit/${{MERGE_SHA}})",
"empty_template": "- no changes", "empty_template": "- no changes",
"label_extractor": [ "label_extractor": [
{ {

View File

@ -6,6 +6,10 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
# prevent push event from triggering if it's part of a local PR, see
# https://github.com/orgs/community/discussions/57827#discussioncomment-6579237
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Set up Python - name: Set up Python
@ -18,4 +22,6 @@ jobs:
pip install cpplint pip install cpplint
- name: Linting - name: Linting
run: | run: |
cpplint --repository=. --recursive --filter=-build/c++11,-runtime/references,-readability/braces,-whitespace,-legal,-build/include ./src ./include ./lib/Hoymiles ./lib/MqttSubscribeParser ./lib/TimeoutHelper ./lib/ResetReason cpplint --repository=. --recursive \
--filter=-build/c++11,-runtime/references,-readability/braces,-whitespace,-legal,-build/include \
./src ./include ./lib/Hoymiles ./lib/MqttSubscribeParser ./lib/TimeoutHelper ./lib/ResetReason

View File

@ -6,6 +6,10 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
# prevent push event from triggering if it's part of a local PR, see
# https://github.com/orgs/community/discussions/57827#discussioncomment-6579237
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
defaults: defaults:
run: run:
working-directory: webapp working-directory: webapp

View File

@ -6,6 +6,10 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
# prevent push event from triggering if it's part of a local PR, see
# https://github.com/orgs/community/discussions/57827#discussioncomment-6579237
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
defaults: defaults:
run: run:
working-directory: webapp working-directory: webapp

1
.gitignore vendored
View File

@ -7,4 +7,5 @@
platformio-device-monitor*.log platformio-device-monitor*.log
logs/device-monitor*.log logs/device-monitor*.log
platformio_override.ini platformio_override.ini
webapp_dist/
.DS_Store .DS_Store

62
.vscode/settings.json vendored
View File

@ -1,3 +1,63 @@
{ {
"C_Cpp.clang_format_style": "WebKit" "C_Cpp.clang_format_style": "WebKit",
"files.associations": {
"*.tcc": "cpp",
"algorithm": "cpp",
"array": "cpp",
"atomic": "cpp",
"bitset": "cpp",
"cctype": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"condition_variable": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"list": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"vector": "cpp",
"exception": "cpp",
"functional": "cpp",
"iterator": "cpp",
"map": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"optional": "cpp",
"random": "cpp",
"ratio": "cpp",
"regex": "cpp",
"string": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"fstream": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"mutex": "cpp",
"new": "cpp",
"ostream": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"thread": "cpp",
"cinttypes": "cpp",
"typeinfo": "cpp",
"variant": "cpp"
}
} }

110
README.md
View File

@ -1,43 +1,93 @@
# OpenDTU [![OpenDTU-OnBattery Build](https://github.com/hoylabs/OpenDTU-OnBattery/actions/workflows/build.yml/badge.svg)](https://github.com/hoylabs/OpenDTU-OnBattery/actions/workflows/build.yml)
[![cpplint](https://github.com/hoylabs/OpenDTU-OnBattery/actions/workflows/cpplint.yml/badge.svg)](https://github.com/hoylabs/OpenDTU-OnBattery/actions/workflows/cpplint.yml)
[![Yarn Linting](https://github.com/hoylabs/OpenDTU-OnBattery/actions/workflows/yarnlint.yml/badge.svg)](https://github.com/hoylabs/OpenDTU-OnBattery/actions/workflows/yarnlint.yml)
<!---
disabled while "create release badge" action is broken, see .github/build.yml
![GitHub tag (latest SemVer)](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/helgeerbe/68b47cc8c8994d04ab3a4fa9d8aee5e6/raw/openDTUcoreRelease.json)
--->
[![OpenDTU Build](https://github.com/tbnobody/OpenDTU/actions/workflows/build.yml/badge.svg)](https://github.com/tbnobody/OpenDTU/actions/workflows/build.yml) - [OpenDTU-OnBattery](#opendtu-onbattery)
[![cpplint](https://github.com/tbnobody/OpenDTU/actions/workflows/cpplint.yml/badge.svg)](https://github.com/tbnobody/OpenDTU/actions/workflows/cpplint.yml) - [Getting Started](#getting-started)
[![Yarn Linting](https://github.com/tbnobody/OpenDTU/actions/workflows/yarnlint.yml/badge.svg)](https://github.com/tbnobody/OpenDTU/actions/workflows/yarnlint.yml) - [Important Differences](#important-differences)
- [Documentation](#documentation)
- [Project State](#project-state)
- [Project History](#project-history)
- [Acknowledgments](#acknowledgments)
## !! IMPORTANT UPGRADE NOTES !! # OpenDTU-OnBattery
If you are upgrading from a version before 15.03.2023 you have to upgrade the partition table of the ESP32. Please follow the [this](docs/UpgradePartition.md) documentation! OpenDTU-OnBattery is a fork of [OpenDTU](https://github.com/tbnobody/OpenDTU),
which adds support for battery chargers, battery management systems (BMS), and
power meters on a single ESP32. Its Dynamic Power Limiter can adjust the
inverter's power production to the actual houshold consumption. In this way, it
is possible to implement a zero export policy.
## Background ## Getting Started
This project was started from [this](https://www.mikrocontroller.net/topic/525778) discussion (Mikrocontroller.net). See the documentation to learn [what hardware](https://opendtu-onbattery.net/hardware/)
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. to acquire, how to [initialize](https://opendtu-onbattery.net/firmware/) it
with OpenDTU-OnBattery firmware, and how to
[configure](https://opendtu-onbattery.net/firmware/device_profiles/)
OpenDTU-OnBattery for your hardware.
## Important Differences
Generally speaking, OpenDTU-OnBattery and the upstream project are compatible
with each other, because OpenDTU-OnBattery mostly only extends the upstream
project. However, there are a few notable differences aside from the added functionality:
* OpenDTU-OnBattery, due to its code footprint, cannot offer support for
over-the-air (OTA) updates on ESP32 with only 4MB of flash memory. Consult
the [documentation](https://opendtu-onbattery.net/firmware/howto/upgrade_8mb/#background)
to learn more.
* Unlike in the upstream project, you **must** compile the web application
yourself when attempting to build your own firmware blob. See the
[documentation](https://opendtu-onbattery.net/firmware/compile_webapp/) for
details.
## Documentation ## Documentation
The documentation can be found [here](https://tbnobody.github.io/OpenDTU-docs/). The canonical documentation of OpenDTU-OnBattery is hosted at
Please feel free to support and create a PR in [this](https://github.com/tbnobody/OpenDTU-docs) repository to make the documentation even better. [https://opendtu-onbattery.net](https://opendtu-onbattery.net).
## Breaking changes You may find additional helpful information in the project's
community-maintained [Github
Wiki](https://github.com/hoylabs/OpenDTU-OnBattery/wiki).
Generated using: `git log --date=short --pretty=format:"* %h%x09%ad%x09%s" | grep BREAKING` To find out what's new or improved have a look at the
[releases](https://github.com/hoylabs/OpenDTU-OnBattery/releases).
```code ## Project State
* 1b637f08 2024-01-30 BREAKING CHANGE: Web API Endpoint /api/livedata/status and /api/prometheus/metrics
* e1564780 2024-01-30 BREAKING CHANGE: Web API Endpoint /api/livedata/status and /api/prometheus/metrics
* f0b5542c 2024-01-30 BREAKING CHANGE: Web API Endpoint /api/livedata/status and /api/prometheus/metrics
* c27ecc36 2024-01-29 BREAKING CHANGE: Web API Endpoint /api/livedata/status
* 71d1b3b 2023-11-07 BREAKING CHANGE: Home Assistant Auto Discovery to new naming scheme
* 04f62e0 2023-04-20 BREAKING CHANGE: Web API Endpoint /api/eventlog/status no nested serial object
* 59f43a8 2023-04-17 BREAKING CHANGE: Web API Endpoint /api/devinfo/status requires GET parameter inv=
* 318136d 2023-03-15 BREAKING CHANGE: Updated partition table: Make sure you have a configuration backup and completly reflash the device!
* 3b7aef6 2023-02-13 BREAKING CHANGE: Web API!
* d4c838a 2023-02-06 BREAKING CHANGE: Prometheus API!
* daf847e 2022-11-14 BREAKING CHANGE: Removed deprecated config parsing method
* 69b675b 2022-11-01 BREAKING CHANGE: Structure WebAPI /api/livedata/status changed
* 27ed4e3 2022-10-31 BREAKING: Change power factor from percent value to value between 0 and 1
```
## Currently supported Inverters OpenDTU-OnBattery is actively maintained. Please note that OpenDTU-OnBattery
may change significantly during its development. Bug reports, comments, feature
requests and pull requests are welcome!
A list of all currently supported inverters can be found [here](https://www.opendtu.solar/hardware/inverter_overview/) ## Project History
The original OpenDTU project was started from [a discussion on
Mikrocontroller.net](https://www.mikrocontroller.net/topic/525778). The
original ambition was to replace the original Hoymiles DTU (Telemetry Gateway)
to avoid using Hoymile's cloud. With a lot of reverse engineering, the Hoymiles
protocol was decrypted and analyzed.
In the summer of 2022 [@helgeerbe](https://github.com/helgeerbe) bought a
Victron MPPT charge controller, and didn't like the idea to set up a separate
ESP32 to receive the charger's data. He decided to fork OpenDTU and extend it
with battery charger support and a Dynamic Power Limiter.
In early October 2024, the project moved to the newly founded GitHub
organisation `hoylabs` and is since maintained by multiple community members.
## Acknowledgments
* Special thanks to Thomas Basler ([@tbnobody](https://github.com/tbnobody)),
the author of the [upstream project](https://github.com/tbnobody/OpenDTU),
for his continued effort!
* Thanks to [@helgeerbe](https://github.com/helgeerbe) for starting
OpenDTU-OnBattery, for his dedication to the project, as well as for his
trust in the current maintainers of the project, which act as part of the
`hoylabs` GitHub organisation.
* We like to thank all contributors. With your ideas and enhancements, you have
made OpenDTU-OnBattery much more than
[@helgeerbe](https://github.com/helgeerbe) originally had in mind.

3
README_onBattery.md Normal file
View File

@ -0,0 +1,3 @@
# Moved
Have a look at the [OpenDTU-OnBattery documentation](https://opendtu-onbattery.net).

View File

@ -1,3 +1,3 @@
# Device Profiles # Moved
This documentation will has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/firmware/device_profiles/> Have a look at the [OpenDTU-OnBattery documentation](https://opendtu-onbattery.net/firmware/device_profiles/).

View File

@ -1,3 +1,3 @@
# Display integration # Moved
This documentation will has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/hardware/display/> Have a look at the [OpenDTU-OnBattery documentation](https://opendtu-onbattery.net/hardware/display/).

View File

@ -1,3 +1,3 @@
# MQTT Topics # Moved
This documentation will has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/firmware/mqtt_topics/> Have a look at the [OpenDTU-OnBattery documentation](https://opendtu-onbattery.net/firmware/mqtt_topics/).

View File

@ -1,13 +1,3 @@
# Documents - Table of content # Moved
More detailed descriptions for some topics can be found here. Have a look at the [OpenDTU-OnBattery documentation](https://opendtu-onbattery.net).
## [Display Documentation](Display.md)
## [MQTT Topic Documentation](MQTT_Topics.md)
## [Web API Documentation](Web-API.md)
## [Device Profile Documentation](DeviceProfiles.md)
## [Builds](builds/README.md)

View File

@ -1,3 +1,3 @@
# Upgrade Partition # Moved
This documentation has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/firmware/howto/upgrade_partition/> Have a look at the [OpenDTU-OnBattery documentation](https://opendtu-onbattery.net/firmware/howto/upgrade_8mb/).

View File

@ -1,3 +1,3 @@
# Web API # Moved
This documentation will has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/firmware/web_api/> Have a look at the [OpenDTU-OnBattery documentation](https://opendtu-onbattery.net/firmware/web_api/).

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

View File

@ -1,28 +0,0 @@
# Builds using different boards
## ESP32 Dev Board
### Build by @tbnobody, jan and @marove2000
* Used build environment: generic
* Case: https://www.printables.com/de/model/441037-opendtu-breakoutboard-case
* Soldering Kit: https://shop.blinkyparts.com/en/OpenDTU-Breakoutboard-Your-evaluation-for-your-balcony-solar-system/blink237542
* Breakout board: https://github.com/marove2000/openDTU_BreakoutBoard
![](opendtu_breakoutboard.jpg)
![](thumbnail.jpg)
### Build by @Marc--
* Used build environment: generic
* Case: https://www.thingiverse.com/thing:5435911
![](large_display_PXL_20220715_145622277.jpg)
### Build by @cepresso
* Used build environment: generic
* Case: https://www.printables.com/de/model/293003-sol-opendtu-esp32-nrf24l01-case
![](sol.webp)
## LILYGO® TTGO T-Internet-POE
### Build by @fromCologne
* Used build environment: LilyGO_T_ETH_POE
* Board info: http://www.lilygo.cn/claprod_view.aspx?TypeId=21&Id=1344&FId=t28:21:28
* Case: https://www.thingiverse.com/thing:5244895
![](202654506-8a4ac4ef-c883-490e-8ee1-1e1f7fa34972.jpg)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 KiB

3
docs/hardware_flash.md Normal file
View File

@ -0,0 +1,3 @@
# Moved
Have a look at the [OpenDTU-OnBattery documentation](https://opendtu-onbattery.net/firmware/).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

View File

@ -1,91 +0,0 @@
# OpenDTU Screenshots
here are some screenshots of OpenDTU's web interface.
***
![Live View](01_LiveView.png)
***
![Limit Settings](15_LimitSettings.png)
***
![Power Settings](16_PowerSettings.png)
***
![Inverter Info](17_InverterInfo.png)
***
![Eventlog](12_Eventlog.png)
***
![Network Admin](02_NetworkAdmin.png)
***
![NTP Admin](03_NtpAdmin.png)
***
![MQTT Admin](04_MqttAdmin.png)
***
![Inverter Admin](05_InverterAdmin.png)
***
![Inverter Settings](13_InverterSettings.png)
***
![Security](22_Security.png)
***
![DTU Admin](06_DtuAdmin.png)
***
![Device Manager Pin](20_DeviceManager_Pin.png)
***
![Device Manager Display](21_DeviceManager_Display.png)
***
![Config Management](14_ConfigManagement.png)
***
![Firmware Upgrade](07_FirmwareUpgrade.png)
***
![Reboot](19_Reboot.png)
***
![System Info](11_SystemInfo.png)
***
![Network Info](08_NetworkInfo.png)
***
![NTP Info](09_NtpInfo.png)
***
![MQTT Info](10_MqttInfo.png)
***
![Console](18_Console.png)

36
include/Battery.h Normal file
View File

@ -0,0 +1,36 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <mutex>
#include <TaskSchedulerDeclarations.h>
#include "BatteryStats.h"
class BatteryProvider {
public:
// returns true if the provider is ready for use, false otherwise
virtual bool init(bool verboseLogging) = 0;
virtual void deinit() = 0;
virtual void loop() = 0;
virtual std::shared_ptr<BatteryStats> getStats() const = 0;
};
class BatteryClass {
public:
void init(Scheduler&);
void updateSettings();
float getDischargeCurrentLimit();
std::shared_ptr<BatteryStats const> getStats() const;
private:
void loop();
Task _loopTask;
mutable std::mutex _mutex;
std::unique_ptr<BatteryProvider> _upProvider = nullptr;
};
extern BatteryClass Battery;

View File

@ -0,0 +1,29 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Battery.h"
#include <driver/twai.h>
#include <Arduino.h>
class BatteryCanReceiver : public BatteryProvider {
public:
bool init(bool verboseLogging, char const* providerName);
void deinit() final;
void loop() final;
virtual void onMessage(twai_message_t rx_message) = 0;
protected:
uint8_t readUnsignedInt8(uint8_t *data);
uint16_t readUnsignedInt16(uint8_t *data);
int16_t readSignedInt16(uint8_t *data);
uint32_t readUnsignedInt32(uint8_t *data);
int32_t readSignedInt24(uint8_t *data);
float scaleValue(int32_t value, float factor);
bool getBit(uint8_t value, uint8_t bit);
bool _verboseLogging = true;
private:
char const* _providerName = "Battery CAN";
};

354
include/BatteryStats.h Normal file
View File

@ -0,0 +1,354 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <stdint.h>
#include "AsyncJson.h"
#include "Arduino.h"
#include "JkBmsDataPoints.h"
#include "JbdBmsDataPoints.h"
#include "VeDirectShuntController.h"
#include <cfloat>
// mandatory interface for all kinds of batteries
class BatteryStats {
public:
String const& getManufacturer() const { return _manufacturer; }
// the last time *any* data was updated
uint32_t getAgeSeconds() const { return (millis() - _lastUpdate) / 1000; }
bool updateAvailable(uint32_t since) const;
float getSoC() const { return _soc; }
uint32_t getSoCAgeSeconds() const { return (millis() - _lastUpdateSoC) / 1000; }
uint8_t getSoCPrecision() const { return _socPrecision; }
float getVoltage() const { return _voltage; }
uint32_t getVoltageAgeSeconds() const { return (millis() - _lastUpdateVoltage) / 1000; }
float getChargeCurrent() const { return _current; };
uint8_t getChargeCurrentPrecision() const { return _currentPrecision; }
float getDischargeCurrentLimit() const { return _dischargeCurrentLimit; };
uint32_t getDischargeCurrentLimitAgeSeconds() const { return (millis() - _lastUpdateDischargeCurrentLimit) / 1000; }
// convert stats to JSON for web application live view
virtual void getLiveViewData(JsonVariant& root) const;
void mqttLoop();
// the interval at which all battery data will be re-published, even
// if they did not change. used to calculate Home Assistent expiration.
virtual uint32_t getMqttFullPublishIntervalMs() const;
bool isSoCValid() const { return _lastUpdateSoC > 0; }
bool isVoltageValid() const { return _lastUpdateVoltage > 0; }
bool isCurrentValid() const { return _lastUpdateCurrent > 0; }
bool isDischargeCurrentLimitValid() const { return _lastUpdateDischargeCurrentLimit > 0; }
// returns true if the battery reached a critically low voltage/SoC,
// such that it is in need of charging to prevent degredation.
virtual bool getImmediateChargingRequest() const { return false; };
virtual float getChargeCurrentLimitation() const { return FLT_MAX; };
virtual bool supportsAlarmsAndWarnings() const { return true; };
protected:
virtual void mqttPublish() const;
void setSoC(float soc, uint8_t precision, uint32_t timestamp) {
_soc = soc;
_socPrecision = precision;
_lastUpdateSoC = _lastUpdate = timestamp;
}
void setVoltage(float voltage, uint32_t timestamp) {
_voltage = voltage;
_lastUpdateVoltage = _lastUpdate = timestamp;
}
void setCurrent(float current, uint8_t precision, uint32_t timestamp) {
_current = current;
_currentPrecision = precision;
_lastUpdateCurrent = _lastUpdate = timestamp;
}
void setDischargeCurrentLimit(float dischargeCurrentLimit, uint32_t timestamp) {
_dischargeCurrentLimit = dischargeCurrentLimit;
_lastUpdateDischargeCurrentLimit = _lastUpdate = timestamp;
}
void setManufacturer(const String& m);
String _hwversion = "";
String _fwversion = "";
String _serial = "";
uint32_t _lastUpdate = 0;
private:
String _manufacturer = "unknown";
uint32_t _lastMqttPublish = 0;
float _soc = 0;
uint8_t _socPrecision = 0; // decimal places
uint32_t _lastUpdateSoC = 0;
float _voltage = 0; // total battery pack voltage
uint32_t _lastUpdateVoltage = 0;
// total current into (positive) or from (negative)
// the battery, i.e., the charging current
float _current = 0;
uint8_t _currentPrecision = 0; // decimal places
uint32_t _lastUpdateCurrent = 0;
float _dischargeCurrentLimit = 0;
uint32_t _lastUpdateDischargeCurrentLimit = 0;
};
class PylontechBatteryStats : public BatteryStats {
friend class PylontechCanReceiver;
public:
void getLiveViewData(JsonVariant& root) const final;
void mqttPublish() const final;
bool getImmediateChargingRequest() const { return _chargeImmediately; } ;
float getChargeCurrentLimitation() const { return _chargeCurrentLimitation; } ;
private:
void setLastUpdate(uint32_t ts) { _lastUpdate = ts; }
float _chargeVoltage;
float _chargeCurrentLimitation;
float _dischargeVoltageLimitation;
uint16_t _stateOfHealth;
float _temperature;
bool _alarmOverCurrentDischarge;
bool _alarmOverCurrentCharge;
bool _alarmUnderTemperature;
bool _alarmOverTemperature;
bool _alarmUnderVoltage;
bool _alarmOverVoltage;
bool _alarmBmsInternal;
bool _warningHighCurrentDischarge;
bool _warningHighCurrentCharge;
bool _warningLowTemperature;
bool _warningHighTemperature;
bool _warningLowVoltage;
bool _warningHighVoltage;
bool _warningBmsInternal;
bool _chargeEnabled;
bool _dischargeEnabled;
bool _chargeImmediately;
uint8_t _moduleCount;
};
class SBSBatteryStats : public BatteryStats {
friend class SBSCanReceiver;
public:
void getLiveViewData(JsonVariant& root) const final;
void mqttPublish() const final;
float getChargeCurrent() const { return _current; } ;
float getChargeCurrentLimitation() const { return _chargeCurrentLimitation; } ;
private:
void setLastUpdate(uint32_t ts) { _lastUpdate = ts; }
float _chargeVoltage;
float _chargeCurrentLimitation;
uint16_t _stateOfHealth;
float _current;
float _temperature;
bool _alarmUnderTemperature;
bool _alarmOverTemperature;
bool _alarmUnderVoltage;
bool _alarmOverVoltage;
bool _alarmBmsInternal;
bool _warningHighCurrentDischarge;
bool _warningHighCurrentCharge;
bool _chargeEnabled;
bool _dischargeEnabled;
};
class PytesBatteryStats : public BatteryStats {
friend class PytesCanReceiver;
public:
void getLiveViewData(JsonVariant& root) const final;
void mqttPublish() const final;
bool getImmediateChargingRequest() const { return _chargeImmediately; };
float getChargeCurrentLimitation() const { return _chargeCurrentLimit; };
private:
void setLastUpdate(uint32_t ts) { _lastUpdate = ts; }
void updateSerial() {
if (!_serialPart1.isEmpty() && !_serialPart2.isEmpty()) {
_serial = _serialPart1 + _serialPart2;
}
}
String _serialPart1 = "";
String _serialPart2 = "";
float _chargeVoltageLimit;
float _chargeCurrentLimit;
float _dischargeVoltageLimit;
uint16_t _stateOfHealth;
int _chargeCycles = -1;
int _balance = -1;
float _temperature;
uint16_t _cellMinMilliVolt;
uint16_t _cellMaxMilliVolt;
float _cellMinTemperature;
float _cellMaxTemperature;
String _cellMinVoltageName;
String _cellMaxVoltageName;
String _cellMinTemperatureName;
String _cellMaxTemperatureName;
uint8_t _moduleCountOnline;
uint8_t _moduleCountOffline;
uint8_t _moduleCountBlockingCharge;
uint8_t _moduleCountBlockingDischarge;
float _totalCapacity;
float _availableCapacity;
uint8_t _capacityPrecision = 0; // decimal places
float _chargedEnergy = -1;
float _dischargedEnergy = -1;
bool _alarmUnderVoltage;
bool _alarmOverVoltage;
bool _alarmOverCurrentCharge;
bool _alarmOverCurrentDischarge;
bool _alarmUnderTemperature;
bool _alarmOverTemperature;
bool _alarmUnderTemperatureCharge;
bool _alarmOverTemperatureCharge;
bool _alarmInternalFailure;
bool _alarmCellImbalance;
bool _warningLowVoltage;
bool _warningHighVoltage;
bool _warningHighChargeCurrent;
bool _warningHighDischargeCurrent;
bool _warningLowTemperature;
bool _warningHighTemperature;
bool _warningLowTemperatureCharge;
bool _warningHighTemperatureCharge;
bool _warningInternalFailure;
bool _warningCellImbalance;
bool _chargeImmediately;
};
class JkBmsBatteryStats : public BatteryStats {
public:
void getLiveViewData(JsonVariant& root) const final {
getJsonData(root, false);
}
void getInfoViewData(JsonVariant& root) const {
getJsonData(root, true);
}
void mqttPublish() const final;
uint32_t getMqttFullPublishIntervalMs() const final { return 60 * 1000; }
void updateFrom(JkBms::DataPointContainer const& dp);
private:
void getJsonData(JsonVariant& root, bool verbose) const;
JkBms::DataPointContainer _dataPoints;
mutable uint32_t _lastMqttPublish = 0;
mutable uint32_t _lastFullMqttPublish = 0;
uint16_t _cellMinMilliVolt = 0;
uint16_t _cellAvgMilliVolt = 0;
uint16_t _cellMaxMilliVolt = 0;
uint32_t _cellVoltageTimestamp = 0;
};
class JbdBmsBatteryStats : public BatteryStats {
public:
void getLiveViewData(JsonVariant& root) const final {
getJsonData(root, false);
}
void getInfoViewData(JsonVariant& root) const {
getJsonData(root, true);
}
void mqttPublish() const final;
uint32_t getMqttFullPublishIntervalMs() const final { return 60 * 1000; }
void updateFrom(JbdBms::DataPointContainer const& dp);
private:
void getJsonData(JsonVariant& root, bool verbose) const;
JbdBms::DataPointContainer _dataPoints;
mutable uint32_t _lastMqttPublish = 0;
mutable uint32_t _lastFullMqttPublish = 0;
uint16_t _cellMinMilliVolt = 0;
uint16_t _cellAvgMilliVolt = 0;
uint16_t _cellMaxMilliVolt = 0;
uint32_t _cellVoltageTimestamp = 0;
};
class VictronSmartShuntStats : public BatteryStats {
public:
void getLiveViewData(JsonVariant& root) const final;
void mqttPublish() const final;
void updateFrom(VeDirectShuntController::data_t const& shuntData);
private:
float _temperature;
bool _tempPresent;
uint8_t _chargeCycles;
uint32_t _timeToGo;
float _chargedEnergy;
float _dischargedEnergy;
int32_t _instantaneousPower;
float _midpointVoltage;
float _midpointDeviation;
float _consumedAmpHours;
int32_t _lastFullCharge;
bool _alarmLowVoltage;
bool _alarmHighVoltage;
bool _alarmLowSOC;
bool _alarmLowTemperature;
bool _alarmHighTemperature;
};
class MqttBatteryStats : public BatteryStats {
friend class MqttBattery;
public:
// since the source of information was MQTT in the first place,
// we do NOT publish the same data under a different topic.
void mqttPublish() const final { }
void getLiveViewData(JsonVariant& root) const final;
bool supportsAlarmsAndWarnings() const final { return false; }
};

View File

@ -3,6 +3,7 @@
#include "PinMapping.h" #include "PinMapping.h"
#include <cstdint> #include <cstdint>
#include <ArduinoJson.h>
#include <TaskSchedulerDeclarations.h> #include <TaskSchedulerDeclarations.h>
#include <mutex> #include <mutex>
#include <condition_variable> #include <condition_variable>
@ -14,6 +15,8 @@
#define WIFI_MAX_PASSWORD_STRLEN 64 #define WIFI_MAX_PASSWORD_STRLEN 64
#define WIFI_MAX_HOSTNAME_STRLEN 31 #define WIFI_MAX_HOSTNAME_STRLEN 31
#define SYSLOG_MAX_HOSTNAME_STRLEN 128
#define NTP_MAX_SERVER_STRLEN 31 #define NTP_MAX_SERVER_STRLEN 31
#define NTP_MAX_TIMEZONE_STRLEN 50 #define NTP_MAX_TIMEZONE_STRLEN 50
#define NTP_MAX_TIMEZONEDESCR_STRLEN 50 #define NTP_MAX_TIMEZONEDESCR_STRLEN 50
@ -22,7 +25,7 @@
#define MQTT_MAX_CLIENTID_STRLEN 64 #define MQTT_MAX_CLIENTID_STRLEN 64
#define MQTT_MAX_USERNAME_STRLEN 64 #define MQTT_MAX_USERNAME_STRLEN 64
#define MQTT_MAX_PASSWORD_STRLEN 64 #define MQTT_MAX_PASSWORD_STRLEN 64
#define MQTT_MAX_TOPIC_STRLEN 32 #define MQTT_MAX_TOPIC_STRLEN 256
#define MQTT_MAX_LWTVALUE_STRLEN 20 #define MQTT_MAX_LWTVALUE_STRLEN 20
#define MQTT_MAX_CERT_STRLEN 2560 #define MQTT_MAX_CERT_STRLEN 2560
@ -35,6 +38,17 @@
#define DEV_MAX_MAPPING_NAME_STRLEN 63 #define DEV_MAX_MAPPING_NAME_STRLEN 63
#define LOCALE_STRLEN 2 #define LOCALE_STRLEN 2
#define HTTP_REQUEST_MAX_URL_STRLEN 1024
#define HTTP_REQUEST_MAX_USERNAME_STRLEN 64
#define HTTP_REQUEST_MAX_PASSWORD_STRLEN 64
#define HTTP_REQUEST_MAX_HEADER_KEY_STRLEN 64
#define HTTP_REQUEST_MAX_HEADER_VALUE_STRLEN 256
#define POWERMETER_MQTT_MAX_VALUES 3
#define POWERMETER_HTTP_JSON_MAX_VALUES 3
#define POWERMETER_HTTP_JSON_MAX_PATH_STRLEN 256
#define BATTERY_JSON_MAX_PATH_STRLEN 128
struct CHANNEL_CONFIG_T { struct CHANNEL_CONFIG_T {
uint16_t MaxChannelPower; uint16_t MaxChannelPower;
char Name[CHAN_MAX_NAME_STRLEN]; char Name[CHAN_MAX_NAME_STRLEN];
@ -57,6 +71,130 @@ struct INVERTER_CONFIG_T {
CHANNEL_CONFIG_T channel[INV_MAX_CHAN_COUNT]; CHANNEL_CONFIG_T channel[INV_MAX_CHAN_COUNT];
}; };
struct HTTP_REQUEST_CONFIG_T {
char Url[HTTP_REQUEST_MAX_URL_STRLEN + 1];
enum Auth { None, Basic, Digest };
Auth AuthType;
char Username[HTTP_REQUEST_MAX_USERNAME_STRLEN + 1];
char Password[HTTP_REQUEST_MAX_PASSWORD_STRLEN + 1];
char HeaderKey[HTTP_REQUEST_MAX_HEADER_KEY_STRLEN + 1];
char HeaderValue[HTTP_REQUEST_MAX_HEADER_VALUE_STRLEN + 1];
uint16_t Timeout;
};
using HttpRequestConfig = struct HTTP_REQUEST_CONFIG_T;
struct POWERMETER_MQTT_VALUE_T {
char Topic[MQTT_MAX_TOPIC_STRLEN + 1];
char JsonPath[POWERMETER_HTTP_JSON_MAX_PATH_STRLEN + 1];
enum Unit { Watts = 0, MilliWatts = 1, KiloWatts = 2 };
Unit PowerUnit;
bool SignInverted;
};
using PowerMeterMqttValue = struct POWERMETER_MQTT_VALUE_T;
struct POWERMETER_MQTT_CONFIG_T {
PowerMeterMqttValue Values[POWERMETER_MQTT_MAX_VALUES];
};
using PowerMeterMqttConfig = struct POWERMETER_MQTT_CONFIG_T;
struct POWERMETER_SERIAL_SDM_CONFIG_T {
uint32_t Address;
uint32_t PollingInterval;
};
using PowerMeterSerialSdmConfig = struct POWERMETER_SERIAL_SDM_CONFIG_T;
struct POWERMETER_HTTP_JSON_VALUE_T {
HttpRequestConfig HttpRequest;
bool Enabled;
char JsonPath[POWERMETER_HTTP_JSON_MAX_PATH_STRLEN + 1];
enum Unit { Watts = 0, MilliWatts = 1, KiloWatts = 2 };
Unit PowerUnit;
bool SignInverted;
};
using PowerMeterHttpJsonValue = struct POWERMETER_HTTP_JSON_VALUE_T;
struct POWERMETER_HTTP_JSON_CONFIG_T {
uint32_t PollingInterval;
bool IndividualRequests;
PowerMeterHttpJsonValue Values[POWERMETER_HTTP_JSON_MAX_VALUES];
};
using PowerMeterHttpJsonConfig = struct POWERMETER_HTTP_JSON_CONFIG_T;
struct POWERMETER_HTTP_SML_CONFIG_T {
uint32_t PollingInterval;
HttpRequestConfig HttpRequest;
};
using PowerMeterHttpSmlConfig = struct POWERMETER_HTTP_SML_CONFIG_T;
struct POWERLIMITER_INVERTER_CONFIG_T {
uint64_t Serial;
bool IsGoverned;
bool IsBehindPowerMeter;
bool IsSolarPowered;
bool UseOverscalingToCompensateShading;
uint16_t LowerPowerLimit;
uint16_t UpperPowerLimit;
};
using PowerLimiterInverterConfig = struct POWERLIMITER_INVERTER_CONFIG_T;
struct POWERLIMITER_CONFIG_T {
bool Enabled;
bool VerboseLogging;
bool SolarPassThroughEnabled;
uint8_t SolarPassThroughLosses;
bool BatteryAlwaysUseAtNight;
int16_t TargetPowerConsumption;
uint16_t TargetPowerConsumptionHysteresis;
uint16_t BaseLoadLimit;
bool IgnoreSoc;
uint16_t BatterySocStartThreshold;
uint16_t BatterySocStopThreshold;
float VoltageStartThreshold;
float VoltageStopThreshold;
float VoltageLoadCorrectionFactor;
uint16_t FullSolarPassThroughSoc;
float FullSolarPassThroughStartVoltage;
float FullSolarPassThroughStopVoltage;
uint64_t InverterSerialForDcVoltage;
uint8_t InverterChannelIdForDcVoltage;
int8_t RestartHour;
uint16_t TotalUpperPowerLimit;
PowerLimiterInverterConfig Inverters[INV_MAX_COUNT];
};
using PowerLimiterConfig = struct POWERLIMITER_CONFIG_T;
enum BatteryVoltageUnit { Volts = 0, DeciVolts = 1, CentiVolts = 2, MilliVolts = 3 };
enum BatteryAmperageUnit { Amps = 0, MilliAmps = 1 };
struct BATTERY_CONFIG_T {
bool Enabled;
bool VerboseLogging;
uint8_t Provider;
uint8_t JkBmsInterface;
uint8_t JkBmsPollingInterval;
char MqttSocTopic[MQTT_MAX_TOPIC_STRLEN + 1];
char MqttSocJsonPath[BATTERY_JSON_MAX_PATH_STRLEN + 1];
char MqttVoltageTopic[MQTT_MAX_TOPIC_STRLEN + 1];
char MqttVoltageJsonPath[BATTERY_JSON_MAX_PATH_STRLEN + 1];
BatteryVoltageUnit MqttVoltageUnit;
bool EnableDischargeCurrentLimit;
float DischargeCurrentLimit;
float DischargeCurrentLimitBelowSoc;
float DischargeCurrentLimitBelowVoltage;
bool UseBatteryReportedDischargeCurrentLimit;
char MqttDischargeCurrentTopic[MQTT_MAX_TOPIC_STRLEN + 1];
char MqttDischargeCurrentJsonPath[BATTERY_JSON_MAX_PATH_STRLEN + 1];
BatteryAmperageUnit MqttAmperageUnit;
};
using BatteryConfig = struct BATTERY_CONFIG_T;
struct CONFIG_T { struct CONFIG_T {
struct { struct {
uint32_t Version; uint32_t Version;
@ -80,6 +218,12 @@ struct CONFIG_T {
bool Enabled; bool Enabled;
} Mdns; } Mdns;
struct {
bool Enabled;
char Hostname[SYSLOG_MAX_HOSTNAME_STRLEN + 1];
uint16_t Port;
} Syslog;
struct { struct {
char Server[NTP_MAX_SERVER_STRLEN + 1]; char Server[NTP_MAX_SERVER_STRLEN + 1];
char Timezone[NTP_MAX_TIMEZONE_STRLEN + 1]; char Timezone[NTP_MAX_TIMEZONE_STRLEN + 1];
@ -92,6 +236,7 @@ struct CONFIG_T {
struct { struct {
bool Enabled; bool Enabled;
char Hostname[MQTT_MAX_HOSTNAME_STRLEN + 1]; char Hostname[MQTT_MAX_HOSTNAME_STRLEN + 1];
bool VerboseLogging;
uint32_t Port; uint32_t Port;
char ClientId[MQTT_MAX_CLIENTID_STRLEN + 1]; char ClientId[MQTT_MAX_CLIENTID_STRLEN + 1];
char Username[MQTT_MAX_USERNAME_STRLEN + 1]; char Username[MQTT_MAX_USERNAME_STRLEN + 1];
@ -136,6 +281,7 @@ struct CONFIG_T {
uint32_t Frequency; uint32_t Frequency;
uint8_t CountryMode; uint8_t CountryMode;
} Cmt; } Cmt;
bool VerboseLogging;
} Dtu; } Dtu;
struct { struct {
@ -159,6 +305,42 @@ struct CONFIG_T {
uint8_t Brightness; uint8_t Brightness;
} Led_Single[PINMAPPING_LED_COUNT]; } Led_Single[PINMAPPING_LED_COUNT];
struct {
bool Enabled;
bool VerboseLogging;
bool UpdatesOnly;
} Vedirect;
struct PowerMeterConfig {
bool Enabled;
bool VerboseLogging;
uint32_t Source;
PowerMeterMqttConfig Mqtt;
PowerMeterSerialSdmConfig SerialSdm;
PowerMeterHttpJsonConfig HttpJson;
PowerMeterHttpSmlConfig HttpSml;
} PowerMeter;
PowerLimiterConfig PowerLimiter;
BatteryConfig Battery;
struct {
bool Enabled;
bool VerboseLogging;
uint32_t CAN_Controller_Frequency;
bool Auto_Power_Enabled;
bool Auto_Power_BatterySoC_Limits_Enabled;
bool Emergency_Charge_Enabled;
float Auto_Power_Voltage_Limit;
float Auto_Power_Enable_Voltage_Limit;
float Auto_Power_Lower_Power_Limit;
float Auto_Power_Upper_Power_Limit;
uint8_t Auto_Power_Stop_BatterySoC_Threshold;
float Auto_Power_Target_Power_Consumption;
} Huawei;
INVERTER_CONFIG_T Inverter[INV_MAX_COUNT]; INVERTER_CONFIG_T Inverter[INV_MAX_COUNT];
char Dev_PinMapping[DEV_MAX_MAPPING_NAME_STRLEN + 1]; char Dev_PinMapping[DEV_MAX_MAPPING_NAME_STRLEN + 1];
}; };
@ -187,6 +369,22 @@ public:
INVERTER_CONFIG_T* getInverterConfig(const uint64_t serial); INVERTER_CONFIG_T* getInverterConfig(const uint64_t serial);
void deleteInverterById(const uint8_t id); void deleteInverterById(const uint8_t id);
static void serializeHttpRequestConfig(HttpRequestConfig const& source, JsonObject& target);
static void serializePowerMeterMqttConfig(PowerMeterMqttConfig const& source, JsonObject& target);
static void serializePowerMeterSerialSdmConfig(PowerMeterSerialSdmConfig const& source, JsonObject& target);
static void serializePowerMeterHttpJsonConfig(PowerMeterHttpJsonConfig const& source, JsonObject& target);
static void serializePowerMeterHttpSmlConfig(PowerMeterHttpSmlConfig const& source, JsonObject& target);
static void serializeBatteryConfig(BatteryConfig const& source, JsonObject& target);
static void serializePowerLimiterConfig(PowerLimiterConfig const& source, JsonObject& target);
static void deserializeHttpRequestConfig(JsonObject const& source, HttpRequestConfig& target);
static void deserializePowerMeterMqttConfig(JsonObject const& source, PowerMeterMqttConfig& target);
static void deserializePowerMeterSerialSdmConfig(JsonObject const& source, PowerMeterSerialSdmConfig& target);
static void deserializePowerMeterHttpJsonConfig(JsonObject const& source, PowerMeterHttpJsonConfig& target);
static void deserializePowerMeterHttpSmlConfig(JsonObject const& source, PowerMeterHttpSmlConfig& target);
static void deserializeBatteryConfig(JsonObject const& source, BatteryConfig& target);
static void deserializePowerLimiterConfig(JsonObject const& source, PowerLimiterConfig& target);
private: private:
void loop(); void loop();

119
include/DataPoints.h Normal file
View File

@ -0,0 +1,119 @@
#pragma once
#include <Arduino.h>
#include <map>
#include <optional>
#include <string>
#include <unordered_map>
#include <variant>
using tCellVoltages = std::map<uint8_t, uint16_t>;
template<typename... V>
class DataPoint {
template<typename, typename L, template<L> class>
friend class DataPointContainer;
public:
using tValue = std::variant<V...>;
DataPoint() = delete;
DataPoint(DataPoint const& other)
: _strLabel(other._strLabel)
, _strValue(other._strValue)
, _strUnit(other._strUnit)
, _value(other._value)
, _timestamp(other._timestamp) { }
DataPoint(std::string const& strLabel, std::string const& strValue,
std::string const& strUnit, tValue value, uint32_t timestamp)
: _strLabel(strLabel)
, _strValue(strValue)
, _strUnit(strUnit)
, _value(std::move(value))
, _timestamp(timestamp) { }
std::string const& getLabelText() const { return _strLabel; }
std::string const& getValueText() const { return _strValue; }
std::string const& getUnitText() const { return _strUnit; }
uint32_t getTimestamp() const { return _timestamp; }
bool operator==(DataPoint const& other) const {
return _value == other._value;
}
private:
std::string _strLabel;
std::string _strValue;
std::string _strUnit;
tValue _value;
uint32_t _timestamp;
};
template<typename T> std::string dataPointValueToStr(T const& v);
template<typename DataPoint, typename Label, template<Label> class Traits>
class DataPointContainer {
public:
DataPointContainer() = default;
//template<Label L> using Traits = LabelTraits<L>;
template<Label L>
void add(typename Traits<L>::type val) {
_dataPoints.emplace(
L,
DataPoint(
Traits<L>::name,
dataPointValueToStr(val),
Traits<L>::unit,
typename DataPoint::tValue(std::move(val)),
millis()
)
);
}
// make sure add() is only called with the type expected for the
// respective label, no implicit conversions allowed.
template<Label L, typename T>
void add(T) = delete;
template<Label L>
std::optional<DataPoint const> getDataPointFor() const {
auto it = _dataPoints.find(L);
if (it == _dataPoints.end()) { return std::nullopt; }
return it->second;
}
template<Label L>
std::optional<typename Traits<L>::type> get() const {
auto optionalDataPoint = getDataPointFor<L>();
if (!optionalDataPoint.has_value()) { return std::nullopt; }
return std::get<typename Traits<L>::type>(optionalDataPoint->_value);
}
using tMap = std::unordered_map<Label, DataPoint const>;
typename tMap::const_iterator cbegin() const { return _dataPoints.cbegin(); }
typename tMap::const_iterator cend() const { return _dataPoints.cend(); }
// copy all data points from source into this instance, overwriting
// existing data points in this instance.
void updateFrom(DataPointContainer const& source)
{
for (auto iter = source.cbegin(); iter != source.cend(); ++iter) {
auto pos = _dataPoints.find(iter->first);
if (pos != _dataPoints.end()) {
// do not update existing data points with the same value
if (pos->second == iter->second) { continue; }
_dataPoints.erase(pos);
}
_dataPoints.insert(*iter);
}
}
private:
tMap _dataPoints;
};

View File

@ -80,6 +80,8 @@ private:
String _i18n_date_format; String _i18n_date_format;
String _i18n_current_power_kw; String _i18n_current_power_kw;
String _i18n_current_power_w; String _i18n_current_power_w;
String _i18n_meter_power_w;
String _i18n_meter_power_kw;
String _i18n_yield_total_mwh; String _i18n_yield_total_mwh;
String _i18n_yield_total_kwh; String _i18n_yield_total_kwh;
}; };

90
include/HttpGetter.h Normal file
View File

@ -0,0 +1,90 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Configuration.h"
#include <memory>
#include <vector>
#include <utility>
#include <string>
#include <HTTPClient.h>
#include <WiFiClient.h>
class HttpGetterClient : public HTTPClient {
public:
void restartTCP() {
// keeps the NetworkClient, and closes the TCP connections (as we
// effectively do not support keep-alive with HTTP 1.0).
HTTPClient::disconnect(true);
HTTPClient::connect();
}
};
using up_http_client_t = std::unique_ptr<HttpGetterClient>;
using sp_wifi_client_t = std::shared_ptr<WiFiClient>;
class HttpRequestResult {
public:
HttpRequestResult(bool success,
up_http_client_t upHttpClient = nullptr,
sp_wifi_client_t spWiFiClient = nullptr)
: _success(success)
, _upHttpClient(std::move(upHttpClient))
, _spWiFiClient(std::move(spWiFiClient)) { }
~HttpRequestResult() {
// the wifi client *must* die *after* the http client, as the http
// client uses the wifi client in its destructor.
if (_upHttpClient) { _upHttpClient->end(); }
_upHttpClient = nullptr;
_spWiFiClient = nullptr;
}
HttpRequestResult(HttpRequestResult const&) = delete;
HttpRequestResult(HttpRequestResult&&) = delete;
HttpRequestResult& operator=(HttpRequestResult const&) = delete;
HttpRequestResult& operator=(HttpRequestResult&&) = delete;
operator bool() const { return _success; }
Stream* getStream() {
if(!_upHttpClient) { return nullptr; }
return _upHttpClient->getStreamPtr();
}
private:
bool _success;
up_http_client_t _upHttpClient;
sp_wifi_client_t _spWiFiClient;
};
class HttpGetter {
public:
explicit HttpGetter(HttpRequestConfig const& cfg)
: _config(cfg) { }
bool init();
void addHeader(char const* key, char const* value);
HttpRequestResult performGetRequest();
char const* getErrorText() const { return _errBuffer; }
private:
std::pair<bool, String> getAuthDigest();
HttpRequestConfig const& _config;
template<typename... Args>
void logError(char const* format, Args... args);
char _errBuffer[256];
bool _useHttps;
String _host;
String _uri;
uint16_t _port;
String _wwwAuthenticate = "";
unsigned _nonceCounter = 0;
sp_wifi_client_t _spWiFiClient; // reused for multiple HTTP requests
std::vector<std::pair<std::string, std::string>> _additionalHeaders;
};

158
include/Huawei_can.h Normal file
View File

@ -0,0 +1,158 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <cstdint>
#include "SPI.h"
#include <mcp_can.h>
#include <mutex>
#include <TaskSchedulerDeclarations.h>
#ifndef HUAWEI_PIN_MISO
#define HUAWEI_PIN_MISO 12
#endif
#ifndef HUAWEI_PIN_MOSI
#define HUAWEI_PIN_MOSI 13
#endif
#ifndef HUAWEI_PIN_SCLK
#define HUAWEI_PIN_SCLK 26
#endif
#ifndef HUAWEI_PIN_IRQ
#define HUAWEI_PIN_IRQ 25
#endif
#ifndef HUAWEI_PIN_CS
#define HUAWEI_PIN_CS 15
#endif
#ifndef HUAWEI_PIN_POWER
#define HUAWEI_PIN_POWER 33
#endif
#define HUAWEI_MINIMAL_OFFLINE_VOLTAGE 48
#define HUAWEI_MINIMAL_ONLINE_VOLTAGE 42
#define MAX_CURRENT_MULTIPLIER 20
// Index values for rec_values array
#define HUAWEI_INPUT_POWER_IDX 0
#define HUAWEI_INPUT_FREQ_IDX 1
#define HUAWEI_INPUT_CURRENT_IDX 2
#define HUAWEI_OUTPUT_POWER_IDX 3
#define HUAWEI_EFFICIENCY_IDX 4
#define HUAWEI_OUTPUT_VOLTAGE_IDX 5
#define HUAWEI_OUTPUT_CURRENT_MAX_IDX 6
#define HUAWEI_INPUT_VOLTAGE_IDX 7
#define HUAWEI_OUTPUT_TEMPERATURE_IDX 8
#define HUAWEI_INPUT_TEMPERATURE_IDX 9
#define HUAWEI_OUTPUT_CURRENT_IDX 10
#define HUAWEI_OUTPUT_CURRENT1_IDX 11
// Defines and index values for tx_values array
#define HUAWEI_OFFLINE_VOLTAGE 0x01
#define HUAWEI_ONLINE_VOLTAGE 0x00
#define HUAWEI_OFFLINE_CURRENT 0x04
#define HUAWEI_ONLINE_CURRENT 0x03
// Modes of operation
#define HUAWEI_MODE_OFF 0
#define HUAWEI_MODE_ON 1
#define HUAWEI_MODE_AUTO_EXT 2
#define HUAWEI_MODE_AUTO_INT 3
// Error codes
#define HUAWEI_ERROR_CODE_RX 0x01
#define HUAWEI_ERROR_CODE_TX 0x02
// Wait time/current before shuting down the PSU / charger
// This is set to allow the fan to run for some time
#define HUAWEI_AUTO_MODE_SHUTDOWN_DELAY 60000
#define HUAWEI_AUTO_MODE_SHUTDOWN_CURRENT 0.75
// Updateinterval used to request new values from the PSU
#define HUAWEI_DATA_REQUEST_INTERVAL_MS 2500
typedef struct RectifierParameters {
float input_voltage;
float input_frequency;
float input_current;
float input_power;
float input_temp;
float efficiency;
float output_voltage;
float output_current;
float max_output_current;
float output_power;
float output_temp;
float amp_hour;
} RectifierParameters_t;
class HuaweiCanCommClass {
public:
bool init(uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huawei_clk,
uint8_t huawei_irq, uint8_t huawei_cs, uint32_t frequency);
void loop();
bool gotNewRxDataFrame(bool clear);
uint8_t getErrorCode(bool clear);
uint32_t getParameterValue(uint8_t parameter);
void setParameterValue(uint16_t in, uint8_t parameterType);
private:
void sendRequest();
SPIClass *SPI;
MCP_CAN *_CAN;
uint8_t _huaweiIrq; // IRQ pin
uint32_t _nextRequestMillis = 0; // When to send next data request to PSU
std::mutex _mutex;
uint32_t _recValues[12];
uint16_t _txValues[5];
bool _hasNewTxValue[5];
uint8_t _errorCode;
bool _completeUpdateReceived;
};
class HuaweiCanClass {
public:
void init(Scheduler& scheduler, uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huawei_clk, uint8_t huawei_irq, uint8_t huawei_cs, uint8_t huawei_power);
void updateSettings(uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huawei_clk, uint8_t huawei_irq, uint8_t huawei_cs, uint8_t huawei_power);
void setValue(float in, uint8_t parameterType);
void setMode(uint8_t mode);
RectifierParameters_t * get();
uint32_t getLastUpdate() const { return _lastUpdateReceivedMillis; };
bool getAutoPowerStatus() const { return _autoPowerEnabled; };
uint8_t getMode() const { return _mode; };
private:
void loop();
void processReceivedParameters();
void _setValue(float in, uint8_t parameterType);
Task _loopTask;
TaskHandle_t _HuaweiCanCommunicationTaskHdl = NULL;
bool _initialized = false;
uint8_t _huaweiPower; // Power pin
uint8_t _mode = HUAWEI_MODE_AUTO_EXT;
RectifierParameters_t _rp;
uint32_t _lastUpdateReceivedMillis; // Timestamp for last data seen from the PSU
uint32_t _outputCurrentOnSinceMillis; // Timestamp since when the PSU was idle at zero amps
uint32_t _nextAutoModePeriodicIntMillis; // When to set the next output voltage in automatic mode
uint32_t _lastPowerMeterUpdateReceivedMillis; // Timestamp of last seen power meter value
uint32_t _autoModeBlockedTillMillis = 0; // Timestamp to block running auto mode for some time
uint8_t _autoPowerEnabledCounter = 0;
bool _autoPowerEnabled = false;
bool _batteryEmergencyCharging = false;
};
extern HuaweiCanClass HuaweiCan;
extern HuaweiCanCommClass HuaweiCanComm;

View File

@ -22,6 +22,7 @@ public:
String& date_format, String& date_format,
String& offline, String& offline,
String& power_w, String& power_kw, String& power_w, String& power_kw,
String& meter_power_w, String& meter_power_kw,
String& yield_today_wh, String& yield_today_kwh, String& yield_today_wh, String& yield_today_kwh,
String& yield_total_kwh, String& yield_total_mwh); String& yield_total_kwh, String& yield_total_mwh);

View File

@ -0,0 +1,87 @@
#pragma once
#include <memory>
#include <vector>
#include <frozen/string.h>
#include "Battery.h"
#include "JbdBmsDataPoints.h"
#include "JbdBmsSerialMessage.h"
#include "JbdBmsController.h"
namespace JbdBms {
class Controller : public BatteryProvider {
public:
Controller() = default;
bool init(bool verboseLogging) final;
void deinit() final;
void loop() final;
std::shared_ptr<BatteryStats> getStats() const final { return _stats; }
private:
static char constexpr _serialPortOwner[] = "JBD BMS";
#ifdef JBDBMS_DUMMY_SERIAL
std::unique_ptr<DummySerial> _upSerial;
#else
std::unique_ptr<HardwareSerial> _upSerial;
#endif
enum class Status : unsigned {
Initializing,
Timeout,
WaitingForPollInterval,
HwSerialNotAvailableForWrite,
BusyReading,
RequestSent,
FrameCompleted
};
frozen::string const& getStatusText(Status status);
void announceStatus(Status status);
void sendRequest(uint8_t pollInterval);
void rxData(uint8_t inbyte);
void reset();
void frameComplete();
void processDataPoints(DataPointContainer const& dataPoints);
enum class Interface : unsigned {
Invalid,
Uart,
Transceiver
};
Interface getInterface() const;
enum class ReadState : unsigned {
Idle,
WaitingForFrameStart,
FrameStartReceived, // 1 Byte: 0xDD
StateReceived,
CommandCodeReceived,
ReadingDataContent,
DataContentReceived,
ReadingCheckSum,
CheckSumReceived,
};
ReadState _readState;
void setReadState(ReadState state) {
_readState = state;
}
bool _verboseLogging = true;
int8_t _rxEnablePin = -1;
int8_t _txEnablePin = -1;
Status _lastStatus = Status::Initializing;
uint32_t _lastStatusPrinted = 0;
uint32_t _lastRequest = 0;
uint8_t _dataLength = 0;
JbdBms::SerialResponse::tData _buffer = {};
std::shared_ptr<JbdBmsBatteryStats> _stats =
std::make_shared<JbdBmsBatteryStats>();
};
} /* namespace JbdBms */

118
include/JbdBmsDataPoints.h Normal file
View File

@ -0,0 +1,118 @@
#pragma once
#include <Arduino.h>
#include <map>
#include <frozen/map.h>
#include <frozen/string.h>
#include "DataPoints.h"
namespace JbdBms {
#define JBD_PROTECTION_STATUS(fnc) \
fnc(CellOverVoltage, (1<<0)) \
fnc(CellUnderVoltage, (1<<1)) \
fnc(PackOverVoltage, (1<<2)) \
fnc(PackUnderVoltage, (1<<3)) \
fnc(ChargingOverTemperature, (1<<4)) \
fnc(ChargingLowTemperature, (1<<5)) \
fnc(DischargingOverTemperature, (1<<6)) \
fnc(DischargingLowTemperature, (1<<7)) \
fnc(ChargingOverCurrent, (1<<8)) \
fnc(DischargeOverCurrent, (1<<9)) \
fnc(ShortCircuit, (1<<10)) \
fnc(IcFrontEndError, (1<<11)) \
fnc(MosSotwareLock, (1<<12)) \
fnc(Reserved1, (1<<13)) \
fnc(Reserved2, (1<<14)) \
fnc(Reserved3, (1<<15))
enum class AlarmBits : uint16_t {
#define ALARM_ENUM(name, value) name = value,
JBD_PROTECTION_STATUS(ALARM_ENUM)
#undef ALARM_ENUM
};
static const frozen::map<AlarmBits, frozen::string, 16> AlarmBitTexts = {
#define ALARM_TEXT(name, value) { AlarmBits::name, #name },
JBD_PROTECTION_STATUS(ALARM_TEXT)
#undef ALARM_TEXT
};
enum class DataPointLabel : uint8_t {
CellsMilliVolt,
BatteryTempOneCelsius,
BatteryTempTwoCelsius,
BatteryVoltageMilliVolt,
BatteryCurrentMilliAmps,
BatterySoCPercent,
BatteryTemperatureSensorAmount,
BatteryCycles,
BatteryCellAmount,
AlarmsBitmask,
BalancingEnabled,
CellAmountSetting,
BatteryCapacitySettingAmpHours,
BatteryChargeEnabled,
BatteryDischargeEnabled,
DateOfManufacturing,
BmsSoftwareVersion,
BmsHardwareVersion,
ActualBatteryCapacityAmpHours
};
using tCells = tCellVoltages;
template<DataPointLabel> struct DataPointLabelTraits;
#define LABEL_TRAIT(n, t, u) template<> struct DataPointLabelTraits<DataPointLabel::n> { \
using type = t; \
static constexpr char const name[] = #n; \
static constexpr char const unit[] = u; \
};
/**
* the types associated with the labels are the types for the respective data
* points in the JbdBms::DataPoint class. they are *not* always equal to the
* type used in the serial message.
*
* it is unfortunate that we have to repeat all enum values here to define the
* traits. code generation could help here (labels are defined in a single
* source of truth and this code is generated -- no typing errors, etc.).
* however, the compiler will complain if an enum is misspelled or traits are
* defined for a removed enum, so we will notice. it will also complain when a
* trait is missing and if a data point for a label without traits is added to
* the DataPointContainer class, because the traits must be available then.
* even though this is tedious to maintain, human errors will be caught.
*/
LABEL_TRAIT(CellsMilliVolt, tCells, "mV");
LABEL_TRAIT(BatteryTempOneCelsius, int16_t, "°C");
LABEL_TRAIT(BatteryTempTwoCelsius, int16_t, "°C");
LABEL_TRAIT(BatteryVoltageMilliVolt, uint32_t, "mV");
LABEL_TRAIT(BatteryCurrentMilliAmps, int32_t, "mA");
LABEL_TRAIT(BatterySoCPercent, uint8_t, "%");
LABEL_TRAIT(BatteryTemperatureSensorAmount, uint8_t, "");
LABEL_TRAIT(BatteryCycles, uint16_t, "");
LABEL_TRAIT(BatteryCellAmount, uint16_t, "");
LABEL_TRAIT(AlarmsBitmask, uint16_t, "");
LABEL_TRAIT(BalancingEnabled, bool, "");
LABEL_TRAIT(CellAmountSetting, uint8_t, "");
LABEL_TRAIT(BatteryCapacitySettingAmpHours, uint32_t, "Ah");
LABEL_TRAIT(BatteryChargeEnabled, bool, "");
LABEL_TRAIT(BatteryDischargeEnabled, bool, "");
LABEL_TRAIT(DateOfManufacturing, std::string, "");
LABEL_TRAIT(BmsSoftwareVersion, std::string, "");
LABEL_TRAIT(BmsHardwareVersion, std::string, "");
LABEL_TRAIT(ActualBatteryCapacityAmpHours, uint32_t, "Ah");
#undef LABEL_TRAIT
} /* namespace JbdBms */
using JbdBmsDataPoint = DataPoint<bool, uint8_t, uint16_t, uint32_t,
int16_t, int32_t, std::string, JbdBms::tCells>;
template class DataPointContainer<JbdBmsDataPoint, JbdBms::DataPointLabel, JbdBms::DataPointLabelTraits>;
namespace JbdBms {
using DataPointContainer = DataPointContainer<JbdBmsDataPoint, DataPointLabel, DataPointLabelTraits>;
} /* namespace JbdBms */

View File

@ -0,0 +1,96 @@
#pragma once
#include <utility>
#include <vector>
#include <Arduino.h>
#include "JbdBmsDataPoints.h"
namespace JbdBms {
// Only valid for receiving messages
class SerialMessage {
public:
using tData = std::vector<uint8_t>;
SerialMessage() = delete;
enum class Command : uint8_t {
Init = 0x00,
ReadBasicInformation = 0x03,
ReadCellVoltages = 0x04,
ReadHardwareVersionNumber = 0x05,
ControlMosInstruction = 0xE1,
};
uint8_t getStartMarker() const { return _raw[0]; }
virtual Command getCommand() const = 0;
uint8_t getDataLength() const { return _raw[3]; }
uint16_t getChecksum() const { return get<uint16_t>(_raw.cend()-3); }
uint8_t getEndMarker() const { return *(_raw.cend()-1); }
void printMessage();
bool isValid() const;
uint8_t const* data() { return _raw.data(); }
size_t size() { return _raw.size(); }
static constexpr uint8_t startMarker = 0xDD;
static constexpr uint8_t endMarker = 0x77;
protected:
template <typename... Args>
explicit SerialMessage(Args&&... args) : _raw(std::forward<Args>(args)...) { }
template<typename T, typename It> T get(It&& pos) const;
template<typename It> bool getBool(It&& pos) const;
template<typename It> int16_t getTemperature(It&& pos) const;
template<typename It> std::string getString(It&& pos, size_t len, bool replaceZeroes = false) const;
template<typename It> std::string getProductionDate(It&& pos) const;
template<typename T> void set(tData::iterator const& pos, T val);
uint16_t calcChecksum() const;
void updateChecksum();
tData _raw;
JbdBms::DataPointContainer _dp;
};
class SerialResponse : public SerialMessage {
public:
enum class Status : uint8_t {
Ok = 0x00,
Error = 0x80
};
using tData = SerialMessage::tData;
explicit SerialResponse(tData&& raw);
Command getCommand() const { return static_cast<Command>(_raw[1]); }
Status getStatus() const { return static_cast<Status>(_raw[2]); }
bool isValid() const;
DataPointContainer const& getDataPoints() const { return _dp; }
};
class SerialCommand : public SerialMessage {
public:
enum class Status : uint8_t {
Read = 0xA5,
Write = 0x5A,
};
explicit SerialCommand(Status status, Command cmd);
Status getStatus() const { return static_cast<Status>(_raw[1]); }
Command getCommand() const { return static_cast<Command>(_raw[2]); }
static Command getLastCommand() { return _lastCmd; }
bool isValid() const;
private:
static Command _lastCmd;
};
} /* namespace JbdBms */

86
include/JkBmsController.h Normal file
View File

@ -0,0 +1,86 @@
#pragma once
#include <memory>
#include <vector>
#include <frozen/string.h>
#include "Battery.h"
#include "JkBmsDataPoints.h"
#include "JkBmsSerialMessage.h"
#include "JkBmsDummy.h"
//#define JKBMS_DUMMY_SERIAL
namespace JkBms {
class Controller : public BatteryProvider {
public:
Controller() = default;
bool init(bool verboseLogging) final;
void deinit() final;
void loop() final;
std::shared_ptr<BatteryStats> getStats() const final { return _stats; }
private:
static char constexpr _serialPortOwner[] = "JK BMS";
#ifdef JKBMS_DUMMY_SERIAL
std::unique_ptr<DummySerial> _upSerial;
#else
std::unique_ptr<HardwareSerial> _upSerial;
#endif
enum class Status : unsigned {
Initializing,
Timeout,
WaitingForPollInterval,
HwSerialNotAvailableForWrite,
BusyReading,
RequestSent,
FrameCompleted
};
frozen::string const& getStatusText(Status status);
void announceStatus(Status status);
void sendRequest(uint8_t pollInterval);
void rxData(uint8_t inbyte);
void reset();
void frameComplete();
void processDataPoints(DataPointContainer const& dataPoints);
enum class Interface : unsigned {
Invalid,
Uart,
Transceiver
};
Interface getInterface() const;
enum class ReadState : unsigned {
Idle,
WaitingForFrameStart,
FrameStartReceived,
StartMarkerReceived,
FrameLengthMsbReceived,
ReadingFrame
};
ReadState _readState;
void setReadState(ReadState state) {
_readState = state;
}
bool _verboseLogging = true;
int8_t _rxEnablePin = -1;
int8_t _txEnablePin = -1;
Status _lastStatus = Status::Initializing;
uint32_t _lastStatusPrinted = 0;
uint32_t _lastRequest = 0;
uint16_t _frameLength = 0;
uint8_t _protocolVersion = -1;
SerialResponse::tData _buffer = {};
std::shared_ptr<JkBmsBatteryStats> _stats =
std::make_shared<JkBmsBatteryStats>();
};
} /* namespace JkBms */

216
include/JkBmsDataPoints.h Normal file
View File

@ -0,0 +1,216 @@
#pragma once
#include <Arduino.h>
#include <map>
#include <frozen/map.h>
#include <frozen/string.h>
#include "DataPoints.h"
namespace JkBms {
#define ALARM_BITS(fnc) \
fnc(LowCapacity, (1<<0)) \
fnc(BmsOvertemperature, (1<<1)) \
fnc(ChargingOvervoltage, (1<<2)) \
fnc(DischargeUndervoltage, (1<<3)) \
fnc(BatteryOvertemperature, (1<<4)) \
fnc(ChargingOvercurrent, (1<<5)) \
fnc(DischargeOvercurrent, (1<<6)) \
fnc(CellVoltageDifference, (1<<7)) \
fnc(BatteryBoxOvertemperature, (1<<8)) \
fnc(BatteryUndertemperature, (1<<9)) \
fnc(CellOvervoltage, (1<<10)) \
fnc(CellUndervoltage, (1<<11)) \
fnc(AProtect, (1<<12)) \
fnc(BProtect, (1<<13)) \
fnc(Reserved1, (1<<14)) \
fnc(Reserved2, (1<<15))
enum class AlarmBits : uint16_t {
#define ALARM_ENUM(name, value) name = value,
ALARM_BITS(ALARM_ENUM)
#undef ALARM_ENUM
};
static const frozen::map<AlarmBits, frozen::string, 16> AlarmBitTexts = {
#define ALARM_TEXT(name, value) { AlarmBits::name, #name },
ALARM_BITS(ALARM_TEXT)
#undef ALARM_TEXT
};
#define STATUS_BITS(fnc) \
fnc(ChargingActive, (1<<0)) \
fnc(DischargingActive, (1<<1)) \
fnc(BalancingActive, (1<<2)) \
fnc(BatteryOnline, (1<<3))
enum class StatusBits : uint16_t {
#define STATUS_ENUM(name, value) name = value,
STATUS_BITS(STATUS_ENUM)
#undef STATUS_ENUM
};
static const frozen::map<StatusBits, frozen::string, 4> StatusBitTexts = {
#define STATUS_TEXT(name, value) { StatusBits::name, #name },
STATUS_BITS(STATUS_TEXT)
#undef STATUS_TEXT
};
enum class DataPointLabel : uint8_t {
CellsMilliVolt = 0x79,
BmsTempCelsius = 0x80,
BatteryTempOneCelsius = 0x81,
BatteryTempTwoCelsius = 0x82,
BatteryVoltageMilliVolt = 0x83,
BatteryCurrentMilliAmps = 0x84,
BatterySoCPercent = 0x85,
BatteryTemperatureSensorAmount = 0x86,
BatteryCycles = 0x87,
BatteryCycleCapacity = 0x89,
BatteryCellAmount = 0x8a,
AlarmsBitmask = 0x8b,
StatusBitmask = 0x8c,
TotalOvervoltageThresholdMilliVolt = 0x8e,
TotalUndervoltageThresholdMilliVolt = 0x8f,
CellOvervoltageThresholdMilliVolt = 0x90,
CellOvervoltageRecoveryMilliVolt = 0x91,
CellOvervoltageProtectionDelaySeconds = 0x92,
CellUndervoltageThresholdMilliVolt = 0x93,
CellUndervoltageRecoveryMilliVolt = 0x94,
CellUndervoltageProtectionDelaySeconds = 0x95,
CellVoltageDiffThresholdMilliVolt = 0x96,
DischargeOvercurrentThresholdAmperes = 0x97,
DischargeOvercurrentDelaySeconds = 0x98,
ChargeOvercurrentThresholdAmps = 0x99,
ChargeOvercurrentDelaySeconds = 0x9a,
BalanceCellVoltageThresholdMilliVolt = 0x9b,
BalanceVoltageDiffThresholdMilliVolt = 0x9c,
BalancingEnabled = 0x9d,
BmsTempProtectionThresholdCelsius = 0x9e,
BmsTempRecoveryThresholdCelsius = 0x9f,
BatteryTempProtectionThresholdCelsius = 0xa0,
BatteryTempRecoveryThresholdCelsius = 0xa1,
BatteryTempDiffThresholdCelsius = 0xa2,
ChargeHighTempThresholdCelsius = 0xa3,
DischargeHighTempThresholdCelsius = 0xa4,
ChargeLowTempThresholdCelsius = 0xa5,
ChargeLowTempRecoveryCelsius = 0xa6,
DischargeLowTempThresholdCelsius = 0xa7,
DischargeLowTempRecoveryCelsius = 0xa8,
CellAmountSetting = 0xa9,
BatteryCapacitySettingAmpHours = 0xaa,
BatteryChargeEnabled = 0xab,
BatteryDischargeEnabled = 0xac,
CurrentCalibrationMilliAmps = 0xad,
BmsAddress = 0xae,
BatteryType = 0xaf,
SleepWaitTime = 0xb0, // what's this?
LowCapacityAlarmThresholdPercent = 0xb1,
ModificationPassword = 0xb2,
DedicatedChargerSwitch = 0xb3, // what's this?
EquipmentId = 0xb4,
DateOfManufacturing = 0xb5,
BmsHourMeterMinutes = 0xb6,
BmsSoftwareVersion = 0xb7,
CurrentCalibration = 0xb8,
ActualBatteryCapacityAmpHours = 0xb9,
ProductId = 0xba,
ProtocolVersion = 0xc0
};
using tCells = tCellVoltages;
template<DataPointLabel> struct DataPointLabelTraits;
#define LABEL_TRAIT(n, t, u) template<> struct DataPointLabelTraits<DataPointLabel::n> { \
using type = t; \
static constexpr char const name[] = #n; \
static constexpr char const unit[] = u; \
};
/**
* the types associated with the labels are the types for the respective data
* points in the JkBms::DataPoint class. they are *not* always equal to the
* type used in the serial message.
*
* it is unfortunate that we have to repeat all enum values here to define the
* traits. code generation could help here (labels are defined in a single
* source of truth and this code is generated -- no typing errors, etc.).
* however, the compiler will complain if an enum is misspelled or traits are
* defined for a removed enum, so we will notice. it will also complain when a
* trait is missing and if a data point for a label without traits is added to
* the DataPointContainer class, because the traits must be available then.
* even though this is tedious to maintain, human errors will be caught.
*/
LABEL_TRAIT(CellsMilliVolt, tCells, "mV");
LABEL_TRAIT(BmsTempCelsius, int16_t, "°C");
LABEL_TRAIT(BatteryTempOneCelsius, int16_t, "°C");
LABEL_TRAIT(BatteryTempTwoCelsius, int16_t, "°C");
LABEL_TRAIT(BatteryVoltageMilliVolt, uint32_t, "mV");
LABEL_TRAIT(BatteryCurrentMilliAmps, int32_t, "mA");
LABEL_TRAIT(BatterySoCPercent, uint8_t, "%");
LABEL_TRAIT(BatteryTemperatureSensorAmount, uint8_t, "");
LABEL_TRAIT(BatteryCycles, uint16_t, "");
LABEL_TRAIT(BatteryCycleCapacity, uint32_t, "Ah");
LABEL_TRAIT(BatteryCellAmount, uint16_t, "");
LABEL_TRAIT(AlarmsBitmask, uint16_t, "");
LABEL_TRAIT(StatusBitmask, uint16_t, "");
LABEL_TRAIT(TotalOvervoltageThresholdMilliVolt, uint32_t, "mV");
LABEL_TRAIT(TotalUndervoltageThresholdMilliVolt, uint32_t, "mV");
LABEL_TRAIT(CellOvervoltageThresholdMilliVolt, uint16_t, "mV");
LABEL_TRAIT(CellOvervoltageRecoveryMilliVolt, uint16_t, "mV");
LABEL_TRAIT(CellOvervoltageProtectionDelaySeconds, uint16_t, "s");
LABEL_TRAIT(CellUndervoltageThresholdMilliVolt, uint16_t, "mV");
LABEL_TRAIT(CellUndervoltageRecoveryMilliVolt, uint16_t, "mV");
LABEL_TRAIT(CellUndervoltageProtectionDelaySeconds, uint16_t, "s");
LABEL_TRAIT(CellVoltageDiffThresholdMilliVolt, uint16_t, "mV");
LABEL_TRAIT(DischargeOvercurrentThresholdAmperes, uint16_t, "A");
LABEL_TRAIT(DischargeOvercurrentDelaySeconds, uint16_t, "s");
LABEL_TRAIT(ChargeOvercurrentThresholdAmps, uint16_t, "A");
LABEL_TRAIT(ChargeOvercurrentDelaySeconds, uint16_t, "s");
LABEL_TRAIT(BalanceCellVoltageThresholdMilliVolt, uint16_t, "mV");
LABEL_TRAIT(BalanceVoltageDiffThresholdMilliVolt, uint16_t, "mV");
LABEL_TRAIT(BalancingEnabled, bool, "");
LABEL_TRAIT(BmsTempProtectionThresholdCelsius, uint16_t, "°C");
LABEL_TRAIT(BmsTempRecoveryThresholdCelsius, uint16_t, "°C");
LABEL_TRAIT(BatteryTempProtectionThresholdCelsius, uint16_t, "°C");
LABEL_TRAIT(BatteryTempRecoveryThresholdCelsius, uint16_t, "°C");
LABEL_TRAIT(BatteryTempDiffThresholdCelsius, uint16_t, "°C");
LABEL_TRAIT(ChargeHighTempThresholdCelsius, uint16_t, "°C");
LABEL_TRAIT(DischargeHighTempThresholdCelsius, uint16_t, "°C");
LABEL_TRAIT(ChargeLowTempThresholdCelsius, int16_t, "°C");
LABEL_TRAIT(ChargeLowTempRecoveryCelsius, int16_t, "°C");
LABEL_TRAIT(DischargeLowTempThresholdCelsius, int16_t, "°C");
LABEL_TRAIT(DischargeLowTempRecoveryCelsius, int16_t, "°C");
LABEL_TRAIT(CellAmountSetting, uint8_t, "");
LABEL_TRAIT(BatteryCapacitySettingAmpHours, uint32_t, "Ah");
LABEL_TRAIT(BatteryChargeEnabled, bool, "");
LABEL_TRAIT(BatteryDischargeEnabled, bool, "");
LABEL_TRAIT(CurrentCalibrationMilliAmps, uint16_t, "mA");
LABEL_TRAIT(BmsAddress, uint8_t, "");
LABEL_TRAIT(BatteryType, uint8_t, "");
LABEL_TRAIT(SleepWaitTime, uint16_t, "s");
LABEL_TRAIT(LowCapacityAlarmThresholdPercent, uint8_t, "%");
LABEL_TRAIT(ModificationPassword, std::string, "");
LABEL_TRAIT(DedicatedChargerSwitch, bool, "");
LABEL_TRAIT(EquipmentId, std::string, "");
LABEL_TRAIT(DateOfManufacturing, std::string, "");
LABEL_TRAIT(BmsHourMeterMinutes, uint32_t, "min");
LABEL_TRAIT(BmsSoftwareVersion, std::string, "");
LABEL_TRAIT(CurrentCalibration, bool, "");
LABEL_TRAIT(ActualBatteryCapacityAmpHours, uint32_t, "Ah");
LABEL_TRAIT(ProductId, std::string, "");
LABEL_TRAIT(ProtocolVersion, uint8_t, "");
#undef LABEL_TRAIT
} /* namespace JkBms */
using JkBmsDataPoint = DataPoint<bool, uint8_t, uint16_t, uint32_t,
int16_t, int32_t, std::string, JkBms::tCells>;
template class DataPointContainer<JkBmsDataPoint, JkBms::DataPointLabel, JkBms::DataPointLabelTraits>;
namespace JkBms {
using DataPointContainer = DataPointContainer<JkBmsDataPoint, DataPointLabel, DataPointLabelTraits>;
} /* namespace JkBms */

196
include/JkBmsDummy.h Normal file
View File

@ -0,0 +1,196 @@
#pragma once
#include <Arduino.h>
#include <vector>
#include "MessageOutput.h"
namespace JkBms {
class DummySerial {
public:
DummySerial() = default;
void begin(uint32_t, uint32_t, int8_t, int8_t) {
MessageOutput.println("JK BMS Dummy Serial: begin()");
}
void end() { MessageOutput.println("JK BMS Dummy Serial: end()"); }
void flush() { }
bool availableForWrite() const { return true; }
size_t write(const uint8_t *buffer, size_t size) {
MessageOutput.printf("JK BMS Dummy Serial: write(%d Bytes)\r\n", size);
_byte_idx = 0;
_msg_idx = (_msg_idx + 1) % _data.size();
return size;
}
bool available() const {
return _byte_idx < _data[_msg_idx].size();
}
int read() {
if (_byte_idx >= _data[_msg_idx].size()) { return 0; }
return _data[_msg_idx][_byte_idx++];
}
private:
std::vector<std::vector<uint8_t>> const _data =
{
{
0x4e, 0x57, 0x01, 0x21, 0x00, 0x00, 0x00, 0x00,
0x06, 0x00, 0x01, 0x79, 0x30, 0x01, 0x0c, 0xfb,
0x02, 0x0c, 0xfb, 0x03, 0x0c, 0xfb, 0x04, 0x0c,
0xfb, 0x05, 0x0c, 0xfb, 0x06, 0x0c, 0xfb, 0x07,
0x0c, 0xfb, 0x08, 0x0c, 0xf7, 0x09, 0x0d, 0x01,
0x0a, 0x0c, 0xf9, 0x0b, 0x0c, 0xfb, 0x0c, 0x0c,
0xfb, 0x0d, 0x0c, 0xfb, 0x0e, 0x0c, 0xf8, 0x0f,
0x0c, 0xf9, 0x10, 0x0c, 0xfb, 0x80, 0x00, 0x1a,
0x81, 0x00, 0x12, 0x82, 0x00, 0x12, 0x83, 0x14,
0xc3, 0x84, 0x83, 0xf4, 0x85, 0x2e, 0x86, 0x02,
0x87, 0x00, 0x15, 0x89, 0x00, 0x00, 0x13, 0x52,
0x8a, 0x00, 0x10, 0x8b, 0x00, 0x00, 0x8c, 0x00,
0x03, 0x8e, 0x16, 0x80, 0x8f, 0x12, 0xc0, 0x90,
0x0e, 0x10, 0x91, 0x0c, 0xda, 0x92, 0x00, 0x05,
0x93, 0x0b, 0xb8, 0x94, 0x0c, 0x80, 0x95, 0x00,
0x05, 0x96, 0x01, 0x2c, 0x97, 0x00, 0x28, 0x98,
0x01, 0x2c, 0x99, 0x00, 0x28, 0x9a, 0x00, 0x1e,
0x9b, 0x0b, 0xb8, 0x9c, 0x00, 0x0a, 0x9d, 0x01,
0x9e, 0x00, 0x64, 0x9f, 0x00, 0x50, 0xa0, 0x00,
0x64, 0xa1, 0x00, 0x64, 0xa2, 0x00, 0x14, 0xa3,
0x00, 0x46, 0xa4, 0x00, 0x46, 0xa5, 0x00, 0x00,
0xa6, 0x00, 0x02, 0xa7, 0xff, 0xec, 0xa8, 0xff,
0xf6, 0xa9, 0x10, 0xaa, 0x00, 0x00, 0x00, 0xe6,
0xab, 0x01, 0xac, 0x01, 0xad, 0x04, 0x4d, 0xae,
0x01, 0xaf, 0x00, 0xb0, 0x00, 0x0a, 0xb1, 0x14,
0xb2, 0x32, 0x32, 0x31, 0x31, 0x38, 0x37, 0x00,
0x00, 0x00, 0x00, 0xb3, 0x00, 0xb4, 0x62, 0x65,
0x6b, 0x69, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x32,
0x33, 0x30, 0x36, 0xb6, 0x00, 0x01, 0x4a, 0xc3,
0xb7, 0x31, 0x31, 0x2e, 0x58, 0x57, 0x5f, 0x53,
0x31, 0x31, 0x2e, 0x32, 0x36, 0x32, 0x48, 0x5f,
0xb8, 0x00, 0xb9, 0x00, 0x00, 0x00, 0xe6, 0xba,
0x62, 0x65, 0x6b, 0x69, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x4a, 0x4b, 0x5f, 0x42,
0x31, 0x41, 0x32, 0x34, 0x53, 0x31, 0x35, 0x50,
0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
0x00, 0x53, 0xbb
},
{
0x4e, 0x57, 0x01, 0x21, 0x00, 0x00, 0x00, 0x00,
0x06, 0x00, 0x01, 0x79, 0x30, 0x01, 0x0c, 0xc0,
0x02, 0x0c, 0xc1, 0x03, 0x0c, 0xc0, 0x04, 0x0c,
0xc4, 0x05, 0x0c, 0xc4, 0x06, 0x0c, 0xc2, 0x07,
0x0c, 0xc2, 0x08, 0x0c, 0xc1, 0x09, 0x0c, 0xba,
0x0a, 0x0c, 0xc1, 0x0b, 0x0c, 0xc2, 0x0c, 0x0c,
0xc2, 0x0d, 0x0c, 0xc2, 0x0e, 0x0c, 0xc4, 0x0f,
0x0c, 0xc2, 0x10, 0x0c, 0xc1, 0x80, 0x00, 0x1b,
0x81, 0x00, 0x1b, 0x82, 0x00, 0x1a, 0x83, 0x14,
0x68, 0x84, 0x03, 0x70, 0x85, 0x3c, 0x86, 0x02,
0x87, 0x00, 0x19, 0x89, 0x00, 0x00, 0x16, 0x86,
0x8a, 0x00, 0x10, 0x8b, 0x00, 0x00, 0x8c, 0x00,
0x07, 0x8e, 0x16, 0x80, 0x8f, 0x12, 0xc0, 0x90,
0x0e, 0x10, 0x91, 0x0c, 0xda, 0x92, 0x00, 0x05,
0x93, 0x0b, 0xb8, 0x94, 0x0c, 0x80, 0x95, 0x00,
0x05, 0x96, 0x01, 0x2c, 0x97, 0x00, 0x28, 0x98,
0x01, 0x2c, 0x99, 0x00, 0x28, 0x9a, 0x00, 0x1e,
0x9b, 0x0b, 0xb8, 0x9c, 0x00, 0x0a, 0x9d, 0x01,
0x9e, 0x00, 0x64, 0x9f, 0x00, 0x50, 0xa0, 0x00,
0x64, 0xa1, 0x00, 0x64, 0xa2, 0x00, 0x14, 0xa3,
0x00, 0x46, 0xa4, 0x00, 0x46, 0xa5, 0x00, 0x00,
0xa6, 0x00, 0x02, 0xa7, 0xff, 0xec, 0xa8, 0xff,
0xf6, 0xa9, 0x10, 0xaa, 0x00, 0x00, 0x00, 0xe6,
0xab, 0x01, 0xac, 0x01, 0xad, 0x04, 0x4d, 0xae,
0x01, 0xaf, 0x00, 0xb0, 0x00, 0x0a, 0xb1, 0x14,
0xb2, 0x32, 0x32, 0x31, 0x31, 0x38, 0x37, 0x00,
0x00, 0x00, 0x00, 0xb3, 0x00, 0xb4, 0x62, 0x65,
0x6b, 0x69, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x32,
0x33, 0x30, 0x36, 0xb6, 0x00, 0x01, 0x7f, 0x2a,
0xb7, 0x31, 0x31, 0x2e, 0x58, 0x57, 0x5f, 0x53,
0x31, 0x31, 0x2e, 0x32, 0x36, 0x32, 0x48, 0x5f,
0xb8, 0x00, 0xb9, 0x00, 0x00, 0x00, 0xe6, 0xba,
0x62, 0x65, 0x6b, 0x69, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x4a, 0x4b, 0x5f, 0x42,
0x31, 0x41, 0x32, 0x34, 0x53, 0x31, 0x35, 0x50,
0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
0x00, 0x4f, 0xc1
},
{
0x4e, 0x57, 0x01, 0x21, 0x00, 0x00, 0x00, 0x00,
0x06, 0x00, 0x01, 0x79, 0x30, 0x01, 0x0c, 0x13,
0x02, 0x0c, 0x12, 0x03, 0x0c, 0x0f, 0x04, 0x0c,
0x15, 0x05, 0x0c, 0x0d, 0x06, 0x0c, 0x13, 0x07,
0x0c, 0x16, 0x08, 0x0c, 0x13, 0x09, 0x0b, 0xdb,
0x0a, 0x0b, 0xf6, 0x0b, 0x0c, 0x17, 0x0c, 0x0b,
0xf5, 0x0d, 0x0c, 0x16, 0x0e, 0x0c, 0x1a, 0x0f,
0x0c, 0x1b, 0x10, 0x0c, 0x1c, 0x80, 0x00, 0x18,
0x81, 0x00, 0x18, 0x82, 0x00, 0x18, 0x83, 0x13,
0x49, 0x84, 0x00, 0x00, 0x85, 0x00, 0x86, 0x02,
0x87, 0x00, 0x23, 0x89, 0x00, 0x00, 0x20, 0x14,
0x8a, 0x00, 0x10, 0x8b, 0x00, 0x08, 0x8c, 0x00,
0x05, 0x8e, 0x16, 0x80, 0x8f, 0x12, 0xc0, 0x90,
0x0e, 0x10, 0x91, 0x0c, 0xda, 0x92, 0x00, 0x05,
0x93, 0x0b, 0xb8, 0x94, 0x0c, 0x80, 0x95, 0x00,
0x05, 0x96, 0x01, 0x2c, 0x97, 0x00, 0x28, 0x98,
0x01, 0x2c, 0x99, 0x00, 0x28, 0x9a, 0x00, 0x1e,
0x9b, 0x0b, 0xb8, 0x9c, 0x00, 0x0a, 0x9d, 0x01,
0x9e, 0x00, 0x64, 0x9f, 0x00, 0x50, 0xa0, 0x00,
0x64, 0xa1, 0x00, 0x64, 0xa2, 0x00, 0x14, 0xa3,
0x00, 0x46, 0xa4, 0x00, 0x46, 0xa5, 0x00, 0x00,
0xa6, 0x00, 0x02, 0xa7, 0xff, 0xec, 0xa8, 0xff,
0xf6, 0xa9, 0x10, 0xaa, 0x00, 0x00, 0x00, 0xe6,
0xab, 0x01, 0xac, 0x01, 0xad, 0x04, 0x4d, 0xae,
0x01, 0xaf, 0x00, 0xb0, 0x00, 0x0a, 0xb1, 0x14,
0xb2, 0x32, 0x32, 0x31, 0x31, 0x38, 0x37, 0x00,
0x00, 0x00, 0x00, 0xb3, 0x00, 0xb4, 0x62, 0x65,
0x6b, 0x69, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x32,
0x33, 0x30, 0x36, 0xb6, 0x00, 0x02, 0x17, 0x10,
0xb7, 0x31, 0x31, 0x2e, 0x58, 0x57, 0x5f, 0x53,
0x31, 0x31, 0x2e, 0x32, 0x36, 0x32, 0x48, 0x5f,
0xb8, 0x00, 0xb9, 0x00, 0x00, 0x00, 0xe6, 0xba,
0x62, 0x65, 0x6b, 0x69, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x4a, 0x4b, 0x5f, 0x42,
0x31, 0x41, 0x32, 0x34, 0x53, 0x31, 0x35, 0x50,
0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
0x00, 0x45, 0xce
},
{
0x4e, 0x57, 0x01, 0x21, 0x00, 0x00, 0x00, 0x00,
0x06, 0x00, 0x01, 0x79, 0x30, 0x01, 0x0c, 0x07,
0x02, 0x0c, 0x0a, 0x03, 0x0c, 0x0b, 0x04, 0x0c,
0x08, 0x05, 0x0c, 0x05, 0x06, 0x0c, 0x0b, 0x07,
0x0c, 0x07, 0x08, 0x0c, 0x0a, 0x09, 0x0c, 0x08,
0x0a, 0x0c, 0x06, 0x0b, 0x0c, 0x0a, 0x0c, 0x0c,
0x05, 0x0d, 0x0c, 0x0a, 0x0e, 0x0c, 0x0a, 0x0f,
0x0c, 0x0a, 0x10, 0x0c, 0x0a, 0x80, 0x00, 0x06,
0x81, 0x00, 0x03, 0x82, 0x00, 0x03, 0x83, 0x13,
0x40, 0x84, 0x00, 0x00, 0x85, 0x29, 0x86, 0x02,
0x87, 0x00, 0x01, 0x89, 0x00, 0x00, 0x01, 0x0a,
0x8a, 0x00, 0x10, 0x8b, 0x02, 0x00, 0x8c, 0x00,
0x02, 0x8e, 0x16, 0x80, 0x8f, 0x10, 0x40, 0x90,
0x0e, 0x10, 0x91, 0x0d, 0xde, 0x92, 0x00, 0x05,
0x93, 0x0a, 0x28, 0x94, 0x0a, 0x5a, 0x95, 0x00,
0x05, 0x96, 0x01, 0x2c, 0x97, 0x00, 0x28, 0x98,
0x01, 0x2c, 0x99, 0x00, 0x28, 0x9a, 0x00, 0x1e,
0x9b, 0x0b, 0xb8, 0x9c, 0x00, 0x0a, 0x9d, 0x01,
0x9e, 0x00, 0x5a, 0x9f, 0x00, 0x50, 0xa0, 0x00,
0x64, 0xa1, 0x00, 0x64, 0xa2, 0x00, 0x14, 0xa3,
0x00, 0x37, 0xa4, 0x00, 0x37, 0xa5, 0x00, 0x03,
0xa6, 0x00, 0x05, 0xa7, 0xff, 0xec, 0xa8, 0xff,
0xf6, 0xa9, 0x10, 0xaa, 0x00, 0x00, 0x00, 0xe6,
0xab, 0x01, 0xac, 0x01, 0xad, 0x04, 0x4d, 0xae,
0x01, 0xaf, 0x00, 0xb0, 0x00, 0x0a, 0xb1, 0x14,
0xb2, 0x32, 0x32, 0x31, 0x31, 0x38, 0x37, 0x00,
0x00, 0x00, 0x00, 0xb3, 0x00, 0xb4, 0x62, 0x65,
0x6b, 0x69, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x32,
0x33, 0x30, 0x36, 0xb6, 0x00, 0x03, 0xb7, 0x2d,
0xb7, 0x31, 0x31, 0x2e, 0x58, 0x57, 0x5f, 0x53,
0x31, 0x31, 0x2e, 0x32, 0x36, 0x32, 0x48, 0x5f,
0xb8, 0x00, 0xb9, 0x00, 0x00, 0x00, 0xe6, 0xba,
0x62, 0x65, 0x6b, 0x69, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x4a, 0x4b, 0x5f, 0x42,
0x31, 0x41, 0x32, 0x34, 0x53, 0x31, 0x35, 0x50,
0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
0x00, 0x41, 0x7b
}
};
size_t _msg_idx = 0;
size_t _byte_idx = 0;
};
} /* namespace JkBms */

View File

@ -0,0 +1,93 @@
#pragma once
#include <utility>
#include <vector>
#include <Arduino.h>
#include "JkBmsDataPoints.h"
namespace JkBms {
class SerialMessage {
public:
using tData = std::vector<uint8_t>;
SerialMessage() = delete;
enum class Command : uint8_t {
Activate = 0x01,
Write = 0x02,
Read = 0x03,
Password = 0x05,
ReadAll = 0x06
};
Command getCommand() const { return static_cast<Command>(_raw[8]); }
enum class Source : uint8_t {
BMS = 0x00,
Bluetooth = 0x01,
GPS = 0x02,
Host = 0x03
};
Source getSource() const { return static_cast<Source>(_raw[9]); }
enum class Type : uint8_t {
Command = 0x00,
Response = 0x01,
Unsolicited = 0x02
};
Type getType() const { return static_cast<Type>(_raw[10]); }
// this does *not* include the two byte start marker
uint16_t getFrameLength() const { return get<uint16_t>(_raw.cbegin()+2); }
uint32_t getTerminalId() const { return get<uint32_t>(_raw.cbegin()+4); }
// there are 20 bytes of overhead. two of those are the start marker
// bytes, which are *not* counted by the frame length.
uint16_t getVariableFieldLength() const { return getFrameLength() - 18; }
// the upper byte of the 4-byte "record number" is reserved (for encryption)
uint32_t getSequence() const { return get<uint32_t>(_raw.cend()-9) >> 8; }
bool isValid() const;
uint8_t const* data() { return _raw.data(); }
size_t size() { return _raw.size(); }
protected:
template <typename... Args>
explicit SerialMessage(Args&&... args) : _raw(std::forward<Args>(args)...) { }
template<typename T, typename It> T get(It&& pos) const;
template<typename It> bool getBool(It&& pos) const;
template<typename It> int16_t getTemperature(It&& pos) const;
template<typename It> std::string getString(It&& pos, size_t len, bool replaceZeroes = false) const;
void processBatteryCurrent(tData::const_iterator& pos, uint8_t protocolVersion);
template<typename T> void set(tData::iterator const& pos, T val);
uint16_t calcChecksum() const;
void updateChecksum();
tData _raw;
JkBms::DataPointContainer _dp;
static constexpr uint16_t startMarker = 0x4e57;
static constexpr uint8_t endMarker = 0x68;
};
class SerialResponse : public SerialMessage {
public:
using tData = SerialMessage::tData;
explicit SerialResponse(tData&& raw, uint8_t protocolVersion = -1);
DataPointContainer const& getDataPoints() const { return _dp; }
};
class SerialCommand : public SerialMessage {
public:
using Command = SerialMessage::Command;
explicit SerialCommand(Command cmd);
};
} /* namespace JkBms */

View File

@ -2,12 +2,13 @@
#pragma once #pragma once
#include <AsyncWebSocket.h> #include <AsyncWebSocket.h>
#include <HardwareSerial.h>
#include <Stream.h>
#include <TaskSchedulerDeclarations.h> #include <TaskSchedulerDeclarations.h>
#include <Print.h>
#include <freertos/task.h>
#include <mutex> #include <mutex>
#include <vector>
#define BUFFER_SIZE 500 #include <unordered_map>
#include <queue>
class MessageOutputClass : public Print { class MessageOutputClass : public Print {
public: public:
@ -22,13 +23,19 @@ private:
Task _loopTask; Task _loopTask;
using message_t = std::vector<uint8_t>;
// we keep a buffer for every task and only write complete lines to the
// serial output and then move them to be pushed through the websocket.
// this way we prevent mangling of messages from different contexts.
std::unordered_map<TaskHandle_t, message_t> _task_messages;
std::queue<message_t> _lines;
AsyncWebSocket* _ws = nullptr; AsyncWebSocket* _ws = nullptr;
char _buffer[BUFFER_SIZE];
uint16_t _buff_pos = 0;
uint32_t _lastSend = 0;
bool _forceSend = false;
std::mutex _msgLock; std::mutex _msgLock;
void serialWrite(message_t const& m);
}; };
extern MessageOutputClass MessageOutput; extern MessageOutputClass MessageOutput;

32
include/MqttBattery.h Normal file
View File

@ -0,0 +1,32 @@
#pragma once
#include <optional>
#include "Battery.h"
#include <espMqttClient.h>
class MqttBattery : public BatteryProvider {
public:
MqttBattery() = default;
bool init(bool verboseLogging) final;
void deinit() final;
void loop() final { return; } // this class is event-driven
std::shared_ptr<BatteryStats> getStats() const final { return _stats; }
private:
bool _verboseLogging = false;
String _socTopic;
String _voltageTopic;
String _dischargeCurrentLimitTopic;
std::shared_ptr<MqttBatteryStats> _stats = std::make_shared<MqttBatteryStats>();
void onMqttMessageSoC(espMqttClientTypes::MessageProperties const& properties,
char const* topic, uint8_t const* payload, size_t len, size_t index, size_t total,
char const* jsonPath);
void onMqttMessageVoltage(espMqttClientTypes::MessageProperties const& properties,
char const* topic, uint8_t const* payload, size_t len, size_t index, size_t total,
char const* jsonPath);
void onMqttMessageDischargeCurrentLimit(espMqttClientTypes::MessageProperties const& properties,
char const* topic, uint8_t const* payload, size_t len, size_t index, size_t total,
char const* jsonPath);
};

View File

@ -0,0 +1,25 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <ArduinoJson.h>
#include <TaskSchedulerDeclarations.h>
class MqttHandleBatteryHassClass {
public:
void init(Scheduler& scheduler);
void forceUpdate() { _doPublish = true; }
private:
void loop();
void publish(const String& subtopic, const String& payload);
void publishBinarySensor(const char* caption, const char* icon, const char* subTopic, const char* payload_on, const char* payload_off);
void publishSensor(const char* caption, const char* icon, const char* subTopic, const char* deviceClass = NULL, const char* stateClass = NULL, const char* unitOfMeasurement = NULL);
void createDeviceInfo(JsonObject& object);
Task _loopTask;
bool _doPublish = true;
String serial = "0001"; // pseudo-serial, can be replaced in future with real serialnumber
};
extern MqttHandleBatteryHassClass MqttHandleBatteryHass;

View File

@ -69,6 +69,9 @@ public:
void publishConfig(); void publishConfig();
void forceUpdate(); void forceUpdate();
static String getDtuUniqueId();
static String getDtuUrl();
private: private:
void loop(); void loop();
static void publish(const String& subtopic, const String& payload); static void publish(const String& subtopic, const String& payload);
@ -95,9 +98,6 @@ private:
static void createDeviceInfo(JsonDocument& doc, const String& name, const String& identifiers, const String& configuration_url, const String& manufacturer, const String& model, const String& sw_version, const String& via_device = ""); static void createDeviceInfo(JsonDocument& doc, const String& name, const String& identifiers, const String& configuration_url, const String& manufacturer, const String& model, const String& sw_version, const String& via_device = "");
static String getDtuUniqueId();
static String getDtuUrl();
Task _loopTask; Task _loopTask;
bool _wasConnected = false; bool _wasConnected = false;

View File

@ -0,0 +1,60 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Configuration.h"
#include <Huawei_can.h>
#include <espMqttClient.h>
#include <TaskSchedulerDeclarations.h>
#include <mutex>
#include <deque>
#include <functional>
#include <frozen/map.h>
#include <frozen/string.h>
class MqttHandleHuaweiClass {
public:
void init(Scheduler& scheduler);
void forceUpdate();
void subscribeTopics();
void unsubscribeTopics();
private:
void loop();
enum class Topic : unsigned {
LimitOnlineVoltage,
LimitOnlineCurrent,
LimitOfflineVoltage,
LimitOfflineCurrent,
Mode
};
static constexpr frozen::string _cmdtopic = "huawei/cmd/";
static constexpr frozen::map<frozen::string, Topic, 5> _subscriptions = {
{ "limit_online_voltage", Topic::LimitOnlineVoltage },
{ "limit_online_current", Topic::LimitOnlineCurrent },
{ "limit_offline_voltage", Topic::LimitOfflineVoltage },
{ "limit_offline_current", Topic::LimitOfflineCurrent },
{ "mode", Topic::Mode },
};
void onMqttMessage(Topic t,
const espMqttClientTypes::MessageProperties& properties,
const char* topic, const uint8_t* payload, size_t len,
size_t index, size_t total);
Task _loopTask;
uint32_t _lastPublishStats;
uint32_t _lastPublish;
// MQTT callbacks to process updates on subscribed topics are executed in
// the MQTT thread's context. we use this queue to switch processing the
// user requests into the main loop's context (TaskScheduler context).
mutable std::mutex _mqttMutex;
std::deque<std::function<void()>> _mqttCallbacks;
};
extern MqttHandleHuaweiClass MqttHandleHuawei;

View File

@ -0,0 +1,66 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Configuration.h"
#include <espMqttClient.h>
#include <TaskSchedulerDeclarations.h>
#include <mutex>
#include <deque>
#include <functional>
#include <frozen/map.h>
#include <frozen/string.h>
class MqttHandlePowerLimiterClass {
public:
void init(Scheduler& scheduler);
void forceUpdate();
void subscribeTopics();
void unsubscribeTopics();
private:
void loop();
enum class MqttPowerLimiterCommand : unsigned {
Mode,
BatterySoCStartThreshold,
BatterySoCStopThreshold,
FullSolarPassthroughSoC,
VoltageStartThreshold,
VoltageStopThreshold,
FullSolarPassThroughStartVoltage,
FullSolarPassThroughStopVoltage,
UpperPowerLimit,
TargetPowerConsumption
};
static constexpr frozen::string _cmdtopic = "powerlimiter/cmd/";
static constexpr frozen::map<frozen::string, MqttPowerLimiterCommand, 10> _subscriptions = {
{ "threshold/soc/start", MqttPowerLimiterCommand::BatterySoCStartThreshold },
{ "threshold/soc/stop", MqttPowerLimiterCommand::BatterySoCStopThreshold },
{ "threshold/soc/full_solar_passthrough", MqttPowerLimiterCommand::FullSolarPassthroughSoC },
{ "threshold/voltage/start", MqttPowerLimiterCommand::VoltageStartThreshold },
{ "threshold/voltage/stop", MqttPowerLimiterCommand::VoltageStopThreshold },
{ "threshold/voltage/full_solar_passthrough_start", MqttPowerLimiterCommand::FullSolarPassThroughStartVoltage },
{ "threshold/voltage/full_solar_passthrough_stop", MqttPowerLimiterCommand::FullSolarPassThroughStopVoltage },
{ "mode", MqttPowerLimiterCommand::Mode },
{ "upper_power_limit", MqttPowerLimiterCommand::UpperPowerLimit },
{ "target_power_consumption", MqttPowerLimiterCommand::TargetPowerConsumption },
};
void onMqttCmd(MqttPowerLimiterCommand command, const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total);
Task _loopTask;
uint32_t _lastPublishStats;
uint32_t _lastPublish;
// MQTT callbacks to process updates on subscribed topics are executed in
// the MQTT thread's context. we use this queue to switch processing the
// user requests into the main loop's context (TaskScheduler context).
mutable std::mutex _mqttMutex;
std::deque<std::function<void()>> _mqttCallbacks;
};
extern MqttHandlePowerLimiterClass MqttHandlePowerLimiter;

View File

@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <ArduinoJson.h>
#include <TaskSchedulerDeclarations.h>
class MqttHandlePowerLimiterHassClass {
public:
void init(Scheduler& scheduler);
void publishConfig();
void forceUpdate();
private:
void loop();
void publish(const String& subtopic, const String& payload);
void publishNumber(const char* caption, const char* icon, const char* category, const char* commandTopic, const char* stateTopic, const char* unitOfMeasure, const int16_t min, const int16_t max, const float step);
void publishSelect(const char* caption, const char* icon, const char* category, const char* commandTopic, const char* stateTopic);
void publishBinarySensor(const char* caption, const char* icon, const char* stateTopic, const char* payload_on, const char* payload_off);
void createDeviceInfo(JsonDocument& root);
Task _loopTask;
bool _wasConnected = false;
bool _updateForced = false;
};
extern MqttHandlePowerLimiterHassClass MqttHandlePowerLimiterHass;

View File

@ -0,0 +1,40 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "VeDirectMpptController.h"
#include "Configuration.h"
#include <Arduino.h>
#include <map>
#include <TaskSchedulerDeclarations.h>
#ifndef VICTRON_PIN_RX
#define VICTRON_PIN_RX 22
#endif
#ifndef VICTRON_PIN_TX
#define VICTRON_PIN_TX 21
#endif
class MqttHandleVedirectClass {
public:
void init(Scheduler& scheduler);
void forceUpdate();
private:
void loop();
std::map<std::string, VeDirectMpptController::data_t> _kvFrames;
Task _loopTask;
// point of time in millis() when updated values will be published
uint32_t _nextPublishUpdatesOnly = 0;
// point of time in millis() when all values will be published
uint32_t _nextPublishFull = 1;
bool _PublishFull;
void publish_mppt_data(const VeDirectMpptController::data_t &mpptData,
const VeDirectMpptController::data_t &frame) const;
};
extern MqttHandleVedirectClass MqttHandleVedirect;

View File

@ -0,0 +1,33 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <ArduinoJson.h>
#include "VeDirectMpptController.h"
#include <TaskSchedulerDeclarations.h>
class MqttHandleVedirectHassClass {
public:
void init(Scheduler& scheduler);
void publishConfig();
void forceUpdate();
private:
void loop();
void publish(const String& subtopic, const String& payload);
void publishBinarySensor(const char *caption, const char *icon, const char *subTopic,
const char *payload_on, const char *payload_off,
const VeDirectMpptController::data_t &mpptData);
void publishSensor(const char *caption, const char *icon, const char *subTopic,
const char *deviceClass, const char *stateClass,
const char *unitOfMeasurement,
const VeDirectMpptController::data_t &mpptData);
void createDeviceInfo(JsonObject &object,
const VeDirectMpptController::data_t &mpptData);
Task _loopTask;
bool _wasConnected = false;
bool _updateForced = false;
};
extern MqttHandleVedirectHassClass MqttHandleVedirectHass;

View File

@ -11,6 +11,7 @@ class MqttSettingsClass {
public: public:
MqttSettingsClass(); MqttSettingsClass();
void init(); void init();
void loop();
void performReconnect(); void performReconnect();
bool getConnected(); bool getConnected();
void publish(const String& subtopic, const String& payload); void publish(const String& subtopic, const String& payload);
@ -38,6 +39,7 @@ private:
Ticker _mqttReconnectTimer; Ticker _mqttReconnectTimer;
MqttSubscribeParser _mqttSubscribeParser; MqttSubscribeParser _mqttSubscribeParser;
std::mutex _clientLock; std::mutex _clientLock;
bool _verboseLogging = true;
}; };
extern MqttSettingsClass MqttSettings; extern MqttSettingsClass MqttSettings;

View File

@ -51,6 +51,29 @@ struct PinMapping_t {
uint8_t display_reset; uint8_t display_reset;
int8_t led[PINMAPPING_LED_COUNT]; int8_t led[PINMAPPING_LED_COUNT];
// OpenDTU-OnBattery-specific pins below
int8_t victron_tx;
int8_t victron_rx;
int8_t victron_tx2;
int8_t victron_rx2;
int8_t victron_tx3;
int8_t victron_rx3;
int8_t battery_rx;
int8_t battery_rxen;
int8_t battery_tx;
int8_t battery_txen;
int8_t huawei_miso;
int8_t huawei_mosi;
int8_t huawei_clk;
int8_t huawei_irq;
int8_t huawei_cs;
int8_t huawei_power;
int8_t powermeter_rx;
int8_t powermeter_tx;
int8_t powermeter_dere;
int8_t powermeter_rxen;
int8_t powermeter_txen;
}; };
class PinMappingClass { class PinMappingClass {
@ -65,6 +88,7 @@ public:
#if CONFIG_ETH_USE_ESP32_EMAC #if CONFIG_ETH_USE_ESP32_EMAC
bool isValidEthConfig() const; bool isValidEthConfig() const;
#endif #endif
bool isValidHuaweiConfig() const;
private: private:
PinMapping_t _pinMapping; PinMapping_t _pinMapping;

105
include/PowerLimiter.h Normal file
View File

@ -0,0 +1,105 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Configuration.h"
#include "PowerLimiterInverter.h"
#include <espMqttClient.h>
#include <Arduino.h>
#include <atomic>
#include <deque>
#include <memory>
#include <functional>
#include <optional>
#include <TaskSchedulerDeclarations.h>
#include <frozen/string.h>
#define PL_UI_STATE_INACTIVE 0
#define PL_UI_STATE_CHARGING 1
#define PL_UI_STATE_USE_SOLAR_ONLY 2
#define PL_UI_STATE_USE_SOLAR_AND_BATTERY 3
class PowerLimiterClass {
public:
PowerLimiterClass() = default;
enum class Status : unsigned {
Initializing,
DisabledByConfig,
DisabledByMqtt,
WaitingForValidTimestamp,
PowerMeterPending,
InverterInvalid,
InverterCmdPending,
ConfigReload,
InverterStatsPending,
FullSolarPassthrough,
UnconditionalSolarPassthrough,
Stable,
};
void init(Scheduler& scheduler);
void triggerReloadingConfig() { _reloadConfigFlag = true; }
uint8_t getInverterUpdateTimeouts() const;
uint8_t getPowerLimiterState();
int32_t getInverterOutput() { return _lastExpectedInverterOutput; }
bool getFullSolarPassThroughEnabled() const { return _fullSolarPassThroughEnabled; }
enum class Mode : unsigned {
Normal = 0,
Disabled = 1,
UnconditionalFullSolarPassthrough = 2
};
void setMode(Mode m) { _mode = m; }
Mode getMode() const { return _mode; }
bool usesBatteryPoweredInverter();
bool isGovernedInverterProducing();
private:
void loop();
Task _loopTask;
std::atomic<bool> _reloadConfigFlag = true;
uint16_t _lastExpectedInverterOutput = 0;
Status _lastStatus = Status::Initializing;
uint32_t _lastStatusPrinted = 0;
uint32_t _lastCalculation = 0;
static constexpr uint32_t _calculationBackoffMsDefault = 128;
uint32_t _calculationBackoffMs = _calculationBackoffMsDefault;
Mode _mode = Mode::Normal;
std::deque<std::unique_ptr<PowerLimiterInverter>> _inverters;
bool _batteryDischargeEnabled = false;
bool _nighttimeDischarging = false;
std::pair<bool, uint32_t> _nextInverterRestart = { false, 0 };
bool _fullSolarPassThroughEnabled = false;
bool _verboseLogging = true;
frozen::string const& getStatusText(Status status);
void announceStatus(Status status);
bool shutdown(Status status);
void reloadConfig();
std::pair<float, char const*> getInverterDcVoltage();
float getBatteryVoltage(bool log = false);
uint16_t solarDcToInverterAc(uint16_t dcPower);
void fullSolarPassthrough(PowerLimiterClass::Status reason);
int16_t calcHouseholdConsumption();
using inverter_filter_t = std::function<bool(PowerLimiterInverter const&)>;
uint16_t updateInverterLimits(uint16_t powerRequested, inverter_filter_t filter, std::string const& filterExpression);
uint16_t calcBatteryAllowance(uint16_t powerRequested);
bool updateInverters();
uint16_t getSolarPassthroughPower();
std::optional<uint16_t> getBatteryDischargeLimit();
float getBatteryInvertersOutputAcWatts();
float getLoadCorrectedVoltage();
bool testThreshold(float socThreshold, float voltThreshold,
std::function<bool(float, float)> compare);
bool isStartThresholdReached();
bool isStopThresholdReached();
bool isBelowStopThreshold();
void calcNextInverterRestart();
bool isFullSolarPassthroughActive();
};
extern PowerLimiterClass PowerLimiter;

View File

@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "PowerLimiterInverter.h"
class PowerLimiterBatteryInverter : public PowerLimiterInverter {
public:
PowerLimiterBatteryInverter(bool verboseLogging, PowerLimiterInverterConfig const& config);
uint16_t getMaxReductionWatts(bool allowStandby) const final;
uint16_t getMaxIncreaseWatts() const final;
uint16_t applyReduction(uint16_t reduction, bool allowStandby) final;
uint16_t applyIncrease(uint16_t increase) final;
uint16_t standby() final;
bool isSolarPowered() const final { return false; }
private:
void setAcOutput(uint16_t expectedOutputWatts) final;
};

View File

@ -0,0 +1,111 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Configuration.h"
#include <Hoymiles.h>
#include <optional>
#include <memory>
class PowerLimiterInverter {
public:
static std::unique_ptr<PowerLimiterInverter> create(bool verboseLogging, PowerLimiterInverterConfig const& config);
// send command(s) to inverter to reach desired target state (limit and
// production). return true if an update is pending, i.e., if the target
// state is NOT yet reached, false otherwise.
bool update();
// returns the timestamp of the oldest stats received for this inverter
// *after* its last command completed. return std::nullopt if new stats
// are pending after the last command completed.
std::optional<uint32_t> getLatestStatsMillis() const;
// the amount of times an update command issued to the inverter timed out
uint8_t getUpdateTimeouts() const { return _updateTimeouts; }
// maximum amount of AC power the inverter is able to produce
// (not regarding the configured upper power limit)
uint16_t getInverterMaxPowerWatts() const;
// maximum amount of AC power the inverter is allowed to produce as per
// upper power limit (additionally restricted by inverter's absolute max)
uint16_t getConfiguredMaxPowerWatts() const;
uint16_t getCurrentOutputAcWatts() const;
// this differs from current output power if new limit was assigned
uint16_t getExpectedOutputAcWatts() const;
// the maximum reduction of power output the inverter
// can achieve with or withouth going into standby.
virtual uint16_t getMaxReductionWatts(bool allowStandby) const = 0;
// the maximum increase of power output the inverter can achieve
// (is expected to achieve), possibly coming out of standby.
virtual uint16_t getMaxIncreaseWatts() const = 0;
// change the target limit such that the requested change becomes effective
// on the expected AC power output. returns the change in the range
// [0..reduction] that will become effective (once update() returns false).
virtual uint16_t applyReduction(uint16_t reduction, bool allowStandby) = 0;
virtual uint16_t applyIncrease(uint16_t increase) = 0;
// stop producing AC power. returns the change in power output
// that will become effective (once update() returns false).
virtual uint16_t standby() = 0;
// wake the inverter from standby and set it to produce
// as much power as permissible by its upper power limit.
void setMaxOutput();
void restart();
float getDcVoltage(uint8_t input);
bool isSendingCommandsEnabled() const { return _spInverter->getEnableCommands(); }
bool isReachable() const { return _spInverter->isReachable(); }
bool isProducing() const { return _spInverter->isProducing(); }
uint64_t getSerial() const { return _config.Serial; }
char const* getSerialStr() const { return _serialStr; }
bool isBehindPowerMeter() const { return _config.IsBehindPowerMeter; }
virtual bool isSolarPowered() const = 0;
void debug() const;
protected:
PowerLimiterInverter(bool verboseLogging, PowerLimiterInverterConfig const& config);
// returns false if the inverter cannot participate
// in achieving the requested change in power output
bool isEligible() const;
uint16_t getCurrentLimitWatts() const;
void setTargetPowerLimitWatts(uint16_t power) { _oTargetPowerLimitWatts = power; }
void setTargetPowerState(bool enable) { _oTargetPowerState = enable; }
void setExpectedOutputAcWatts(uint16_t power) { _expectedOutputAcWatts = power; }
// copied to avoid races with web UI
PowerLimiterInverterConfig _config;
// Hoymiles lib inverter instance
std::shared_ptr<InverterAbstract> _spInverter = nullptr;
bool _verboseLogging;
char _logPrefix[32];
private:
virtual void setAcOutput(uint16_t expectedOutputWatts) = 0;
char _serialStr[16];
// track (target) state
uint8_t _updateTimeouts = 0;
std::optional<uint32_t> _oUpdateStartMillis = std::nullopt;
std::optional<uint16_t> _oTargetPowerLimitWatts = std::nullopt;
std::optional<bool> _oTargetPowerState = std::nullopt;
mutable std::optional<uint32_t> _oStatsMillis = std::nullopt;
// the expected AC output (possibly is different from the target limit)
uint16_t _expectedOutputAcWatts = 0;
};

View File

@ -0,0 +1,20 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "PowerLimiterInverter.h"
class PowerLimiterSolarInverter : public PowerLimiterInverter {
public:
PowerLimiterSolarInverter(bool verboseLogging, PowerLimiterInverterConfig const& config);
uint16_t getMaxReductionWatts(bool allowStandby) const final;
uint16_t getMaxIncreaseWatts() const final;
uint16_t applyReduction(uint16_t reduction, bool allowStandby) final;
uint16_t applyIncrease(uint16_t increase) final;
uint16_t standby() final;
bool isSolarPowered() const final { return true; }
private:
uint16_t scaleLimit(uint16_t expectedOutputWatts);
void setAcOutput(uint16_t expectedOutputWatts) final;
};

27
include/PowerMeter.h Normal file
View File

@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "PowerMeterProvider.h"
#include <TaskSchedulerDeclarations.h>
#include <memory>
#include <mutex>
class PowerMeterClass {
public:
void init(Scheduler& scheduler);
void updateSettings();
float getPowerTotal() const;
uint32_t getLastUpdate() const;
bool isDataValid() const;
private:
void loop();
Task _loopTask;
mutable std::mutex _mutex;
std::unique_ptr<PowerMeterProvider> _upProvider = nullptr;
};
extern PowerMeterClass PowerMeter;

View File

@ -0,0 +1,53 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <array>
#include <variant>
#include <memory>
#include <condition_variable>
#include <mutex>
#include <stdint.h>
#include "HttpGetter.h"
#include "Configuration.h"
#include "PowerMeterProvider.h"
using Auth_t = HttpRequestConfig::Auth;
using Unit_t = PowerMeterHttpJsonValue::Unit;
class PowerMeterHttpJson : public PowerMeterProvider {
public:
explicit PowerMeterHttpJson(PowerMeterHttpJsonConfig const& cfg)
: _cfg(cfg) { }
~PowerMeterHttpJson();
bool init() final;
void loop() final;
float getPowerTotal() const final;
bool isDataValid() const final;
void doMqttPublish() const final;
using power_values_t = std::array<float, POWERMETER_HTTP_JSON_MAX_VALUES>;
using poll_result_t = std::variant<power_values_t, String>;
poll_result_t poll();
private:
static void pollingLoopHelper(void* context);
std::atomic<bool> _taskDone;
void pollingLoop();
PowerMeterHttpJsonConfig const _cfg;
uint32_t _lastPoll = 0;
mutable std::mutex _valueMutex;
power_values_t _powerValues = {};
std::array<std::unique_ptr<HttpGetter>, POWERMETER_HTTP_JSON_MAX_VALUES> _httpGetters;
TaskHandle_t _taskHandle = nullptr;
bool _stopPolling;
mutable std::mutex _pollingMutex;
std::condition_variable _cv;
};

View File

@ -0,0 +1,45 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <memory>
#include <condition_variable>
#include <mutex>
#include <stdint.h>
#include <Arduino.h>
#include "HttpGetter.h"
#include "Configuration.h"
#include "PowerMeterSml.h"
class PowerMeterHttpSml : public PowerMeterSml {
public:
explicit PowerMeterHttpSml(PowerMeterHttpSmlConfig const& cfg)
: PowerMeterSml("PowerMeterHttpSml")
, _cfg(cfg) { }
~PowerMeterHttpSml();
bool init() final;
void loop() final;
bool isDataValid() const final;
// returns an empty string on success,
// returns an error message otherwise.
String poll();
private:
static void pollingLoopHelper(void* context);
std::atomic<bool> _taskDone;
void pollingLoop();
PowerMeterHttpSmlConfig const _cfg;
uint32_t _lastPoll = 0;
std::unique_ptr<HttpGetter> _upHttpGetter;
TaskHandle_t _taskHandle = nullptr;
bool _stopPolling;
mutable std::mutex _pollingMutex;
std::condition_variable _cv;
};

39
include/PowerMeterMqtt.h Normal file
View File

@ -0,0 +1,39 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Configuration.h"
#include "PowerMeterProvider.h"
#include <espMqttClient.h>
#include <vector>
#include <mutex>
#include <array>
class PowerMeterMqtt : public PowerMeterProvider {
public:
explicit PowerMeterMqtt(PowerMeterMqttConfig const& cfg)
: _cfg(cfg) { }
~PowerMeterMqtt();
bool init() final;
void loop() final { }
float getPowerTotal() const final;
private:
using MsgProperties = espMqttClientTypes::MessageProperties;
void onMessage(MsgProperties const& properties, char const* topic,
uint8_t const* payload, size_t len, size_t index,
size_t total, float* targetVariable, PowerMeterMqttValue const* cfg);
// we don't need to republish data received from MQTT
void doMqttPublish() const final { };
PowerMeterMqttConfig const _cfg;
using power_values_t = std::array<float, POWERMETER_MQTT_MAX_VALUES>;
power_values_t _powerValues;
std::vector<String> _mqttSubscriptions;
mutable std::mutex _mutex;
};

View File

@ -0,0 +1,51 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include "Configuration.h"
class PowerMeterProvider {
public:
virtual ~PowerMeterProvider() { }
enum class Type : unsigned {
MQTT = 0,
SDM1PH = 1,
SDM3PH = 2,
HTTP_JSON = 3,
SERIAL_SML = 4,
SMAHM2 = 5,
HTTP_SML = 6
};
// returns true if the provider is ready for use, false otherwise
virtual bool init() = 0;
virtual void loop() = 0;
virtual float getPowerTotal() const = 0;
virtual bool isDataValid() const;
uint32_t getLastUpdate() const { return _lastUpdate; }
void mqttLoop() const;
protected:
PowerMeterProvider() {
auto const& config = Configuration.get();
_verboseLogging = config.PowerMeter.VerboseLogging;
}
void gotUpdate() { _lastUpdate = millis(); }
void mqttPublish(String const& topic, float const& value) const;
bool _verboseLogging;
private:
virtual void doMqttPublish() const = 0;
// gotUpdate() updates this variable potentially from a different thread
// than users that request to read this variable through getLastUpdate().
std::atomic<uint32_t> _lastUpdate = 0;
mutable uint32_t _lastMqttPublish = 0;
};

View File

@ -0,0 +1,60 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <mutex>
#include <condition_variable>
#include <SoftwareSerial.h>
#include "Configuration.h"
#include "PowerMeterProvider.h"
#include "SDM.h"
class PowerMeterSerialSdm : public PowerMeterProvider {
public:
enum class Phases {
One,
Three
};
PowerMeterSerialSdm(Phases phases, PowerMeterSerialSdmConfig const& cfg)
: _phases(phases)
, _cfg(cfg) { }
~PowerMeterSerialSdm();
bool init() final;
void loop() final;
float getPowerTotal() const final;
bool isDataValid() const final;
void doMqttPublish() const final;
private:
static void pollingLoopHelper(void* context);
bool readValue(std::unique_lock<std::mutex>& lock, uint16_t reg, float& targetVar);
std::atomic<bool> _taskDone;
void pollingLoop();
Phases _phases;
PowerMeterSerialSdmConfig const _cfg;
uint32_t _lastPoll = 0;
float _phase1Power = 0.0;
float _phase2Power = 0.0;
float _phase3Power = 0.0;
float _phase1Voltage = 0.0;
float _phase2Voltage = 0.0;
float _phase3Voltage = 0.0;
float _energyImport = 0.0;
float _energyExport = 0.0;
mutable std::mutex _valueMutex;
std::unique_ptr<SoftwareSerial> _upSdmSerial = nullptr;
std::unique_ptr<SDM> _upSdm = nullptr;
TaskHandle_t _taskHandle = nullptr;
bool _stopPolling;
mutable std::mutex _pollingMutex;
std::condition_variable _cv;
};

View File

@ -0,0 +1,44 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "PowerMeterSml.h"
#include <SoftwareSerial.h>
class PowerMeterSerialSml : public PowerMeterSml {
public:
PowerMeterSerialSml()
: PowerMeterSml("PowerMeterSerialSml") { }
~PowerMeterSerialSml();
bool init() final;
void loop() final;
private:
// we assume that an SML datagram is complete after no additional
// characters were received for this many milliseconds.
static uint8_t constexpr _datagramGapMillis = 50;
static uint32_t constexpr _baud = 9600;
// size in bytes of the software serial receive buffer. must have the
// capacity to hold a full SML datagram, as we are processing the datagrams
// only after all data of one datagram was received.
static int constexpr _bufCapacity = 1024; // memory usage: 1 byte each
// amount of bits (RX pin state transitions) the software serial can buffer
// without decoding bits to bytes and storing those in the receive buffer.
// this value dictates how ofter we need to call a function of the software
// serial instance that performs bit decoding (we call available()).
static int constexpr _isrCapacity = 256; // memory usage: 8 bytes each (timestamp + pointer)
static void pollingLoopHelper(void* context);
std::atomic<bool> _taskDone;
void pollingLoop();
TaskHandle_t _taskHandle = nullptr;
bool _stopPolling;
mutable std::mutex _pollingMutex;
std::unique_ptr<SoftwareSerial> _upSmlSerial = nullptr;
};

69
include/PowerMeterSml.h Normal file
View File

@ -0,0 +1,69 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <list>
#include <mutex>
#include <optional>
#include <stdint.h>
#include <Arduino.h>
#include <HTTPClient.h>
#include "Configuration.h"
#include "PowerMeterProvider.h"
#include "sml.h"
class PowerMeterSml : public PowerMeterProvider {
public:
float getPowerTotal() const final;
void doMqttPublish() const final;
protected:
explicit PowerMeterSml(char const* user)
: _user(user) { }
void reset();
void processSmlByte(uint8_t byte);
private:
std::string _user;
mutable std::mutex _mutex;
using values_t = struct {
std::optional<float> activePowerTotal = std::nullopt;
std::optional<float> activePowerL1 = std::nullopt;
std::optional<float> activePowerL2 = std::nullopt;
std::optional<float> activePowerL3 = std::nullopt;
std::optional<float> voltageL1 = std::nullopt;
std::optional<float> voltageL2 = std::nullopt;
std::optional<float> voltageL3 = std::nullopt;
std::optional<float> currentL1 = std::nullopt;
std::optional<float> currentL2 = std::nullopt;
std::optional<float> currentL3 = std::nullopt;
std::optional<float> energyImport = std::nullopt;
std::optional<float> energyExport = std::nullopt;
};
values_t _values;
values_t _cache;
using OBISHandler = struct {
uint8_t const OBIS[6];
void (*decoder)(float&);
std::optional<float>* target;
char const* name;
};
const std::list<OBISHandler> smlHandlerList{
{{0x01, 0x00, 0x10, 0x07, 0x00, 0xff}, &smlOBISW, &_cache.activePowerTotal, "active power total"},
{{0x01, 0x00, 0x24, 0x07, 0x00, 0xff}, &smlOBISW, &_cache.activePowerL1, "active power L1"},
{{0x01, 0x00, 0x38, 0x07, 0x00, 0xff}, &smlOBISW, &_cache.activePowerL2, "active power L2"},
{{0x01, 0x00, 0x4c, 0x07, 0x00, 0xff}, &smlOBISW, &_cache.activePowerL3, "active power L3"},
{{0x01, 0x00, 0x20, 0x07, 0x00, 0xff}, &smlOBISVolt, &_cache.voltageL1, "voltage L1"},
{{0x01, 0x00, 0x34, 0x07, 0x00, 0xff}, &smlOBISVolt, &_cache.voltageL2, "voltage L2"},
{{0x01, 0x00, 0x48, 0x07, 0x00, 0xff}, &smlOBISVolt, &_cache.voltageL3, "voltage L3"},
{{0x01, 0x00, 0x1f, 0x07, 0x00, 0xff}, &smlOBISAmpere, &_cache.currentL1, "current L1"},
{{0x01, 0x00, 0x33, 0x07, 0x00, 0xff}, &smlOBISAmpere, &_cache.currentL2, "current L2"},
{{0x01, 0x00, 0x47, 0x07, 0x00, 0xff}, &smlOBISAmpere, &_cache.currentL3, "current L3"},
{{0x01, 0x00, 0x01, 0x08, 0x00, 0xff}, &smlOBISWh, &_cache.energyImport, "energy import"},
{{0x01, 0x00, 0x02, 0x08, 0x00, 0xff}, &smlOBISWh, &_cache.energyExport, "energy export"}
};
};

View File

@ -0,0 +1,31 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2024 Holger-Steffen Stapf
*/
#pragma once
#include <cstdint>
#include "PowerMeterProvider.h"
class PowerMeterUdpSmaHomeManager : public PowerMeterProvider {
public:
~PowerMeterUdpSmaHomeManager();
bool init() final;
void loop() final;
float getPowerTotal() const final { return _powerMeterPower; }
void doMqttPublish() const final;
private:
void Soutput(int kanal, int index, int art, int tarif,
char const* name, float value, uint32_t timestamp);
uint8_t* decodeGroup(uint8_t* offset, uint16_t grouplen);
float _powerMeterPower = 0.0;
float _powerMeterL1 = 0.0;
float _powerMeterL2 = 0.0;
float _powerMeterL3 = 0.0;
uint32_t _previousMillis = 0;
uint32_t _serial = 0;
};

View File

@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Configuration.h"
#include "Battery.h"
#include "BatteryCanReceiver.h"
#include <driver/twai.h>
#include <Arduino.h>
class PylontechCanReceiver : public BatteryCanReceiver {
public:
bool init(bool verboseLogging) final;
void onMessage(twai_message_t rx_message) final;
std::shared_ptr<BatteryStats> getStats() const final { return _stats; }
private:
void dummyData();
std::shared_ptr<PylontechBatteryStats> _stats =
std::make_shared<PylontechBatteryStats>();
};

View File

@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Configuration.h"
#include "Battery.h"
#include "BatteryCanReceiver.h"
#include <driver/twai.h>
class PytesCanReceiver : public BatteryCanReceiver {
public:
bool init(bool verboseLogging) final;
void onMessage(twai_message_t rx_message) final;
std::shared_ptr<BatteryStats> getStats() const final { return _stats; }
private:
std::shared_ptr<PytesBatteryStats> _stats =
std::make_shared<PytesBatteryStats>();
};

19
include/SBSCanReceiver.h Normal file
View File

@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "BatteryCanReceiver.h"
#include <driver/twai.h>
#include <Arduino.h>
class SBSCanReceiver : public BatteryCanReceiver {
public:
bool init(bool verboseLogging) final;
void onMessage(twai_message_t rx_message) final;
std::shared_ptr<BatteryStats> getStats() const final { return _stats; }
private:
void dummyData();
std::shared_ptr<SBSBatteryStats> _stats =
std::make_shared<SBSBatteryStats>();
};

View File

@ -0,0 +1,21 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <optional>
#include <string>
class SerialPortManagerClass {
public:
void init();
std::optional<uint8_t> allocatePort(std::string const& owner);
void freePort(std::string const& owner);
private:
// the amount of hardare UARTs available on supported ESP32 chips
static size_t constexpr _num_controllers = 3;
std::array<std::string, _num_controllers> _ports = { "" };
};
extern SerialPortManagerClass SerialPortManager;

34
include/SyslogLogger.h Normal file
View File

@ -0,0 +1,34 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <WiFiUdp.h>
#include <TaskSchedulerDeclarations.h>
#include <mutex>
class SyslogLogger {
public:
SyslogLogger();
void init(Scheduler& scheduler);
void updateSettings(const String&& hostname);
void write(const uint8_t *buffer, size_t size);
private:
void loop();
void disable();
void enable();
bool resolveAndStart();
bool isResolved() const {
return _address != INADDR_NONE;
}
Task _loopTask;
std::mutex _mutex;
WiFiUDP _udp;
IPAddress _address;
String _syslog_hostname;
String _proc_id;
String _header;
uint16_t _port;
bool _enabled;
};
extern SyslogLogger Syslog;

Some files were not shown because too many files have changed in this diff Show More