Compare commits

..

623 Commits

Author SHA1 Message Date
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
414 changed files with 24807 additions and 17035 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

View File

@ -1,9 +0,0 @@
# http://editorconfig.org
root = true
[*]
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true

View File

@ -5,18 +5,11 @@ body:
- type: markdown
attributes:
value: >
### ⚠️ Please remember: issues are for *bugs*
That is, something you believe affects every single user of OpenDTU, not just you. If you're not sure, start with one of the other options below.
- type: markdown
attributes:
value: |
#### Have a question? 👉 [Start a new discussion](https://github.com/tbnobody/OpenDTU/discussions/new) or [ask in chat](https://discord.gg/WzhxEY62mB).
### ✋ **This is bug tracker, not a support forum**
#### Before opening an issue, please double check:
If something isn't working right, you have questions or need help, [**get in touch on the Discussions**](https://github.com/tbnobody/OpenDTU/discussions).
- [Documentation](https://www.opendtu.solar).
- [The FAQs](https://www.opendtu.solar/firmware/faq/).
- [Existing issues and discussions](https://github.com/tbnobody/OpenDTU/search?q=&type=issues).
Please quickly search existing issues first before submitting a bug.
- type: textarea
id: what-happened
attributes:
@ -47,8 +40,7 @@ body:
label: Install Method
description: How did you install OpenDTU?
options:
- Pre-Compiled binary from GitHub releases
- Pre-Compiled binary from GitHub actions/pull-request
- Pre-Compiled binary from GitHub
- Self-Compiled
validations:
required: true
@ -60,14 +52,6 @@ body:
placeholder: "e.g. 359d513"
validations:
required: true
- type: input
id: environment
attributes:
label: What firmware variant (PIO Environment) are you using?
description: You can find this in by going to Info -> System
placeholder: "generic_esp32s3_usb"
validations:
required: true
- type: textarea
id: logs
attributes:
@ -82,16 +66,3 @@ body:
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
validations:
required: false
- type: checkboxes
id: required-checks
attributes:
label: Please confirm the following
options:
- label: I believe this issue is a bug that affects all users of OpenDTU, not something specific to my installation.
required: true
- label: I have already searched for relevant existing issues and discussions before opening this report.
required: true
- label: I have updated the title field above with a concise description.
required: true
- label: I have double checked that my inverter does not contain a W in the model name (like HMS-xxxW) as they are not supported.
required: true

View File

@ -1,10 +1,15 @@
name: OpenDTU Build
name: OpenDTU-onBattery Build
on:
push:
paths-ignore:
- docs/**
- '**/*.md'
branches:
- master
- development
tags-ignore:
- v*
pull_request:
paths-ignore:
- docs/**
@ -18,14 +23,14 @@ jobs:
- uses: actions/checkout@v4
- name: Cache pip
uses: actions/cache@v4
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- uses: actions/setup-python@v5
- uses: actions/setup-python@v4
with:
python-version: "3.x"
@ -43,7 +48,7 @@ jobs:
environments: ${{ steps.envs.outputs.environments }}
build:
name: Build Environments
name: Build Enviornments
runs-on: ubuntu-latest
needs: get_default_envs
strategy:
@ -56,7 +61,7 @@ jobs:
run: git fetch --force --tags origin
- name: Cache pip
uses: actions/cache@v4
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
@ -64,13 +69,13 @@ jobs:
${{ runner.os }}-pip-
- name: Cache PlatformIO
uses: actions/cache@v4
uses: actions/cache@v3
with:
path: ~/.platformio
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v4
with:
python-version: "3.x"
@ -79,56 +84,64 @@ jobs:
python -m pip install --upgrade pip
pip install --upgrade platformio setuptools
- name: Enable Corepack
run: |
cd webapp
corepack enable
- name: Setup Node.js and yarn
uses: actions/setup-node@v4
with:
node-version: "22"
node-version: "20"
cache: "yarn"
cache-dependency-path: "webapp/yarn.lock"
- name: Install WebApp dependencies
run: |
cd webapp
yarn install --frozen-lockfile
run: yarn --cwd webapp install --frozen-lockfile
- name: Build WebApp
run: |
cd webapp
yarn build
run: yarn --cwd webapp build
- name: Build firmware
run: pio run -e ${{ matrix.environment }}
- 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
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
with:
name: opendtu-${{ matrix.environment }}
name: opendtu-onbattery-${{ matrix.environment }}
path: |
.pio/build/${{ matrix.environment }}/opendtu-${{ matrix.environment }}.bin
.pio/build/${{ matrix.environment }}/opendtu-${{ matrix.environment }}.factory.bin
.pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.bin
.pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.factory.bin
release:
name: Create Release
runs-on: ubuntu-latest
needs: [get_default_envs, build]
if: startsWith(github.ref, 'refs/tags/')
if: startsWith(github.ref, 'refs/tags/2')
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
- 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
- name: Create openDTU-core-release-Badge
uses: schneegans/dynamic-badges-action@v1.6.0
with:
auth: ${{ secrets.GIST_SECRET }}
gistID: 68b47cc8c8994d04ab3a4fa9d8aee5e6
filename: openDTUcoreRelease.json
label: based on original OpenDTU
message: ${{ env.OPEN_DTU_CORE_RELEASE }}
color: lightblue
- name: Build Changelog
id: github_release
uses: mikepenz/release-changelog-builder-action@v4
uses: mikepenz/release-changelog-builder-action@v3
with:
failOnError: true
commitMode: true
@ -144,10 +157,10 @@ jobs:
run: |
ls -R
cd artifacts
for i in */; do cp ${i}opendtu-*.bin ./; done
for i in */; do cp ${i}opendtu-onbattery-*.bin ./; done
- name: Create release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v1
with:
body: ${{steps.github_release.outputs.changelog}}
draft: False

View File

@ -18,12 +18,6 @@
"fix"
]
},
{
"title": "## 🌎 Web Application",
"labels": [
"webapp"
]
},
{
"title": "## 📚 Documentation",
"labels": [
@ -36,7 +30,7 @@
}
],
"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",
"label_extractor": [
{

View File

@ -7,9 +7,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@v4
with:
python-version: "3.x"
- name: Install dependencies

View File

@ -1,54 +0,0 @@
name: 'Repository Maintenance'
on:
schedule:
- cron: '0 4 * * *'
workflow_dispatch:
permissions:
issues: write
pull-requests: write
discussions: write
concurrency:
group: lock
jobs:
stale:
name: 'Stale'
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
days-before-stale: 14
days-before-close: 60
any-of-labels: 'cant-reproduce,not a bug'
stale-issue-label: stale
stale-pr-label: stale
stale-issue-message: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
lock-threads:
name: 'Lock Old Threads'
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v5
with:
issue-inactive-days: '30'
pr-inactive-days: '30'
discussion-inactive-days: '30'
log-output: true
issue-comment: >
This issue has been automatically locked since there
has not been any recent activity after it was closed.
Please open a new discussion or issue for related concerns.
pr-comment: >
This pull request has been automatically locked since there
has not been any recent activity after it was closed.
Please open a new discussion or issue for related concerns.
discussion-comment: >
This discussion has been automatically locked since there
has not been any recent activity after it was closed.
Please open a new discussion for related concerns.

103
.github/workflows/test_build.yml vendored Normal file
View File

@ -0,0 +1,103 @@
name: OpenDTU-onBattery Test Build
on: workflow_dispatch
jobs:
get_default_envs:
name: Gather Environments
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Cache pip
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- uses: actions/setup-python@v4
with:
python-version: "3.9"
- name: Install PlatformIO
run: |
python -m pip install --upgrade pip
pip install --upgrade platformio
- name: Get default environments
id: envs
run: |
echo "environments=$(pio project config --json-output | jq -cr '.[1][1][0][1]|split(",")')" >> $GITHUB_OUTPUT
outputs:
environments: ${{ steps.envs.outputs.environments }}
build:
name: Build Enviornments
runs-on: ubuntu-latest
needs: get_default_envs
strategy:
matrix:
environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }}
steps:
- uses: actions/checkout@v3
- name: Get tags
run: git fetch --force --tags origin
- name: Cache pip
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Cache PlatformIO
uses: actions/cache@v3
with:
path: ~/.platformio
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.9"
- name: Install PlatformIO
run: |
python -m pip install --upgrade pip
pip install --upgrade platformio
- name: Setup Node.js and yarn
uses: actions/setup-node@v3
with:
node-version: "18"
cache: "yarn"
cache-dependency-path: "webapp/yarn.lock"
- name: Install WebApp dependencies
run: yarn --cwd webapp install --frozen-lockfile
- name: Build WebApp
run: yarn --cwd webapp build
- name: Build firmware
run: pio run -e ${{ matrix.environment }}
- name: Rename Firmware
run: mv .pio/build/${{ matrix.environment }}/firmware.bin .pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.bin
- name: Rename Factory Firmware
run: mv .pio/build/${{ matrix.environment }}/firmware.factory.bin .pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.factory.bin
- uses: actions/upload-artifact@v3
with:
name: opendtu-onbattery-${{ matrix.environment }}
path: |
.pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.bin
.pio/build/${{ matrix.environment }}/opendtu-onbattery-${{ matrix.environment }}.factory.bin

View File

@ -6,23 +6,17 @@ jobs:
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: webapp
steps:
- uses: actions/checkout@v4
- name: Enable Corepack
run: corepack enable
- uses: actions/checkout@v3
- name: Setup Node.js and yarn
uses: actions/setup-node@v4
uses: actions/setup-node@v3
with:
node-version: "22"
node-version: "18"
cache: "yarn"
cache-dependency-path: "webapp/yarn.lock"
- name: Install WebApp dependencies
run: yarn install --frozen-lockfile
run: yarn --cwd webapp install --frozen-lockfile
- name: Linting
run: yarn lint
run: yarn --cwd webapp lint

View File

@ -1,28 +0,0 @@
name: Yarn Prettier
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: webapp
steps:
- uses: actions/checkout@v4
- name: Enable Corepack
run: corepack enable
- name: Setup Node.js and yarn
uses: actions/setup-node@v4
with:
node-version: "22"
cache: "yarn"
cache-dependency-path: "webapp/yarn.lock"
- name: Install WebApp dependencies
run: yarn install --frozen-lockfile
- name: Check Formatting
run: yarn prettier --check src/

View File

@ -3,8 +3,8 @@
// for the documentation about the extensions.json format
"recommendations": [
"DavidAnson.vscode-markdownlint",
"EditorConfig.EditorConfig",
"Vue.volar",
"Vue.vscode-typescript-vue-plugin",
"platformio.platformio-ide"
],
"unwantedRecommendations": [

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"
}
}

View File

@ -1,43 +1,60 @@
# OpenDTU
- [OpenDTU-onBattery](#opendtu-onbattery)
- [What is OpenDTU-onBattery](#what-is-opendtu-onbattery)
- [History of the project](#history-of-the-project)
- [Highlights of OpenDTU-onBattery](#highlights-of-opendtu-onbattery)
- [Documentation](#documentation)
- [Acknowledgment](#acknowledgment)
[![OpenDTU Build](https://github.com/tbnobody/OpenDTU/actions/workflows/build.yml/badge.svg)](https://github.com/tbnobody/OpenDTU/actions/workflows/build.yml)
[![cpplint](https://github.com/tbnobody/OpenDTU/actions/workflows/cpplint.yml/badge.svg)](https://github.com/tbnobody/OpenDTU/actions/workflows/cpplint.yml)
[![Yarn Linting](https://github.com/tbnobody/OpenDTU/actions/workflows/yarnlint.yml/badge.svg)](https://github.com/tbnobody/OpenDTU/actions/workflows/yarnlint.yml)
# OpenDTU-onBattery
## !! IMPORTANT UPGRADE NOTES !!
This is a fork from the Hoymiles project [OpenDTU](https://github.com/tbnobody/OpenDTU).
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!
![GitHub tag (latest SemVer)](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/helgeerbe/68b47cc8c8994d04ab3a4fa9d8aee5e6/raw/openDTUcoreRelease.json)
## Background
[![OpenDTU-onBattery Build](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/build.yml/badge.svg)](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/build.yml)
[![cpplint](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/cpplint.yml/badge.svg)](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/cpplint.yml)
[![Yarn Linting](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/yarnlint.yml/badge.svg)](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/yarnlint.yml)
This project was started from [this](https://www.mikrocontroller.net/topic/525778) discussion (Mikrocontroller.net).
It was the goal to replace the original Hoymiles DTU (Telemetry Gateway) with their cloud access. With a lot of reverse engineering the Hoymiles protocol was decrypted and analyzed.
## What is OpenDTU-onBattery
OpenDTU-onBattery is an extension of the original OpenDTU to support battery chargers, battery management systems (BMS) and power meters on a single esp32. With the help of a dynamic power limiter, the power production can be adjusted to the actual consumption. In this way, it is possible to come as close as possible to the goal of zero feed-in.
## History of the project
The original OpenDTU project was started from [this](https://www.mikrocontroller.net/topic/525778) discussion (Mikrocontroller.net). It was the goal to replace the original Hoymiles DTU (Telemetry Gateway) with their cloud access. With a lot of reverse engineering the Hoymiles protocol was decrypted and analyzed.
Summer 2022 I bought my Victron MPPT battery charger, and didn't like the idea to set up a separate esp32 to recieve the charger data. I decided to fork OpenDTU and extend it with battery charger support and a dynamic power limitter to my own needs. Hoping someone can make use of it.
## Highlights of OpenDTU-onBattery
This project is still under development and adds following features:
> **Warning**
>
> In contrast to the original openDTU, with release 2023.05.23.post1 openDTU-onBattery supports only 5 inverters. Otherwise, there is not enough memory for the liveData view.
* Support Victron's Ve.Direct protocol on the same chip (cable based serial interface!). Additional information about Ve.direct can be downloaded directly from [Victron's website](https://www.victronenergy.com/support-and-downloads/technical-information).
* Dynamically sets the Hoymiles power limited according to the currently used energy in the household. Needs an HTTP JSON based power meter (e.g. Tasmota), an MQTT based power meter like Shelly 3EM or an SDM power meter.
* Battery support: Read the voltage from Victron MPPT charge controller or from the Hoymiles DC inputs and starts/stops the power producing based on configurable voltage thresholds
* Voltage correction that takes the voltage drop because of the current output load into account (not 100% reliable calculation)
* Can read the current solar panel power from the Victron MPPT and adjust the limiter accordingly to not save energy in the battery (for increased system efficiency). Increases the battery lifespan and reduces energy loses.
* Settings can be configured in the UI
* Pylontech Battery support (via CAN bus interface). Use the SOC for starting/stopping the power output and provide the battery data via MQTT (autodiscovery for home assistant is currently not supported). Pin Mapping is supported (default RX PIN 27, TX PIN 26). Actual no live view support for Pylontech Battery.
* Huawei R4850G2 power supply unit that can act as AC charger. Supports status shown on the web interface and options to set voltage and current limits on the web interface and via MQTT. Connection is done using CAN bus (needs to be separate from Pylontech CAN bus) via SN65HVD230 interface.
## Documentation
The documentation can be found [here](https://tbnobody.github.io/OpenDTU-docs/).
Please feel free to support and create a PR in [this](https://github.com/tbnobody/OpenDTU-docs) repository to make the documentation even better.
[Full documentation of OpenDTU-onBattery extensions can be found at the project's wiki](https://github.com/helgeerbe/OpenDTU-OnBattery/wiki).
## Breaking changes
For documentation of openDTU core functionality I refer to the original [repo](https://github.com/tbnobody/OpenDTU) and its [wiki](https://github.com/tbnobody/OpenDTU/wiki).
Generated using: `git log --date=short --pretty=format:"* %h%x09%ad%x09%s" | grep BREAKING`
Please note that openDTU-onBattery may change significantly during its development.
Bug reports, comments, feature requests and fixes are most welcome!
```code
* 1b637f08 2024-01-30 BREAKING CHANGE: Web API Endpoint /api/livedata/status and /api/prometheus/metrics
* e1564780 2024-01-30 BREAKING CHANGE: Web API Endpoint /api/livedata/status and /api/prometheus/metrics
* f0b5542c 2024-01-30 BREAKING CHANGE: Web API Endpoint /api/livedata/status and /api/prometheus/metrics
* c27ecc36 2024-01-29 BREAKING CHANGE: Web API Endpoint /api/livedata/status
* 71d1b3b 2023-11-07 BREAKING CHANGE: Home Assistant Auto Discovery to new naming scheme
* 04f62e0 2023-04-20 BREAKING CHANGE: Web API Endpoint /api/eventlog/status no nested serial object
* 59f43a8 2023-04-17 BREAKING CHANGE: Web API Endpoint /api/devinfo/status requires GET parameter inv=
* 318136d 2023-03-15 BREAKING CHANGE: Updated partition table: Make sure you have a configuration backup and completly reflash the device!
* 3b7aef6 2023-02-13 BREAKING CHANGE: Web API!
* d4c838a 2023-02-06 BREAKING CHANGE: Prometheus API!
* daf847e 2022-11-14 BREAKING CHANGE: Removed deprecated config parsing method
* 69b675b 2022-11-01 BREAKING CHANGE: Structure WebAPI /api/livedata/status changed
* 27ed4e3 2022-10-31 BREAKING: Change power factor from percent value to value between 0 and 1
```
To find out what's new or improved have a look at the [changelog](https://github.com/helgeerbe/OpenDTU-OnBattery/releases).
## Currently supported Inverters
## Acknowledgment
A list of all currently supported inverters can be found [here](https://www.opendtu.solar/hardware/inverter_overview/)
A special Thank to Thomas Basler (tbnobody) the author of the original [OpenDTU](https://github.com/tbnobody/OpenDTU) project. You are doing a great job!
Last but not least, I would like to thank all the contributors. With your ideas and enhancements, you have made OpenDTU-onBattery much more than I originally had in mind.

186
README_onBattery.md Normal file
View File

@ -0,0 +1,186 @@
# OpenDTU-OnBattery
This is a fork from the Hoymiles project [OpenDTU](https://github.com/tbnobody/OpenDTU). This project is still under development but is being used on a day to day basis as well.
![GitHub tag (latest SemVer)](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/helgeerbe/68b47cc8c8994d04ab3a4fa9d8aee5e6/raw/openDTUcoreRelease.json)
> **Warning**
>
> In contrast to the original openDTU, with release 2023.05.23.post1 openDTU-onBattery supports only 5 inverters. Otherwise, there is not enough memory for the liveData view.
## Features
* Hoymiles inverter support for live data and display of various inverter internal information. (Partial) support for multiple inverters.
* MQTT support (with TLS) with partial Home Assistant MQTT Auto Discovery
* Automatic inverter power control of a selected Hoymiles inverter to compensate the currently used energy in the household.
* Energy meter support with interface options to HTTP JSON based power meters (e.g. Tasmota), MQTT based power meters (e.g. Shelly 3EM) or SDM power meters.
* Support for Victron MPPT charge controller using Ve.Direct. cf. Ve.direct: https://www.victronenergy.com/support-and-downloads/technical-information.
* Generic voltage based battery support using Victron MPPT charge controller or Hoymiles inverter voltage values to start / stop inverter power output. (with load compensation)
* Pylontech battery support via CAN bus interface. State of Charge reported by BMS is used to start / stop inverter power output. Battery data is exported via MQTT (no support for home assistant auto discovery).
* Support for Huawei R4850G2 power supply unit that can act as AC charging source. [Overview](https://www.beyondlogic.org/review-huawei-r4850g2-power-supply-53-5vdc-3kw/)
* Other features from [OpenDTU](https://github.com/tbnobody/OpenDTU) maintained
## Hardware
To get started with this project you will need to assemble a few hardware components that allow interfacing with the desired devices. What is needed depends on the use-case but may consist of:
* ESP32 board that contains the CPU and WIFI connectivity
* NRF24L01+ or CMT2300A radio board to interface with the inverter. Please check the list of the supported inverters below for the board needed.
* 3.3V / 5V logic level shifter to interface with the Victron MPPT charge controller
* SN65HVD230 CAN bus transceiver to interface with a Pylontech battery
* MCP2515 SPI / CAN bus transceiver to interface with the Huawei AC PSU
* Relais board + 3.3V / 5 V logic level shifter to switch the slot detect on the Huawei AC PSU
* Display [Display](docs/Display.md)
More detailed information on the hardware can be found in the [Hardware and flashing](docs/hardware_flash.md) document.
### Currently supported Inverters
| Model | Required RF Module | DC Inputs | MPP-Tracker | AC Phases |
| --------------------| ------------------ | --------- | ----------- | --------- |
| Hoymiles HM-300 | NRF24L01+ | 1 | 1 | 1 |
| Hoymiles HM-350 | NRF24L01+ | 1 | 1 | 1 |
| Hoymiles HM-400 | NRF24L01+ | 1 | 1 | 1 |
| Hoymiles HM-600 | NRF24L01+ | 2 | 2 | 1 |
| Hoymiles HM-700 | NRF24L01+ | 2 | 2 | 1 |
| Hoymiles HM-800 | NRF24L01+ | 2 | 2 | 1 |
| Hoymiles HM-1000 | NRF24L01+ | 4 | 2 | 1 |
| Hoymiles HM-1200 | NRF24L01+ | 4 | 2 | 1 |
| Hoymiles HM-1500 | NRF24L01+ | 4 | 2 | 1 |
| Hoymiles HMS-300 | CMT2300A | 1 | 1 | 1 |
| Hoymiles HMS-350 | CMT2300A | 1 | 1 | 1 |
| Hoymiles HMS-400 | CMT2300A | 1 | 1 | 1 |
| Hoymiles HMS-450 | CMT2300A | 1 | 1 | 1 |
| Hoymiles HMS-500 | CMT2300A | 1 | 1 | 1 |
| Hoymiles HMS-600 | CMT2300A | 2 | 2 | 1 |
| Hoymiles HMS-700 | CMT2300A | 2 | 2 | 1 |
| Hoymiles HMS-800 | CMT2300A | 2 | 2 | 1 |
| Hoymiles HMS-900 | CMT2300A | 2 | 2 | 1 |
| Hoymiles HMS-1000 | CMT2300A | 2 | 2 | 1 |
| Hoymiles HMS-1600 | CMT2300A | 4 | 4 | 1 |
| Hoymiles HMS-1800 | CMT2300A | 4 | 4 | 1 |
| Hoymiles HMS-2000 | CMT2300A | 4 | 4 | 1 |
| Hoymiles HMT-1800 | CMT2300A | 6 | 3 | 3 |
| Hoymiles HMT-2250 | CMT2300A | 6 | 3 | 3 |
| Solenso SOL-H350 | NRF24L01+ | 1 | 1 | 1 |
| Solenso SOL-H400 | NRF24L01+ | 1 | 1 | 1 |
| Solenso SOL-H800 | NRF24L01+ | 2 | 2 | 1 |
| TSUN TSOL-M350 | NRF24L01+ | 1 | 1 | 1 |
| TSUN TSOL-M800 | NRF24L01+ | 2 | 2 | 1 |
| TSUN TSOL-M1600 | NRF24L01+ | 4 | 2 | 1 |
**TSUN compatibility remark:**
Compatibility with OpenDTU is most likely related to the serial number of the inverter. Current findings indicate that TSUN inverters with a serial number starting with "11" are supported, whereby inverters with a serial number starting with "10" are not.
## Screenshots
Several screenshots of the frontend can be found here: [Screenshots](docs/screenshots/README.md)
## Configuration and usage
### First configuration
* After the [initial flashing](docs/hardware_flash.md#flashing-and-starting-up) of the microcontroller, an Access Point called "OpenDTU-*" is opened. The default password is "openDTU42".
* Use a web browser to open the address [http://192.168.4.1](http://192.168.4.1)
* Navigate to Settings --> Network Settings and enter your WiFi credentials. The username to access the config menu is "admin" and the password the same as for accessing the Access Point (default: "openDTU42").
* OpenDTU then simultaneously connects to your WiFi AP with these credentials. Navigate to Info --> Network and look into section "Network Interface (Station)" for the IP address received via DHCP.
* If your WiFi AP uses an allow-list for MAC-addresses, please be aware that the ESP32 has two different MAC addresses for its AP and client modes, they are also listed at Info --> Network.
* When OpenDTU is connected to a configured WiFI AP, the "OpenDTU-*" Access Point is closed after 3 minutes.
* OpenDTU needs access to a working NTP server to get the current date & time. Both are sent to the inverter with each request. Default NTP server is pool.ntp.org. If your network has different requirements please change accordingly (Settings --> NTP Settings).
* Activate Ve.direct, Battery and the AC Charger according to the available hardware
* Configure a Power Meter to provide a data source for the current consumption
* Configure the Dynamic Power Limiter according to the used battery. Documentation about the power limiter interface and states can be found below.
* If desired connect to a home automation system using MQTT or the Webapi.
* A documentation of all available MQTT Topics can be found here: [MQTT Documentation](docs/MQTT_Topics.md)
* A documentation of the Web API can be found here: [Web-API Documentation](docs/Web-API.md)
* Home Assistant auto discovery is supported. [Example image](https://user-images.githubusercontent.com/59169507/217558862-a83846c5-6070-43cd-9a0b-90a8b2e2e8c6.png)
### Huawei PSU
The Huawei PSU can be used to charge a battery. This can be be useful if an external (AC) connected solar system shall be utilized or if variable energy prices should be exploited.
Some points for consideration are:
* Make sure to consider the PSU voltage range when selecting the battery voltage as lower voltages <42V are not supported.
* The PSU runs a noisy fan and it is therefore desireable to switch it off when not being used. Some users have found that switching the slot detect pins with a relay accomplishes this. A GPIO pin is made available from the ESP to turn the PSU on/off
#### Operation modes
openDTU-onBattery supports three operation modes for the Huawei PSU:
1. Fully manual - In this mode the PSU needs to be turned on/off externally using MQTT and voltage and current limits need to be provided. See [MQTT Documentation](docs/MQTT_Topics.md) for details on these commands
2. Manual with auto power on / off - In this mode the PSU is turned on when a current limit > 1A is set. If the current limit is < 1A for some time the PSU is turned off. Current and voltage limits need to be provided externally using MQTT. See [MQTT Documentation](docs/MQTT_Topics.md) for details on these commands.
3. Automatic - In this mode the PSU power is controlled by the Power Meter and information provided in the web-interface. If excess power is present the PSU is turned on. The voltage limit is set as per web-interface and the current limit is set so that the maximum PSU output power equals the Power Meter value. Minium and maximum PSU power levels as configured in the web-interface are respected in this process. The PSU is turned off if the output current is limited and the output power drops below the minium power level. This will disable automatic mode until the battery is discharged below the start voltage level (set in the web-interface). This mode can be enabled using the web-interface and MQTT. See [MQTT Documentation](docs/MQTT_Topics.md)
## Troubleshooting
* First: When there is no light on the solar panels, the inverter completely turns off and does not answer to OpenDTU! So if you assembled your OpenDTU in the evening, wait until tomorrow.
* When there is no data received from the inverter(s) - try to reduce the distance between the openDTU and the inverter (e.g. move it to the window towards the roof)
* Under Settings -> DTU Settings you can increase the transmit power "PA level". Default is "minimum".
* The NRF24L01+ needs relatively much current. With bad power supply (and especially bad cables!) a 10 µF capacitor soldered directly to the NRF24L01+ board connector brings more stability (pin 1+2 are the power supply). Note the polarity of the capacitor…
* You can try to use an USB power supply with 1 A or more instead of connecting the ESP32 to the computer.
* Try a different USB cable. Once again, a stable power source is important. Some USB cables are made of much plastic and very little copper inside.
* Double check that you have a radio module NRF24L01+ with a plus sign at the end. NRF24L01 module without the plus are not compatible with this project.
* There is no possibility of auto-discovering the inverters. Double check you have entered the serial numbers of the inverters correctly.
* OpenDTU needs access to a working NTP server to get the current date & time.
* If your problem persists, check the [Issues on Github](https://github.com/tbnobody/OpenDTU/issues). Please inspect not only the open issues, also the closed issues contain useful information.
* Another source of information are the [Discussions](https://github.com/tbnobody/OpenDTU/discussions/)
* When flashing with VSCode Plattform.IO fails and also with ESPRESSIF tool a demo bin file cannot be flashed to the ESP32 with error message "A fatal error occurred: MD5 of file does not match data in flash!" than un-wire/unconnect ESP32 from the NRF24L01+ board. Try to flash again and rewire afterwards.
## Background
This project was started from [this](https://www.mikrocontroller.net/topic/525778) discussion (Mikrocontroller.net).
It was the goal to replace the original Hoymiles DTU (Telemetry Gateway) with their cloud access. With a lot of reverse engineering the Hoymiles protocol was decrypted and analyzed.
## Features for developers
### Status
[![OpenDTU-onBattery Build](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/build.yml/badge.svg)](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/build.yml)
[![cpplint](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/cpplint.yml/badge.svg)](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/cpplint.yml)
[![Yarn Linting](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/yarnlint.yml/badge.svg)](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/yarnlint.yml)
### Core technologies used
* The microcontroller part
* Build with Arduino PlatformIO Framework for the ESP32
* Uses a fork of [ESPAsyncWebserver](https://github.com/yubox-node-org/ESPAsyncWebServer) and [espMqttClient](https://github.com/bertmelis/espMqttClient)
* The WebApp part
* Build with [Vue.js](https://vuejs.org)
* Source is written in TypeScript
### Breaking changes
Generated using: `git log --date=short --pretty=format:"* %h%x09%ad%x09%s" | grep BREAKING`
```code
* 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
```
### Building
* Building the WebApp
* The WebApp can be build using yarn
```bash
cd webapp
yarn install
yarn build
```
* The updated output is placed in the 'webapp_dist' directory
* It is only necessary to build the webapp when you made changes to it
* Building the microcontroller firmware
* Visual Studio Code with the PlatformIO Extension is required for building
## Related Projects
* [Ahoy](https://github.com/grindylow/ahoy)
* [DTU Simulator](https://github.com/Ziyatoe/DTUsimMI1x00-Hoymiles)
* [OpenDTU extended to talk to Victrons MPPT battery chargers (Ve.Direct)](https://github.com/helgeerbe/OpenDTU_VeDirect)

View File

@ -1,3 +1,91 @@
# Device Profiles
This documentation will has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/firmware/device_profiles/>
## Structure of the json file for openDTU-onBattery (outdated example)
```json
[
{
"name": "Generic NodeMCU 38 pin",
"nrf24": {
"miso": 19,
"mosi": 23,
"clk": 18,
"irq": 16,
"en": 4,
"cs": 5
},
"victron": {
"rx": 22,
"tx": 21
},
"battery": {
"rx": 27,
"tx": 14
},
"huawei": {
"miso": 12,
"mosi": 13,
"clk": 26,
"irq": 25,
"power": 33,
"cs": 15
},
"eth": {
"enabled": false,
"phy_addr": -1,
"power": -1,
"mdc": -1,
"mdio": -1,
"type": -1,
"clk_mode": -1
}
},
{
"name": "Generic NodeMCU 38 pin with SSD1306",
"nrf24": {
"miso": 19,
"mosi": 23,
"clk": 18,
"irq": 16,
"en": 4,
"cs": 5
},
"eth": {
"enabled": false,
"phy_addr": -1,
"power": -1,
"mdc": -1,
"mdio": -1,
"type": -1,
"clk_mode": -1
},
"display": {
"type": 2,
"data": 21,
"clk": 22
}
},
{
"name": "Olimex ESP32-POE",
"nrf24": {
"miso": 15,
"mosi": 2,
"clk": 14,
"irq": 13,
"en": 16,
"cs": 5
},
"eth": {
"enabled": true,
"phy_addr": 0,
"power": 12,
"mdc": 23,
"mdio": 18,
"type": 0,
"clk_mode": 3
}
}
]
```

View File

@ -1,76 +0,0 @@
[
{
"name": "AhoyDTU ESP32 Display LED",
"links": [
{"name": "Information", "url": "https://ahoydtu.de/getting_started/"}
],
"nrf24": {
"miso": 19,
"mosi": 23,
"clk": 18,
"irq": 16,
"en": 4,
"cs": 5
},
"led": {
"led0": 25,
"led1": 26
},
"display": {
"type": 2,
"data": 21,
"clk": 22
}
},
{
"name": "AhoyDTU ESP32 Display",
"links": [
{"name": "Information", "url": "https://ahoydtu.de/getting_started/"}
],
"nrf24": {
"miso": 19,
"mosi": 23,
"clk": 18,
"irq": 16,
"en": 4,
"cs": 5
},
"display": {
"type": 2,
"data": 21,
"clk": 22
}
},
{
"name": "AhoyDTU ESP32 LED",
"links": [
{"name": "Information", "url": "https://ahoydtu.de/getting_started/"}
],
"nrf24": {
"miso": 19,
"mosi": 23,
"clk": 18,
"irq": 16,
"en": 4,
"cs": 5
},
"led": {
"led0": 25,
"led1": 26
}
},
{
"name": "AhoyDTU ESP32",
"links": [
{"name": "Information", "url": "https://ahoydtu.de/getting_started/"}
],
"nrf24": {
"miso": 19,
"mosi": 23,
"clk": 18,
"irq": 16,
"en": 4,
"cs": 5
}
}
]

View File

@ -1,74 +0,0 @@
[
{
"name": "LILYGO T-ETH-Lite-POE CMT",
"links": [
{"name": "Datasheet", "url": "https://www.lilygo.cc/products/t-eth-lite"}
],
"eth": {
"enabled": true,
"phy_addr": 0,
"power": 12,
"mdc": 23,
"mdio": 18,
"type": 2,
"clk_mode": 0
},
"cmt": {
"clk": 15,
"cs": 32,
"fcs": 33,
"sdio": 4
}
},
{
"name": "LILYGO T-ETH-Lite-POE NRF24",
"links": [
{"name": "Datasheet", "url": "https://www.lilygo.cc/products/t-eth-lite"}
],
"eth": {
"enabled": true,
"phy_addr": 0,
"power": 12,
"mdc": 23,
"mdio": 18,
"type": 2,
"clk_mode": 0
},
"nrf24": {
"miso": 34,
"mosi": 13,
"clk": 14,
"irq": 35,
"en": 4,
"cs": 2
}
},
{
"name": "LILYGO T-ETH-Lite-POE NRF24 + Display",
"links": [
{"name": "Datasheet", "url": "https://www.lilygo.cc/products/t-eth-lite"}
],
"eth": {
"enabled": true,
"phy_addr": 0,
"power": 12,
"mdc": 23,
"mdio": 18,
"type": 2,
"clk_mode": 0
},
"nrf24": {
"miso": 34,
"mosi": 13,
"clk": 14,
"irq": 35,
"en": 4,
"cs": 2
},
"display": {
"type": 3,
"data": 32,
"clk": 33
}
}
]

View File

@ -1,47 +0,0 @@
[
{
"name": "Olimex ESP32-Gateway",
"nrf24": {
"miso": 14,
"mosi": 13,
"clk": 12,
"irq": 15,
"en": 2,
"cs": 4
},
"eth": {
"enabled": true,
"phy_addr": 0,
"power": 12,
"mdc": 23,
"mdio": 18,
"type": 0,
"clk_mode": 3
}
},
{
"name": "Olimex ESP32-Gateway with SSH1106",
"nrf24": {
"miso": 14,
"mosi": 13,
"clk": 12,
"irq": 15,
"en": 2,
"cs": 4
},
"eth": {
"enabled": true,
"phy_addr": 0,
"power": 12,
"mdc": 23,
"mdio": 18,
"type": 0,
"clk_mode": 3
},
"display": {
"type": 3,
"data": 32,
"clk": 16
}
}
]

View File

@ -1,9 +1,6 @@
[
{
"name": "OpenDTU Fusion v1",
"links": [
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
],
"nrf24": {
"miso": 48,
"mosi": 35,
@ -28,9 +25,6 @@
},
{
"name": "OpenDTU Fusion v1 with SSD1306 Display",
"links": [
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
],
"nrf24": {
"miso": 48,
"mosi": 35,
@ -60,9 +54,6 @@
},
{
"name": "OpenDTU Fusion v1 with SH1106 Display",
"links": [
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
],
"nrf24": {
"miso": 48,
"mosi": 35,
@ -92,9 +83,6 @@
},
{
"name": "OpenDTU Fusion v2 with CMT2300A and NRF24",
"links": [
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
],
"nrf24": {
"miso": 48,
"mosi": 35,
@ -127,9 +115,6 @@
},
{
"name": "OpenDTU Fusion v2 with CMT2300A, NRF24 and SH1106 Display",
"links": [
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
],
"nrf24": {
"miso": 48,
"mosi": 35,
@ -167,9 +152,6 @@
},
{
"name": "OpenDTU Fusion v2 with CMT2300A, NRF24 and SSD1306 Display",
"links": [
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
],
"nrf24": {
"miso": 48,
"mosi": 35,
@ -204,122 +186,5 @@
"data": 2,
"clk": 1
}
},
{
"name": "OpenDTU Fusion v2 PoE",
"links": [
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
],
"nrf24": {
"miso": 48,
"mosi": 35,
"clk": 36,
"irq": 47,
"en": 38,
"cs": 37
},
"cmt": {
"clk": 6,
"cs": 4,
"fcs": 21,
"sdio": 5,
"gpio2": 3,
"gpio3": 8
},
"w5500": {
"mosi": 40,
"miso": 41,
"sclk": 39,
"cs": 42,
"int": 44,
"rst": 43
},
"led": {
"led0": 17,
"led1": 18
},
"display": {
"type": 0,
"data": 2,
"clk": 1
}
},
{
"name": "OpenDTU Fusion v2 PoE with SH1106 Display",
"links": [
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
],
"nrf24": {
"miso": 48,
"mosi": 35,
"clk": 36,
"irq": 47,
"en": 38,
"cs": 37
},
"cmt": {
"clk": 6,
"cs": 4,
"fcs": 21,
"sdio": 5,
"gpio2": 3,
"gpio3": 8
},
"w5500": {
"mosi": 40,
"miso": 41,
"sclk": 39,
"cs": 42,
"int": 44,
"rst": 43
},
"led": {
"led0": 17,
"led1": 18
},
"display": {
"type": 3,
"data": 2,
"clk": 1
}
},
{
"name": "OpenDTU Fusion v2 PoE with SSD1306 Display",
"links": [
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
],
"nrf24": {
"miso": 48,
"mosi": 35,
"clk": 36,
"irq": 47,
"en": 38,
"cs": 37
},
"cmt": {
"clk": 6,
"cs": 4,
"fcs": 21,
"sdio": 5,
"gpio2": 3,
"gpio3": 8
},
"w5500": {
"mosi": 40,
"miso": 41,
"sclk": 39,
"cs": 42,
"int": 44,
"rst": 43
},
"led": {
"led0": 17,
"led1": 18
},
"display": {
"type": 2,
"data": 2,
"clk": 1
}
}
]

View File

@ -22,34 +22,6 @@
"clk_mode": 0
}
},
{
"name": "WT32-ETH01 with SH1106",
"links": [
{"name": "Datasheet", "url": "http://www.wireless-tag.com/portfolio/wt32-eth01/"}
],
"nrf24": {
"miso": 4,
"mosi": 2,
"clk": 32,
"irq": 33,
"en": 14,
"cs": 15
},
"eth": {
"enabled": true,
"phy_addr": 1,
"power": 16,
"mdc": 23,
"mdio": 18,
"type": 0,
"clk_mode": 0
},
"display": {
"type": 3,
"data": 5,
"clk": 17
}
},
{
"name": "WT32-ETH01 with SSD1306",
"links": [

View File

@ -1,3 +1,20 @@
# Display integration
This documentation will has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/hardware/display/>
OpenDTU currently supports 3 types of displays (SSD1306, SH1106 and PCD8544). Currently only displays with a resolution of 128x64 pixel are supported. To activate a display you have to specify it's type and pin assignment either in the `platformio_override.ini` or in a device profile. Due to the fact that device profiles work with the pre-compiled binary the following documentation will only cover the device profile method.
You can either create your own device profile as described [here](DeviceProfiles.md) or use some pre-defined. The pre-defined profiles can be found [here](DeviceProfiles/). You can simply open the json file with a text editor of your choice to view/edit the pin assignment.
## Uploading Device Profiles
Use the "Config Management" site to upload (Restore) the json file. Make sure to choose "Pin Mapping (pin_mapping.json)" in the combo box. After you click on restore the ESP will restart. At this point, the profile is not yet active. Please read the next chapter.
![Config Management](screenshots/14_ConfigManagement.png)
## Selecting a Device Profile
After you uploaded the device profile you can select the profile in the "Device Manager" view. After a click on "Save" the ESP will be restarted and the pin assignment is active. At this point the display should already show something. Please see the next chapter for display settings.
![Device Manager](screenshots/20_DeviceManager_Pin.png)
## Display Settings
Display settings can also be found in the "Device Manager".
![Device Manager Display](screenshots/21_DeviceManager_Display.png)

View File

@ -0,0 +1,275 @@
<mxfile host="65bd71144e">
<diagram name="Page-1" id="b5b7bab2-c9e2-2cf4-8b2a-24fd1a2a6d21">
<mxGraphModel dx="1370" dy="985" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" background="none" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="6e0c8c40b5770093-72" value="" style="shape=folder;fontStyle=1;spacingTop=10;tabWidth=194;tabHeight=22;tabPosition=left;html=1;rounded=0;shadow=0;comic=0;labelBackgroundColor=none;strokeWidth=1;fillColor=none;fontFamily=Verdana;fontSize=10;align=center;" parent="1" vertex="1">
<mxGeometry x="150" y="114.5" width="1090" height="1065.5" as="geometry"/>
</mxCell>
<mxCell id="6e0c8c40b5770093-73" value="&lt;div style=&quot;color: rgb(212, 212, 212); background-color: rgb(30, 30, 30); font-family: Menlo, Monaco, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; line-height: 18px;&quot;&gt;&lt;span style=&quot;color: #4ec9b0;&quot;&gt;PowerLimiterClass&lt;/span&gt;::&lt;span style=&quot;color: #dcdcaa;&quot;&gt;loop&lt;/span&gt;()&lt;/div&gt;" style="text;html=1;align=left;verticalAlign=top;spacingTop=-4;fontSize=10;fontFamily=Verdana" parent="1" vertex="1">
<mxGeometry x="150" y="114.5" width="130" height="20" as="geometry"/>
</mxCell>
<mxCell id="sqCMRMHiXPc9LqBIY9SA-1" value="" style="ellipse;html=1;shape=startState;fillColor=#000000;strokeColor=#ff0000;" parent="1" vertex="1">
<mxGeometry x="475" y="50" width="30" height="30" as="geometry"/>
</mxCell>
<mxCell id="sqCMRMHiXPc9LqBIY9SA-2" value="discover state on initial call or after enabling powerLimiter" style="html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;rounded=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="sqCMRMHiXPc9LqBIY9SA-1" target="2" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="205" y="370" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="sqCMRMHiXPc9LqBIY9SA-6" value="&lt;p style=&quot;margin:0px;margin-top:4px;text-align:center;&quot;&gt;STATE_OFF&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;/p&gt;&lt;p style=&quot;margin:0px;margin-left:8px;text-align:left;&quot;&gt;entry / stop inverter, limit lower limit&lt;br&gt;do / nothing&lt;br&gt;exit / nothing&lt;/p&gt;" style="shape=mxgraph.sysml.simpleState;html=1;overflow=fill;whiteSpace=wrap;align=center;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="170" y="585" width="200" height="100" as="geometry"/>
</mxCell>
<mxCell id="sqCMRMHiXPc9LqBIY9SA-7" value="&lt;p style=&quot;margin:0px;margin-top:4px;text-align:center;&quot;&gt;STATE_CONSUME_SOLAR_POWER_ONLY&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;/p&gt;&lt;p style=&quot;margin:0px;margin-left:8px;text-align:left;&quot;&gt;entry /&lt;br&gt;do / setNewLimit&lt;br&gt;exit /&lt;/p&gt;" style="shape=mxgraph.sysml.simpleState;html=1;overflow=fill;whiteSpace=wrap;align=center;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="525" y="590" width="270" height="90" as="geometry"/>
</mxCell>
<mxCell id="sqCMRMHiXPc9LqBIY9SA-8" value="&lt;p style=&quot;margin:0px;margin-top:4px;text-align:center;&quot;&gt;STATE_NORMAL_OPERATION&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;/p&gt;&lt;p style=&quot;margin:0px;margin-left:8px;text-align:left;&quot;&gt;entry /&lt;br&gt;do / setNewLimit&lt;br&gt;exit /&lt;/p&gt;" style="shape=mxgraph.sysml.simpleState;html=1;overflow=fill;whiteSpace=wrap;align=center;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="970" y="585" width="200" height="100" as="geometry"/>
</mxCell>
<mxCell id="2" value="&lt;p style=&quot;margin:0px;margin-top:4px;text-align:center;&quot;&gt;STATE_DISOVER&lt;/p&gt;&lt;hr&gt;&lt;p&gt;&lt;/p&gt;&lt;p style=&quot;margin:0px;margin-left:8px;text-align:left;&quot;&gt;entry /&amp;nbsp;&lt;br&gt;do / nothing&lt;br&gt;exit /&amp;nbsp;&lt;/p&gt;" style="shape=mxgraph.sysml.simpleState;html=1;overflow=fill;whiteSpace=wrap;align=center;fillColor=#f5f5f5;strokeColor=#666666;fontColor=#333333;" parent="1" vertex="1">
<mxGeometry x="390" y="180" width="200" height="100" as="geometry"/>
</mxCell>
<mxCell id="5" value="" style="shape=ellipse;html=1;dashed=0;whitespace=wrap;aspect=fixed;strokeWidth=5;perimeter=ellipsePerimeter;" parent="1" vertex="1">
<mxGeometry x="414" y="780" width="30" height="30" as="geometry"/>
</mxCell>
<mxCell id="7" value="&lt;p&gt;&lt;span style=&quot;font-size: 11px; background-color: rgb(255, 255, 255);&quot;&gt;!Inverter-&amp;gt;isProducing || isStopThresholdReached&lt;/span&gt;&lt;/p&gt;" style="rhombus;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="425" y="300" width="130" height="130" as="geometry"/>
</mxCell>
<mxCell id="10" value="&lt;span style=&quot;font-size: 11px; background-color: rgb(255, 255, 255);&quot;&gt;canUseDirectSolarPower&lt;/span&gt;" style="rhombus;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="595" y="390" width="130" height="120" as="geometry"/>
</mxCell>
<mxCell id="11" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="10" target="sqCMRMHiXPc9LqBIY9SA-7" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="620" y="540" as="sourcePoint"/>
<mxPoint x="670" y="490" as="targetPoint"/>
<Array as="points"/>
</mxGeometry>
</mxCell>
<mxCell id="20" value="yes" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="11" vertex="1" connectable="0">
<mxGeometry x="0.075" y="1" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="12" value="" style="endArrow=classic;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="2" target="7" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="484" y="280" as="sourcePoint"/>
<mxPoint x="420" y="220" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="13" value="" style="endArrow=classic;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryPerimeter=0;" parent="1" source="7" target="sqCMRMHiXPc9LqBIY9SA-6" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="570" y="290" as="sourcePoint"/>
<mxPoint x="400" y="330" as="targetPoint"/>
<Array as="points">
<mxPoint x="270" y="365"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="14" value="yes" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="13" vertex="1" connectable="0">
<mxGeometry x="0.3049" relative="1" as="geometry">
<mxPoint x="80" y="-105" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="15" value="" style="endArrow=classic;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="7" target="10" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="360" y="370" as="sourcePoint"/>
<mxPoint x="280" y="595" as="targetPoint"/>
<Array as="points">
<mxPoint x="660" y="365"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="16" value="no" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="15" vertex="1" connectable="0">
<mxGeometry x="0.3049" relative="1" as="geometry">
<mxPoint x="-45" y="-15" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="18" value="" style="endArrow=classic;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;edgeStyle=orthogonalEdgeStyle;" parent="1" source="10" target="sqCMRMHiXPc9LqBIY9SA-8" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="760" y="340" as="sourcePoint"/>
<mxPoint x="810" y="290" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="19" value="no" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="18" vertex="1" connectable="0">
<mxGeometry x="0.142" y="2" relative="1" as="geometry">
<mxPoint x="-239" y="-8" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="21" value="isStopThresholdReached" style="rhombus;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="220" y="730" width="130" height="130" as="geometry"/>
</mxCell>
<mxCell id="22" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="21" target="24" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="620" y="540" as="sourcePoint"/>
<mxPoint x="670" y="490" as="targetPoint"/>
<Array as="points">
<mxPoint x="285" y="995"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="31" value="no" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="22" vertex="1" connectable="0">
<mxGeometry x="-0.702" y="1" relative="1" as="geometry">
<mxPoint x="39" y="91" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="23" value="" style="endArrow=classic;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.581;exitY=0.998;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="sqCMRMHiXPc9LqBIY9SA-6" target="21" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="480" y="810" as="sourcePoint"/>
<mxPoint x="530" y="760" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="41" style="edgeStyle=none;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="24" target="25">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="42" value="no" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="41">
<mxGeometry x="0.36" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="24" value="canUseDirectSolarPower" style="rhombus;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="600" y="930" width="130" height="130" as="geometry"/>
</mxCell>
<mxCell id="25" value="isStartThresholdReached" style="rhombus;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="830" y="930" width="130" height="130" as="geometry"/>
</mxCell>
<mxCell id="29" value="" style="endArrow=classic;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="21" target="5" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="350" y="830" as="sourcePoint"/>
<mxPoint x="400" y="780" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="30" value="yes" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="29" vertex="1" connectable="0">
<mxGeometry x="-0.1" relative="1" as="geometry">
<mxPoint x="3" y="-15" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="JKiNQljIdbqBsyxyxwz1-38" value="" style="endArrow=classic;html=1;rounded=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="sqCMRMHiXPc9LqBIY9SA-7" target="sqCMRMHiXPc9LqBIY9SA-6" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="570" y="820" as="sourcePoint"/>
<mxPoint x="620" y="770" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="JKiNQljIdbqBsyxyxwz1-39" value="isStopThresholdReached ||&lt;br&gt;(!canUseDirectSolarPower &lt;br&gt;&amp;amp;&amp;amp; EMPTY_WHEN_FULL)" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="JKiNQljIdbqBsyxyxwz1-38" vertex="1" connectable="0">
<mxGeometry x="-0.2129" y="-2" relative="1" as="geometry">
<mxPoint x="-14" y="-33" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="JKiNQljIdbqBsyxyxwz1-40" value="isStartThresholdReached ||&lt;br style=&quot;border-color: var(--border-color);&quot;&gt;(!canUseDirectSolarPower&lt;br style=&quot;border-color: var(--border-color);&quot;&gt;&amp;amp;&amp;amp; EMPTY_AT_NIGHT)" style="endArrow=classic;html=1;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="sqCMRMHiXPc9LqBIY9SA-7" target="sqCMRMHiXPc9LqBIY9SA-8" edge="1">
<mxGeometry x="-0.0286" y="25" width="50" height="50" relative="1" as="geometry">
<mxPoint x="570" y="560" as="sourcePoint"/>
<mxPoint x="620" y="510" as="targetPoint"/>
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="JKiNQljIdbqBsyxyxwz1-41" value="" style="endArrow=classic;html=1;rounded=0;entryX=0.22;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;exitX=0.354;exitY=1.025;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="sqCMRMHiXPc9LqBIY9SA-8" target="sqCMRMHiXPc9LqBIY9SA-6" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="1040" y="680" as="sourcePoint"/>
<mxPoint x="890" y="750" as="targetPoint"/>
<Array as="points">
<mxPoint x="1040" y="1070"/>
<mxPoint x="214" y="1070"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="JKiNQljIdbqBsyxyxwz1-42" value="isStopThresholdReached ||" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="JKiNQljIdbqBsyxyxwz1-41" vertex="1" connectable="0">
<mxGeometry x="-0.2129" y="-2" relative="1" as="geometry">
<mxPoint x="-151" y="32" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="JKiNQljIdbqBsyxyxwz1-43" value="" style="shape=ellipse;html=1;dashed=0;whitespace=wrap;aspect=fixed;strokeWidth=5;perimeter=ellipsePerimeter;" parent="1" vertex="1">
<mxGeometry x="1055" y="799.7" width="30" height="30" as="geometry"/>
</mxCell>
<mxCell id="JKiNQljIdbqBsyxyxwz1-44" value="" style="endArrow=classic;html=1;exitX=0.82;exitY=1.003;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitPerimeter=0;" parent="1" target="JKiNQljIdbqBsyxyxwz1-43" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="1069.0000000000002" y="685" as="sourcePoint"/>
<mxPoint x="1119" y="749.7" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="JKiNQljIdbqBsyxyxwz1-45" value="inTargetRange do nothing" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="JKiNQljIdbqBsyxyxwz1-44" vertex="1" connectable="0">
<mxGeometry x="-0.1" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="JKiNQljIdbqBsyxyxwz1-46" value="" style="shape=ellipse;html=1;dashed=0;whitespace=wrap;aspect=fixed;strokeWidth=5;perimeter=ellipsePerimeter;" parent="1" vertex="1">
<mxGeometry x="530" y="795" width="30" height="30" as="geometry"/>
</mxCell>
<mxCell id="JKiNQljIdbqBsyxyxwz1-47" value="" style="endArrow=classic;html=1;exitX=0.82;exitY=1.003;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitPerimeter=0;" parent="1" target="JKiNQljIdbqBsyxyxwz1-46" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="544" y="680" as="sourcePoint"/>
<mxPoint x="594" y="745" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="JKiNQljIdbqBsyxyxwz1-48" value="after setNewLimit" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="JKiNQljIdbqBsyxyxwz1-47" vertex="1" connectable="0">
<mxGeometry x="-0.1" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="JKiNQljIdbqBsyxyxwz1-49" value="" style="shape=ellipse;html=1;dashed=0;whitespace=wrap;aspect=fixed;strokeWidth=5;perimeter=ellipsePerimeter;" parent="1" vertex="1">
<mxGeometry x="1110" y="800" width="30" height="30" as="geometry"/>
</mxCell>
<mxCell id="JKiNQljIdbqBsyxyxwz1-50" value="" style="endArrow=classic;html=1;exitX=0.82;exitY=1.003;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitPerimeter=0;" parent="1" target="JKiNQljIdbqBsyxyxwz1-49" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="1124.0000000000002" y="685.3" as="sourcePoint"/>
<mxPoint x="1174" y="750" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="JKiNQljIdbqBsyxyxwz1-51" value="after setNewLimit" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="JKiNQljIdbqBsyxyxwz1-50" vertex="1" connectable="0">
<mxGeometry x="-0.1" relative="1" as="geometry">
<mxPoint x="16" y="33" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="38" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="24" target="sqCMRMHiXPc9LqBIY9SA-7">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="580" y="710" as="sourcePoint"/>
<mxPoint x="630" y="660" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="39" value="yes" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="38">
<mxGeometry x="0.104" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="40" value="" style="endArrow=classic;html=1;entryX=1;entryY=0.75;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" target="sqCMRMHiXPc9LqBIY9SA-7">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="970" y="658" as="sourcePoint"/>
<mxPoint x="800" y="660" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="48" value="canUseDirectSolarPower&lt;br style=&quot;border-color: var(--border-color);&quot;&gt;&amp;amp;&amp;amp; EMPTY_AT_NIGHT" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="40">
<mxGeometry x="-0.04" y="1" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="43" value="" style="endArrow=classic;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.25;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="25" target="sqCMRMHiXPc9LqBIY9SA-8">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="960" y="980" as="sourcePoint"/>
<mxPoint x="1010" y="930" as="targetPoint"/>
<Array as="points">
<mxPoint x="1020" y="995"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="44" value="yes" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="43">
<mxGeometry x="0.1135" y="2" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="45" value="" style="shape=ellipse;html=1;dashed=0;whitespace=wrap;aspect=fixed;strokeWidth=5;perimeter=ellipsePerimeter;" vertex="1" parent="1">
<mxGeometry x="880" y="850" width="30" height="30" as="geometry"/>
</mxCell>
<mxCell id="46" value="" style="endArrow=classic;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="25" target="45">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="580" y="710" as="sourcePoint"/>
<mxPoint x="630" y="660" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="47" value="no" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="46">
<mxGeometry x="-0.08" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

View File

@ -1,3 +1,21 @@
# Upgrade Partition
This documentation has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/firmware/howto/upgrade_partition/>
To be able to install further updates you have to update the partition table of the ESP32. Doing so will **erase** all configuration data. Over The Air update using the web interface is **NOT** possible!
**So make sure you export a backup of your configuration files before continuing.**
There are several possibilities to update the partition table:
- Using Visual Studio Code or PlatformIO CLI
If you have already used Visual Studio Code or the `platformio` command you can use it again to install the latest version. The partition table is upgraded automatically.
- Any kind of flash interface
If you like to use any kind of flash interface like `esptool.py`, Espressif Flash Download Tool, ESP_Flasher or esptool-js you have to make sure to upload the provided .factory.bin file. It is important to enter the correct target address.
| Address | File |
| ---------| ---------------------- |
| 0x0 | opendtu-*.factory.bin |
After upgrading the ESP32 will open the intergrated access point (AP) again. Just connect to it using the default password ("openDTU42"). If you are connected, just visit <http://192.168.4.1> and enter the "Configuration Management". Recover the previously backuped config files.

View File

@ -1,3 +1,44 @@
# Web API
This documentation will has been moved and can be found here: <https://tbnobody.github.io/OpenDTU-docs/firmware/web_api/>
## List of URLs
This list may be incomplete
| GET/POST | Auth required | URL |
| -------- | --- | -- |
| Get | no | /api/vedirectlivedata/status |
| Get | no | /api/vedirect/status |
| Get | no | /api/huawei/status |
| Get | no | /api/huawei/config |
| Get | no | /api/huawei/limit/config |
| Get | no | /api/batterylivedata/status |
| Get | no | /api/battery/status |
| Get | no | /api/powerlimiter/status |
### Victron REST-API (/api/vedirectlivedata/status):
````JSON
{
"data_age":0,
"age_critical":false,
"PID":"SmartSolar MPPT 100|30",
"SER":"XXX",
"FW":"159",
"LOAD":"ON",
"CS":"Bulk",
"ERR":"No error",
"OR":"Not off",
"MPPT":"MPP Tracker active",
"HSDS":{"v":46,"u":"Days"},
"V":{"v":26.36,"u":"V"},
"I":{"v":3.4,"u":"A"},
"VPV":{"v":37.13,"u":"V"},
"PPV":{"v":93,"u":"W"},
"H19":{"v":83.16,"u":"kWh"},
"H20":{"v":1.39,"u":"kWh"},
"H21":{"v":719,"u":"W"},
"H22":{"v":1.43,"u":"kWh"},
"H23":{"v":737,"u":"W"}
}
````

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 261 KiB

194
docs/components.drawio.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 3.9 MiB

191
docs/hardware_flash.md Normal file
View File

@ -0,0 +1,191 @@
# Hardware, building and flashing Firmware
## Hardware you need
### ESP32 board
For ease of use, buy a "ESP32 DEVKIT DOIT" or "ESP32 NodeMCU Development Board" with an ESP32-S3 or ESP-WROOM-32 chipset on it.
Sample Picture:
![NodeMCU-ESP32](nodemcu-esp32.png)
Also supported: Board with Ethernet-Connector and Power-over-Ethernet [Olimex ESP32-POE](https://www.olimex.com/Products/IoT/ESP32/ESP32-POE/open-source-hardware)
### NRF24L01+ radio board (See inverter table above for supported inverters)
The PLUS sign is IMPORTANT! There are different variants available, with antenna on the printed circuit board or external antenna.
Sample picture:
![nrf24l01plus](nrf24l01plus.png)
Buy your hardware from a trusted source, at best from a dealer/online shop in your country where you have support and the right to return non-functional hardware.
When you want to buy from Amazon, AliExpress, eBay etc., take note that there is a lot of low-quality or fake hardware offered. Read customer comments and ratings carefully!
A heavily incomplete list of trusted hardware shops in Germany is:
* [AZ-Delivery](https://www.az-delivery.de/)
* [Makershop](https://www.makershop.de/)
* [Berrybase](https://www.berrybase.de/)
This list is for your convenience only, the project is not related to any of these shops.
### CMT2300A radio board (See inverter table above for supported inverters)
It is important to get a module which supports SPI communication. The following modules are currently supported:
* EBYTE E49-900M20S
The CMT2300A uses 3-Wire half duplex SPI communication. Due to this fact it currently requires a separate SPI bus. If you want to run the CMT2300A module on the same ESP32 as a NRF24L01+ module or a PCD8544 display make sure you get a ESP which supports 2 SPI busses. Currently the SPI bus host is hardcoded to number 2. This may change in future.
### 3.3V / 5V logic level converter
The logic level converter is used to interface with the Victron MPPT charge controller and the relay board. It converts the 3.3V logic level used by the ESP32 to 5V logic used by the other devices.
### SN65HVD230 CAN bus transceiver
The SN65HVD230 CAN bus transceiver is used to interface with the Pylontech battery. It leverages the CAN bus controller of the ESP32. This CAN bus operates at 500kbit/s
### MCP2515 CAN bus module
The MCP2515 CAN bus module consists of a CAN bus controller and a CAN bus transceiver and is used to interface with the Huawei AC charger. This CAN bus operates at 125kbit/s. The module is connected via SPI and currently requires a separate SPI bus. If you want to use the Huawei AC charger make sure to get an ESP which supports 2 SPI busses. Currently the SPI bus host is hardcoded to number 2. This may change in future. Please note: Using the Huawei AC charger in combination with the CMT2300A radio board is not supported at the moment.
MCP2515 CAN bus modules that are widely available are designed for 5V supply voltage. To make them work with 3.3V / the ESP32 a modification is required. [This modification is described here.](https://forums.raspberrypi.com/viewtopic.php?t=141052)
### Relay module
The Huawei PSU can be switched on / off using the slot detect port. This is done by this relay.
### Power supply
Use a power supply with 5 V and 1 A. The USB cable connected to your PC/Notebook may be powerful enough or may be not.
## Wiring up the NRF24L01+ module
### Schematic
![Schematic](Wiring_ESP32_Schematic.png)
### Symbolic view
![Symbolic](Wiring_ESP32_Symbol.png)
### Change pin assignment
Its possible to change all the pins of the NRF24L01+ module, the Display, the LED etc.
The recommend way to change the pin assignment is by creating a custom [device profile](DeviceProfiles.md).
It is also possible to create a custom environment and compile the source yourself. This can be achieved by copying one of the [env:....] sections from 'platformio.ini' to 'platformio_override.ini' and editing the 'platformio_override.ini' file and add/change one or more of the following lines to the 'build_flags' parameter:
```makefile
-DHOYMILES_PIN_MISO=19
-DHOYMILES_PIN_MOSI=23
-DHOYMILES_PIN_SCLK=18
-DHOYMILES_PIN_IRQ=16
-DHOYMILES_PIN_CE=4
-DHOYMILES_PIN_CS=5
-DVICTRON_PIN_TX=21
-DVICTRON_PIN_RX=22
-DPYLONTECH_PIN_RX=27
-DPYLONTECH_PIN_TX=14
-DHUAWEI_PIN_MISO=12
-DHUAWEI_PIN_MOSI=13
-DHUAWEI_PIN_SCLK=26
-DHUAWEI_PIN_IRQ=25
-DHUAWEI_PIN_CS=15
-DHUAWEI_PIN_POWER=33
```
It is recommended to make all changes only in the 'platformio_override.ini', this is your personal copy.
## Flashing and starting up
### with Visual Studio Code
* Install [Visual Studio Code](https://code.visualstudio.com/download) (from now named "vscode")
* In Visual Studio Code, install the [PlatformIO Extension](https://marketplace.visualstudio.com/items?itemName=platformio.platformio-ide)
* Install git and enable git in vscode - [git download](https://git-scm.com/downloads/) - [Instructions](https://www.jcchouinard.com/install-git-in-vscode/)
* Clone this repository (you really have to clone it, don't just download the ZIP file. During the build process the git hash gets embedded into the firmware. If you download the ZIP file a build error will occur): Inside vscode open the command palette by pressing `CTRL` + `SHIFT` + `P`. Enter `git clone`, add the repository-URL `https://github.com/tbnobody/OpenDTU`. Next you have to choose (or create) a target directory.
* In vscode, choose File --> Open Folder and select the previously downloaded source code. (You have to select the folder which contains the "platformio.ini" and "platformio_override.ini" file)
* Adjust the COM port in the file "platformio_override.ini" for your USB-to-serial-converter. It occurs twice:
* upload_port
* monitor_port
* Select the arrow button in the blue bottom status bar (PlatformIO: Upload) to compile and upload the firmware. During the compilation, all required libraries are downloaded automatically.
* Under Linux, if the upload fails with error messages "Could not open /dev/ttyUSB0, the port doesn't exist", you can check via ```ls -la /dev/tty*``` to which group your port belongs to, and then add your user this group via ```sudo adduser <yourusername> dialout``` (if you are using ```arch-linux``` use: ```sudo gpasswd -a <yourusername> uucp```, this method requires a logout/login of the affected user).
* There are two videos showing these steps:
* [Git Clone and compilation](https://youtu.be/9cA_esv3zeA)
* [Full installation and compilation](https://youtu.be/xs6TqHn7QWM)
### on the commandline with PlatformIO Core
* Install [PlatformIO Core](https://platformio.org/install/cli)
* Clone this repository (you really have to clone it, don't just download the ZIP file. During the build process the git hash gets embedded into the firmware. If you download the ZIP file a build error will occur)
* Adjust the COM port in the file "platformio_override.ini". It occurs twice:
* upload_port
* monitor_port
* build: `platformio run -e generic`
* upload to esp module: `platformio run -e generic -t upload`
* other options:
* clean the sources: `platformio run -e generic -t clean`
* erase flash: `platformio run -e generic -t erase`
### using the pre-compiled .bin files
The pre-compiled files can be found on the [github page](https://github.com/tbnobody/OpenDTU) in the tab "Actions" and the sub menu "OpenDTU Build". Just choose the latest build from the master branch (search for "master" in the blue font text but click on the white header text!). You need to be logged in with your github account to download the files.
Use a ESP32 flash tool of your choice (see next chapter) and flash the `.bin` files to the right addresses:
| Address | File |
| ---------| ---------------------- |
| 0x1000 | bootloader.bin |
| 0x8000 | partitions.bin |
| 0xe000 | boot_app0.bin |
| 0x10000 | opendtu-*.bin |
For further updates you can just use the web interface and upload the `opendtu-*.bin` file.
#### Flash with esptool.py (Linux)
```bash
esptool.py --port /dev/ttyUSB0 --chip esp32 --before default_reset --after hard_reset \
write_flash --flash_mode dout --flash_freq 40m --flash_size detect \
0x1000 bootloader.bin \
0x8000 partitions.bin \
0xe000 boot_app0.bin \
0x10000 opendtu-generic.bin
```
#### Flash with Espressif Flash Download Tool (Windows)
[Download link](https://www.espressif.com/en/support/download/other-tools)
* On startup, select Chip Type -> "ESP32" / WorkMode -> "Develop"
* Prepare all settings (see picture). Make sure to uncheck the `DoNotChgBin` option. Otherwise you may get errors like "invalid header".
* ![flash tool image](esp32_flash_download_tool.png)
* Press "Erase" button on screen. Look into the terminal window, you should see dots appear. Then press the "Boot" button on the ESP32 board. Wait for "FINISH" to see if flashing/erasing is done.
* To program, press "Start" on screen, then the "Boot" button.
* When flashing is complete (FINISH appears) then press the Reset button on the ESP32 board (or powercycle ) to start the OpenDTU application.
#### Flash with ESP_Flasher (Windows)
Users report that [ESP_Flasher](https://github.com/Jason2866/ESP_Flasher/releases/) is suitable for flashing OpenDTU on Windows.
#### Flash with [ESP_Flasher](https://espressif.github.io/esptool-js/) - web version
It is also possible to flash it via the web tools which might be more convenient and is platform independent.
## Flashing an Update using "Over The Air" OTA Update
Once you have your OpenDTU running and connected to WLAN, you can do further updates through the web interface.
Navigate to Settings --> Firmware upgrade and press the browse button. Select the firmware file from your local computer.
You'll find the firmware file (after a successful build process) under `.pio/build/generic/firmware.bin`.
If you downloaded a precompiled zip archive, unpack it and choose `opendtu-generic.bin`.
After the successful upload, the OpenDTU immediately restarts into the new firmware.
## Builds
Different builds from existing installations can be found here [Builds](builds/README.md)
Like to show your own build? Just send me a Pull Request.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 146 KiB

37
include/Battery.h Normal file
View File

@ -0,0 +1,37 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <stdint.h>
#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();
std::shared_ptr<BatteryStats const> getStats() const;
private:
void loop();
Task _loopTask;
uint32_t _lastMqttPublish = 0;
mutable std::mutex _mutex;
std::unique_ptr<BatteryProvider> _upProvider = nullptr;
};
extern BatteryClass Battery;

143
include/BatteryStats.h Normal file
View File

@ -0,0 +1,143 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <stdint.h>
#include "AsyncJson.h"
#include "Arduino.h"
#include "JkBmsDataPoints.h"
#include "VeDirectShuntController.h"
// mandatory interface for all kinds of batteries
class BatteryStats {
public:
String const& getManufacturer() const { return _manufacturer; }
// the last time *any* datum was updated
uint32_t getAgeSeconds() const { return (millis() - _lastUpdate) / 1000; }
bool updateAvailable(uint32_t since) const { return _lastUpdate > since; }
uint8_t getSoC() const { return _SoC; }
uint32_t getSoCAgeSeconds() const { return (millis() - _lastUpdateSoC) / 1000; }
// convert stats to JSON for web application live view
virtual void getLiveViewData(JsonVariant& root) const;
virtual void mqttPublish() const;
bool isValid() const { return _lastUpdateSoC > 0 && _lastUpdate > 0; }
protected:
String _manufacturer = "unknown";
uint8_t _SoC = 0;
uint32_t _lastUpdateSoC = 0;
uint32_t _lastUpdate = 0;
};
class PylontechBatteryStats : public BatteryStats {
friend class PylontechCanReceiver;
public:
void getLiveViewData(JsonVariant& root) const final;
void mqttPublish() const final;
private:
void setManufacturer(String&& m) { _manufacturer = std::move(m); }
void setSoC(uint8_t SoC) { _SoC = SoC; _lastUpdateSoC = millis(); }
void setLastUpdate(uint32_t ts) { _lastUpdate = ts; }
float _chargeVoltage;
float _chargeCurrentLimitation;
float _dischargeCurrentLimitation;
uint16_t _stateOfHealth;
float _voltage; // total voltage of the battery pack
// total current into (positive) or from (negative)
// the battery, i.e., the charging current
float _current;
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;
};
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;
void updateFrom(JkBms::DataPointContainer const& dp);
private:
void getJsonData(JsonVariant& root, bool verbose) const;
JkBms::DataPointContainer _dataPoints;
mutable uint32_t _lastMqttPublish = 0;
mutable uint32_t _lastFullMqttPublish = 0;
uint16_t _cellMinMilliVolt = 0;
uint16_t _cellAvgMilliVolt = 0;
uint16_t _cellMaxMilliVolt = 0;
uint32_t _cellVoltageTimestamp = 0;
};
class VictronSmartShuntStats : public BatteryStats {
public:
void getLiveViewData(JsonVariant& root) const final;
void mqttPublish() const final;
void updateFrom(VeDirectShuntController::veShuntStruct const& shuntData);
private:
float _voltage;
float _current;
float _temperature;
bool _tempPresent;
uint8_t _chargeCycles;
uint32_t _timeToGo;
float _chargedEnergy;
float _dischargedEnergy;
String _modelName;
bool _alarmLowVoltage;
bool _alarmHighVoltage;
bool _alarmLowSOC;
bool _alarmLowTemperature;
bool _alarmHighTemperature;
};
class MqttBatteryStats : public BatteryStats {
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 { }
// the SoC is the only interesting value in this case, which is already
// displayed at the top of the live view. do not generate a card.
void getLiveViewData(JsonVariant& root) const final { }
void setSoC(uint8_t SoC) { _SoC = SoC; _lastUpdateSoC = _lastUpdate = millis(); }
};

View File

@ -3,12 +3,9 @@
#include "PinMapping.h"
#include <cstdint>
#include <TaskSchedulerDeclarations.h>
#include <mutex>
#include <condition_variable>
#define CONFIG_FILENAME "/config.json"
#define CONFIG_VERSION 0x00011d00 // 0.1.29 // make sure to clean all after change
#define CONFIG_VERSION 0x00011900 // 0.1.24 // make sure to clean all after change
#define WIFI_MAX_SSID_STRLEN 32
#define WIFI_MAX_PASSWORD_STRLEN 64
@ -19,21 +16,30 @@
#define NTP_MAX_TIMEZONEDESCR_STRLEN 50
#define MQTT_MAX_HOSTNAME_STRLEN 128
#define MQTT_MAX_CLIENTID_STRLEN 64
#define MQTT_MAX_USERNAME_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_CERT_STRLEN 2560
#define INV_MAX_NAME_STRLEN 31
#define INV_MAX_COUNT 10
#define INV_MAX_COUNT 5
#define INV_MAX_CHAN_COUNT 6
#define CHAN_MAX_NAME_STRLEN 31
#define DEV_MAX_MAPPING_NAME_STRLEN 63
#define LOCALE_STRLEN 2
#define POWERMETER_MAX_PHASES 3
#define POWERMETER_MAX_HTTP_URL_STRLEN 1024
#define POWERMETER_MAX_USERNAME_STRLEN 64
#define POWERMETER_MAX_PASSWORD_STRLEN 64
#define POWERMETER_MAX_HTTP_HEADER_KEY_STRLEN 64
#define POWERMETER_MAX_HTTP_HEADER_VALUE_STRLEN 256
#define POWERMETER_MAX_HTTP_JSON_PATH_STRLEN 256
#define POWERMETER_HTTP_TIMEOUT 1000
#define JSON_BUFFER_SIZE 15360
struct CHANNEL_CONFIG_T {
uint16_t MaxChannelPower;
@ -52,11 +58,23 @@ struct INVERTER_CONFIG_T {
uint8_t ReachableThreshold;
bool ZeroRuntimeDataIfUnrechable;
bool ZeroYieldDayOnMidnight;
bool ClearEventlogOnMidnight;
bool YieldDayCorrection;
CHANNEL_CONFIG_T channel[INV_MAX_CHAN_COUNT];
};
enum Auth { none, basic, digest };
struct POWERMETER_HTTP_PHASE_CONFIG_T {
bool Enabled;
char Url[POWERMETER_MAX_HTTP_URL_STRLEN + 1];
Auth AuthType;
char Username[POWERMETER_MAX_USERNAME_STRLEN +1];
char Password[POWERMETER_MAX_USERNAME_STRLEN +1];
char HeaderKey[POWERMETER_MAX_HTTP_HEADER_KEY_STRLEN + 1];
char HeaderValue[POWERMETER_MAX_HTTP_HEADER_VALUE_STRLEN + 1];
uint16_t Timeout;
char JsonPath[POWERMETER_MAX_HTTP_JSON_PATH_STRLEN + 1];
};
struct CONFIG_T {
struct {
uint32_t Version;
@ -92,8 +110,8 @@ struct CONFIG_T {
struct {
bool Enabled;
char Hostname[MQTT_MAX_HOSTNAME_STRLEN + 1];
bool VerboseLogging;
uint32_t Port;
char ClientId[MQTT_MAX_CLIENTID_STRLEN + 1];
char Username[MQTT_MAX_USERNAME_STRLEN + 1];
char Password[MQTT_MAX_PASSWORD_STRLEN + 1];
char Topic[MQTT_MAX_TOPIC_STRLEN + 1];
@ -134,8 +152,8 @@ struct CONFIG_T {
struct {
int8_t PaLevel;
uint32_t Frequency;
uint8_t CountryMode;
} Cmt;
bool VerboseLogging;
} Dtu;
struct {
@ -148,49 +166,94 @@ struct CONFIG_T {
bool ScreenSaver;
uint8_t Rotation;
uint8_t Contrast;
char Locale[LOCALE_STRLEN + 1];
struct {
uint32_t Duration;
uint8_t Mode;
} Diagram;
uint8_t Language;
uint32_t DiagramDuration;
} Display;
struct {
uint8_t Brightness;
} Led_Single[PINMAPPING_LED_COUNT];
struct {
bool Enabled;
bool VerboseLogging;
bool UpdatesOnly;
} Vedirect;
struct {
bool Enabled;
bool VerboseLogging;
uint32_t Interval;
uint32_t Source;
char MqttTopicPowerMeter1[MQTT_MAX_TOPIC_STRLEN + 1];
char MqttTopicPowerMeter2[MQTT_MAX_TOPIC_STRLEN + 1];
char MqttTopicPowerMeter3[MQTT_MAX_TOPIC_STRLEN + 1];
uint32_t SdmBaudrate;
uint32_t SdmAddress;
uint32_t HttpInterval;
bool HttpIndividualRequests;
POWERMETER_HTTP_PHASE_CONFIG_T Http_Phase[POWERMETER_MAX_PHASES];
} PowerMeter;
struct {
bool Enabled;
bool VerboseLogging;
bool SolarPassThroughEnabled;
uint8_t SolarPassThroughLosses;
uint8_t BatteryDrainStategy;
uint32_t Interval;
bool IsInverterBehindPowerMeter;
uint8_t InverterId;
uint8_t InverterChannelId;
int32_t TargetPowerConsumption;
int32_t TargetPowerConsumptionHysteresis;
int32_t LowerPowerLimit;
int32_t UpperPowerLimit;
uint32_t BatterySocStartThreshold;
uint32_t BatterySocStopThreshold;
float VoltageStartThreshold;
float VoltageStopThreshold;
float VoltageLoadCorrectionFactor;
int8_t RestartHour;
uint32_t FullSolarPassThroughSoc;
float FullSolarPassThroughStartVoltage;
float FullSolarPassThroughStopVoltage;
} PowerLimiter;
struct {
bool Enabled;
bool VerboseLogging;
uint8_t Provider;
uint8_t JkBmsInterface;
uint8_t JkBmsPollingInterval;
char MqttTopic[MQTT_MAX_TOPIC_STRLEN + 1];
} Battery;
struct {
bool Enabled;
uint32_t CAN_Controller_Frequency;
bool Auto_Power_Enabled;
float Auto_Power_Voltage_Limit;
float Auto_Power_Enable_Voltage_Limit;
float Auto_Power_Lower_Power_Limit;
float Auto_Power_Upper_Power_Limit;
} Huawei;
INVERTER_CONFIG_T Inverter[INV_MAX_COUNT];
char Dev_PinMapping[DEV_MAX_MAPPING_NAME_STRLEN + 1];
};
class ConfigurationClass {
public:
void init(Scheduler& scheduler);
void init();
bool read();
bool write();
void migrate();
CONFIG_T const& get();
class WriteGuard {
public:
WriteGuard();
CONFIG_T& getConfig();
~WriteGuard();
private:
std::unique_lock<std::mutex> _lock;
};
WriteGuard getWriteGuard();
CONFIG_T& get();
INVERTER_CONFIG_T* getFreeInverterSlot();
INVERTER_CONFIG_T* getInverterConfig(const uint64_t serial);
void deleteInverterById(const uint8_t id);
private:
void loop();
Task _loopTask;
};
extern ConfigurationClass Configuration;

View File

@ -6,7 +6,6 @@
class DatastoreClass {
public:
DatastoreClass();
void init(Scheduler& scheduler);
// Sum of yield total of all enabled inverters, a inverter which is just disabled at night is also included

View File

@ -6,31 +6,15 @@
#include <TaskSchedulerDeclarations.h>
#include <U8g2lib.h>
#define CHART_HEIGHT 20 // chart area hight in pixels
#define CHART_WIDTH 47 // chart area width in pixels
// Left-Upper position of diagram is drawn
// (text of Y-axis is display left of that pos)
#define CHART_POSX 80
#define CHART_POSY 0
enum DisplayType_t {
None,
PCD8544,
SSD1306,
SH1106,
SSD1309,
ST7567_GM12864I_59N,
DisplayType_Max,
};
enum DiagramMode_t {
Off,
Small,
Fullscreen,
DisplayMode_Max,
};
class DisplayGraphicClass {
public:
DisplayGraphicClass();
@ -40,8 +24,7 @@ public:
void setContrast(const uint8_t contrast);
void setStatus(const bool turnOn);
void setOrientation(const uint8_t rotation = DISPLAY_ROTATION);
void setLocale(const String& locale);
void setDiagramMode(DiagramMode_t mode);
void setLanguage(const uint8_t language);
void setStartupDisplay();
DisplayGraphicDiagramClass& Diagram();
@ -64,24 +47,14 @@ private:
bool _displayTurnedOn;
DisplayType_t _display_type = DisplayType_t::None;
DiagramMode_t _diagram_mode = DiagramMode_t::Off;
String _display_language = DISPLAY_LOCALE;
uint8_t _display_language = DISPLAY_LANGUAGE;
uint8_t _mExtra;
const uint16_t _period = 1000;
const uint16_t _interval = 60000; // interval at which to power save (milliseconds)
uint16_t _period = 1000;
uint16_t _interval = 60000; // interval at which to power save (milliseconds)
uint32_t _previousMillis = 0;
char _fmtText[32];
bool _isLarge = false;
uint8_t _lineOffsets[5];
String _i18n_offline;
String _i18n_yield_today_kwh;
String _i18n_yield_today_wh;
String _i18n_date_format;
String _i18n_current_power_kw;
String _i18n_current_power_w;
String _i18n_yield_total_mwh;
String _i18n_yield_total_kwh;
};
extern DisplayGraphicClass Display;

View File

@ -5,14 +5,20 @@
#include <U8g2lib.h>
#include <array>
#define MAX_DATAPOINTS 128
#define CHART_HEIGHT 20 // chart area hight in pixels
#define CHART_WIDTH 47 // chart area width in pixels
// Left-Upper position of diagram is drawn
// (text of Y-axis is display left of that pos)
#define DIAG_POSX 80
#define DIAG_POSY 0
class DisplayGraphicDiagramClass {
public:
DisplayGraphicDiagramClass();
void init(Scheduler& scheduler, U8G2* display);
void redraw(uint8_t screenSaverOffsetX, uint8_t xPos, uint8_t yPos, uint8_t width, uint8_t height, bool isFullscreen);
void redraw(uint8_t screenSaverOffsetX);
void updatePeriod();
@ -20,17 +26,15 @@ private:
void averageLoop();
void dataPointLoop();
uint32_t getSecondsPerDot();
static uint32_t getSecondsPerDot();
Task _averageTask;
Task _dataPointTask;
U8G2* _display = nullptr;
std::array<float, MAX_DATAPOINTS> _graphValues = {};
std::array<float, CHART_WIDTH> _graphValues = {};
uint8_t _graphValuesCount = 0;
uint8_t _chartWidth = MAX_DATAPOINTS;
float _iRunningAverage = 0;
uint16_t _iRunningAverageCnt = 0;
};

26
include/HttpPowerMeter.h Normal file
View File

@ -0,0 +1,26 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <stdint.h>
#include <Arduino.h>
#include <HTTPClient.h>
class HttpPowerMeterClass {
public:
void init();
bool updateValues();
float getPower(int8_t phase);
bool httpRequest(const char* url, Auth authType, const char* username, const char* password, const char* httpHeader, const char* httpValue, uint32_t timeout,
char* response, size_t responseSize, char* error, size_t errorSize);
float getFloatValueByJsonPath(const char* jsonString, const char* jsonPath, float &value);
private:
void extractUrlComponents(const String& url, String& protocol, String& hostname, String& uri);
void prepareRequest(uint32_t timeout, const char* httpHeader, const char* httpValue);
HTTPClient httpClient;
float power[POWERMETER_MAX_PHASES];
String sha256(const String& data);
};
extern HttpPowerMeterClass HttpPowerMeter;

156
include/Huawei_can.h Normal file
View File

@ -0,0 +1,156 @@
// 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();
bool getAutoPowerStatus();
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;
};
extern HuaweiCanClass HuaweiCan;
extern HuaweiCanCommClass HuaweiCanComm;

View File

@ -1,35 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <TaskSchedulerDeclarations.h>
#include <WString.h>
#include <list>
struct LanguageInfo_t {
String code;
String name;
String filename;
};
class I18nClass {
public:
I18nClass();
void init(Scheduler& scheduler);
std::list<LanguageInfo_t> getAvailableLanguages();
String getFilenameByLocale(const String& locale) const;
void readDisplayStrings(
const String& locale,
String& date_format,
String& offline,
String& power_w, String& power_kw,
String& yield_today_wh, String& yield_today_kwh,
String& yield_total_kwh, String& yield_total_mwh);
private:
void readLangPacks();
void readConfig(String file);
std::list<LanguageInfo_t> _availLanguages;
};
extern I18nClass I18n;

View File

@ -8,7 +8,6 @@
class InverterSettingsClass {
public:
InverterSettingsClass();
void init(Scheduler& scheduler);
private:

75
include/JkBmsController.h Normal file
View File

@ -0,0 +1,75 @@
#pragma once
#include <memory>
#include <vector>
#include "Battery.h"
#include "JkBmsSerialMessage.h"
class DataPointContainer;
namespace JkBms {
class Controller : public BatteryProvider {
public:
Controller() = default;
bool init(bool verboseLogging) final;
void deinit() final;
void loop() final;
std::shared_ptr<BatteryStats> getStats() const final { return _stats; }
private:
enum class Status : unsigned {
Initializing,
Timeout,
WaitingForPollInterval,
HwSerialNotAvailableForWrite,
BusyReading,
RequestSent,
FrameCompleted
};
std::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 */

302
include/JkBmsDataPoints.h Normal file
View File

@ -0,0 +1,302 @@
#pragma once
#include <Arduino.h>
#include <map>
#include <optional>
#include <string>
#include <unordered_map>
#include <variant>
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 std::map<AlarmBits, std::string> 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 std::map<StatusBits, std::string> StatusBitTexts = {
#define STATUS_TEXT(name, value) { StatusBits::name, #name },
STATUS_BITS(STATUS_TEXT)
#undef STATUS_TEXT
};
enum class DataPointLabel : uint8_t {
CellsMilliVolt = 0x79,
BmsTempCelsius = 0x80,
BatteryTempOneCelsius = 0x81,
BatteryTempTwoCelsius = 0x82,
BatteryVoltageMilliVolt = 0x83,
BatteryCurrentMilliAmps = 0x84,
BatterySoCPercent = 0x85,
BatteryTemperatureSensorAmount = 0x86,
BatteryCycles = 0x87,
BatteryCycleCapacity = 0x89,
BatteryCellAmount = 0x8a,
AlarmsBitmask = 0x8b,
StatusBitmask = 0x8c,
TotalOvervoltageThresholdMilliVolt = 0x8e,
TotalUndervoltageThresholdMilliVolt = 0x8f,
CellOvervoltageThresholdMilliVolt = 0x90,
CellOvervoltageRecoveryMilliVolt = 0x91,
CellOvervoltageProtectionDelaySeconds = 0x92,
CellUndervoltageThresholdMilliVolt = 0x93,
CellUndervoltageRecoveryMilliVolt = 0x94,
CellUndervoltageProtectionDelaySeconds = 0x95,
CellVoltageDiffThresholdMilliVolt = 0x96,
DischargeOvercurrentThresholdAmperes = 0x97,
DischargeOvercurrentDelaySeconds = 0x98,
ChargeOvercurrentThresholdAmps = 0x99,
ChargeOvercurrentDelaySeconds = 0x9a,
BalanceCellVoltageThresholdMilliVolt = 0x9b,
BalanceVoltageDiffThresholdMilliVolt = 0x9c,
BalancingEnabled = 0x9d,
BmsTempProtectionThresholdCelsius = 0x9e,
BmsTempRecoveryThresholdCelsius = 0x9f,
BatteryTempProtectionThresholdCelsius = 0xa0,
BatteryTempRecoveryThresholdCelsius = 0xa1,
BatteryTempDiffThresholdCelsius = 0xa2,
ChargeHighTempThresholdCelsius = 0xa3,
DischargeHighTempThresholdCelsius = 0xa4,
ChargeLowTempThresholdCelsius = 0xa5,
ChargeLowTempRecoveryCelsius = 0xa6,
DischargeLowTempThresholdCelsius = 0xa7,
DischargeLowTempRecoveryCelsius = 0xa8,
CellAmountSetting = 0xa9,
BatteryCapacitySettingAmpHours = 0xaa,
BatteryChargeEnabled = 0xab,
BatteryDischargeEnabled = 0xac,
CurrentCalibrationMilliAmps = 0xad,
BmsAddress = 0xae,
BatteryType = 0xaf,
SleepWaitTime = 0xb0, // what's this?
LowCapacityAlarmThresholdPercent = 0xb1,
ModificationPassword = 0xb2,
DedicatedChargerSwitch = 0xb3, // what's this?
EquipmentId = 0xb4,
DateOfManufacturing = 0xb5,
BmsHourMeterMinutes = 0xb6,
BmsSoftwareVersion = 0xb7,
CurrentCalibration = 0xb8,
ActualBatteryCapacityAmpHours = 0xb9,
ProductId = 0xba,
ProtocolVersion = 0xc0
};
using tCells = std::map<uint8_t, uint16_t>;
template<DataPointLabel> struct DataPointLabelTraits;
#define LABEL_TRAIT(n, t, u) template<> struct DataPointLabelTraits<DataPointLabel::n> { \
using type = t; \
static constexpr char const name[] = #n; \
static constexpr char const unit[] = u; \
};
/**
* the types associated with the labels are the types for the respective data
* points in the JkBms::DataPoint class. they are *not* always equal to the
* type used in the serial message.
*
* it is unfortunate that we have to repeat all enum values here to define the
* traits. code generation could help here (labels are defined in a single
* source of truth and this code is generated -- no typing errors, etc.).
* however, the compiler will complain if an enum is misspelled or traits are
* defined for a removed enum, so we will notice. it will also complain when a
* trait is missing and if a data point for a label without traits is added to
* the DataPointContainer class, because the traits must be available then.
* even though this is tedious to maintain, human errors will be caught.
*/
LABEL_TRAIT(CellsMilliVolt, tCells, "mV");
LABEL_TRAIT(BmsTempCelsius, int16_t, "°C");
LABEL_TRAIT(BatteryTempOneCelsius, int16_t, "°C");
LABEL_TRAIT(BatteryTempTwoCelsius, int16_t, "°C");
LABEL_TRAIT(BatteryVoltageMilliVolt, uint32_t, "mV");
LABEL_TRAIT(BatteryCurrentMilliAmps, int32_t, "mA");
LABEL_TRAIT(BatterySoCPercent, uint8_t, "%");
LABEL_TRAIT(BatteryTemperatureSensorAmount, uint8_t, "");
LABEL_TRAIT(BatteryCycles, uint16_t, "");
LABEL_TRAIT(BatteryCycleCapacity, uint32_t, "Ah");
LABEL_TRAIT(BatteryCellAmount, uint16_t, "");
LABEL_TRAIT(AlarmsBitmask, uint16_t, "");
LABEL_TRAIT(StatusBitmask, uint16_t, "");
LABEL_TRAIT(TotalOvervoltageThresholdMilliVolt, uint32_t, "mV");
LABEL_TRAIT(TotalUndervoltageThresholdMilliVolt, uint32_t, "mV");
LABEL_TRAIT(CellOvervoltageThresholdMilliVolt, uint16_t, "mV");
LABEL_TRAIT(CellOvervoltageRecoveryMilliVolt, uint16_t, "mV");
LABEL_TRAIT(CellOvervoltageProtectionDelaySeconds, uint16_t, "s");
LABEL_TRAIT(CellUndervoltageThresholdMilliVolt, uint16_t, "mV");
LABEL_TRAIT(CellUndervoltageRecoveryMilliVolt, uint16_t, "mV");
LABEL_TRAIT(CellUndervoltageProtectionDelaySeconds, uint16_t, "s");
LABEL_TRAIT(CellVoltageDiffThresholdMilliVolt, uint16_t, "mV");
LABEL_TRAIT(DischargeOvercurrentThresholdAmperes, uint16_t, "A");
LABEL_TRAIT(DischargeOvercurrentDelaySeconds, uint16_t, "s");
LABEL_TRAIT(ChargeOvercurrentThresholdAmps, uint16_t, "A");
LABEL_TRAIT(ChargeOvercurrentDelaySeconds, uint16_t, "s");
LABEL_TRAIT(BalanceCellVoltageThresholdMilliVolt, uint16_t, "mV");
LABEL_TRAIT(BalanceVoltageDiffThresholdMilliVolt, uint16_t, "mV");
LABEL_TRAIT(BalancingEnabled, bool, "");
LABEL_TRAIT(BmsTempProtectionThresholdCelsius, uint16_t, "°C");
LABEL_TRAIT(BmsTempRecoveryThresholdCelsius, uint16_t, "°C");
LABEL_TRAIT(BatteryTempProtectionThresholdCelsius, uint16_t, "°C");
LABEL_TRAIT(BatteryTempRecoveryThresholdCelsius, uint16_t, "°C");
LABEL_TRAIT(BatteryTempDiffThresholdCelsius, uint16_t, "°C");
LABEL_TRAIT(ChargeHighTempThresholdCelsius, uint16_t, "°C");
LABEL_TRAIT(DischargeHighTempThresholdCelsius, uint16_t, "°C");
LABEL_TRAIT(ChargeLowTempThresholdCelsius, int16_t, "°C");
LABEL_TRAIT(ChargeLowTempRecoveryCelsius, int16_t, "°C");
LABEL_TRAIT(DischargeLowTempThresholdCelsius, int16_t, "°C");
LABEL_TRAIT(DischargeLowTempRecoveryCelsius, int16_t, "°C");
LABEL_TRAIT(CellAmountSetting, uint8_t, "");
LABEL_TRAIT(BatteryCapacitySettingAmpHours, uint32_t, "Ah");
LABEL_TRAIT(BatteryChargeEnabled, bool, "");
LABEL_TRAIT(BatteryDischargeEnabled, bool, "");
LABEL_TRAIT(CurrentCalibrationMilliAmps, uint16_t, "mA");
LABEL_TRAIT(BmsAddress, uint8_t, "");
LABEL_TRAIT(BatteryType, uint8_t, "");
LABEL_TRAIT(SleepWaitTime, uint16_t, "s");
LABEL_TRAIT(LowCapacityAlarmThresholdPercent, uint8_t, "%");
LABEL_TRAIT(ModificationPassword, std::string, "");
LABEL_TRAIT(DedicatedChargerSwitch, bool, "");
LABEL_TRAIT(EquipmentId, std::string, "");
LABEL_TRAIT(DateOfManufacturing, std::string, "");
LABEL_TRAIT(BmsHourMeterMinutes, uint32_t, "min");
LABEL_TRAIT(BmsSoftwareVersion, std::string, "");
LABEL_TRAIT(CurrentCalibration, bool, "");
LABEL_TRAIT(ActualBatteryCapacityAmpHours, uint32_t, "Ah");
LABEL_TRAIT(ProductId, std::string, "");
LABEL_TRAIT(ProtocolVersion, uint8_t, "");
#undef LABEL_TRAIT
class DataPoint {
friend class DataPointContainer;
public:
using tValue = std::variant<bool, uint8_t, uint16_t, uint32_t,
int16_t, int32_t, std::string, tCells>;
DataPoint() = delete;
DataPoint(DataPoint const& other)
: _strLabel(other._strLabel)
, _strValue(other._strValue)
, _strUnit(other._strUnit)
, _value(other._value)
, _timestamp(other._timestamp) { }
DataPoint(std::string const& strLabel, std::string const& strValue,
std::string const& strUnit, tValue value, uint32_t timestamp)
: _strLabel(strLabel)
, _strValue(strValue)
, _strUnit(strUnit)
, _value(std::move(value))
, _timestamp(timestamp) { }
std::string const& getLabelText() const { return _strLabel; }
std::string const& getValueText() const { return _strValue; }
std::string const& getUnitText() const { return _strUnit; }
uint32_t getTimestamp() const { return _timestamp; }
bool operator==(DataPoint const& other) const {
return _value == other._value;
}
private:
std::string _strLabel;
std::string _strValue;
std::string _strUnit;
tValue _value;
uint32_t _timestamp;
};
template<typename T> std::string dataPointValueToStr(T const& v);
class DataPointContainer {
public:
DataPointContainer() = default;
using Label = DataPointLabel;
template<Label L> using Traits = JkBms::DataPointLabelTraits<L>;
template<Label L>
void add(typename Traits<L>::type val) {
_dataPoints.emplace(
L,
DataPoint(
Traits<L>::name,
dataPointValueToStr(val),
Traits<L>::unit,
DataPoint::tValue(std::move(val)),
millis()
)
);
}
// make sure add() is only called with the type expected for the
// respective label, no implicit conversions allowed.
template<Label L, typename T>
void add(T) = delete;
template<Label L>
std::optional<DataPoint const> getDataPointFor() const {
auto it = _dataPoints.find(L);
if (it == _dataPoints.end()) { return std::nullopt; }
return it->second;
}
template<Label L>
std::optional<typename Traits<L>::type> get() const {
auto optionalDataPoint = getDataPointFor<L>();
if (!optionalDataPoint.has_value()) { return std::nullopt; }
return std::get<typename Traits<L>::type>(optionalDataPoint->_value);
}
using tMap = std::unordered_map<Label, DataPoint const>;
tMap::const_iterator cbegin() const { return _dataPoints.cbegin(); }
tMap::const_iterator cend() const { return _dataPoints.cend(); }
// copy all data points from source into this instance, overwriting
// existing data points in this instance.
void updateFrom(DataPointContainer const& source);
private:
tMap _dataPoints;
};
} /* namespace JkBms */

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

22
include/MqttBattery.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#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;
std::shared_ptr<MqttBatteryStats> _stats = std::make_shared<MqttBatteryStats>();
void onMqttMessage(espMqttClientTypes::MessageProperties const& properties,
char const* topic, uint8_t const* payload, size_t len, size_t index, size_t total);
};

View File

@ -6,7 +6,6 @@
class MqttHandleDtuClass {
public:
MqttHandleDtuClass();
void init(Scheduler& scheduler);
private:

View File

@ -6,42 +6,29 @@
#include <TaskSchedulerDeclarations.h>
// mqtt discovery device classes
enum DeviceClassType {
enum {
DEVICE_CLS_NONE = 0,
DEVICE_CLS_CURRENT,
DEVICE_CLS_ENERGY,
DEVICE_CLS_PWR,
DEVICE_CLS_VOLTAGE,
DEVICE_CLS_FREQ,
DEVICE_CLS_TEMP,
DEVICE_CLS_POWER_FACTOR,
DEVICE_CLS_REACTIVE_POWER,
DEVICE_CLS_CONNECTIVITY,
DEVICE_CLS_DURATION,
DEVICE_CLS_SIGNAL_STRENGTH,
DEVICE_CLS_TEMPERATURE,
DEVICE_CLS_RESTART
DEVICE_CLS_REACTIVE_POWER
};
const char* const deviceClass_name[] = { 0, "current", "energy", "power", "voltage", "frequency", "power_factor", "reactive_power", "connectivity", "duration", "signal_strength", "temperature", "restart" };
enum StateClassType {
const char* const deviceClasses[] = { 0, "current", "energy", "power", "voltage", "frequency", "temperature", "power_factor", "reactive_power" };
enum {
STATE_CLS_NONE = 0,
STATE_CLS_MEASUREMENT,
STATE_CLS_TOTAL_INCREASING
};
const char* const stateClass_name[] = { 0, "measurement", "total_increasing" };
enum CategoryType {
CATEGORY_NONE = 0,
CATEGORY_CONFIG,
CATEGORY_DIAGNOSTIC
};
const char* const category_name[] = { 0, "config", "diagnostic" };
const char* const stateClasses[] = { 0, "measurement", "total_increasing" };
typedef struct {
FieldId_t fieldId; // field id
DeviceClassType deviceClsId; // device class
StateClassType stateClsId; // state class
uint8_t deviceClsId; // device class
uint8_t stateClsId; // state class
} byteAssign_fieldDeviceClass_t;
const byteAssign_fieldDeviceClass_t deviceFieldAssignment[] = {
@ -54,7 +41,7 @@ const byteAssign_fieldDeviceClass_t deviceFieldAssignment[] = {
{ FLD_IAC, DEVICE_CLS_CURRENT, STATE_CLS_MEASUREMENT },
{ FLD_PAC, DEVICE_CLS_PWR, STATE_CLS_MEASUREMENT },
{ FLD_F, DEVICE_CLS_FREQ, STATE_CLS_MEASUREMENT },
{ FLD_T, DEVICE_CLS_TEMPERATURE, STATE_CLS_MEASUREMENT },
{ FLD_T, DEVICE_CLS_TEMP, STATE_CLS_MEASUREMENT },
{ FLD_PF, DEVICE_CLS_POWER_FACTOR, STATE_CLS_MEASUREMENT },
{ FLD_EFF, DEVICE_CLS_NONE, STATE_CLS_NONE },
{ FLD_IRR, DEVICE_CLS_NONE, STATE_CLS_NONE },
@ -64,36 +51,24 @@ const byteAssign_fieldDeviceClass_t deviceFieldAssignment[] = {
class MqttHandleHassClass {
public:
MqttHandleHassClass();
void init(Scheduler& scheduler);
void publishConfig();
void forceUpdate();
private:
void loop();
static void publish(const String& subtopic, const String& payload);
static void publish(const String& subtopic, const JsonDocument& doc);
void publish(const String& subtopic, const String& payload);
void publishDtuSensor(const char* name, const char* device_class, const char* category, const char* icon, const char* unit_of_measure, const char* subTopic);
void publishDtuBinarySensor(const char* name, const char* device_class, const char* category, const char* payload_on, const char* payload_off, const char* subTopic = "");
void publishInverterField(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const byteAssign_fieldDeviceClass_t fieldType, const bool clear = false);
void publishInverterButton(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* icon, const char* category, const char* deviceClass, const char* subTopic, const char* payload);
void publishInverterNumber(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* icon, const char* category, const char* commandTopic, const char* stateTopic, const char* unitOfMeasure, const int16_t min = 1, const int16_t max = 100);
void publishInverterBinarySensor(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* subTopic, const char* payload_on, const char* payload_off);
static void addCommonMetadata(JsonDocument& doc, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category);
static void createInverterInfo(DynamicJsonDocument& doc, std::shared_ptr<InverterAbstract> inv);
static void createDtuInfo(DynamicJsonDocument& doc);
// Binary Sensor
static void publishBinarySensor(JsonDocument& doc, const String& root_device, const String& unique_id_prefix, const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category);
static void publishDtuBinarySensor(const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category);
static void publishInverterBinarySensor(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category);
// Sensor
static void publishSensor(JsonDocument& doc, const String& root_device, const String& unique_id_prefix, const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category);
static void publishDtuSensor(const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category);
static void publishInverterSensor(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category);
static void publishInverterField(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const byteAssign_fieldDeviceClass_t fieldType, const bool clear = false);
static void publishInverterButton(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& payload, const String& icon, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category);
static void publishInverterNumber(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& command_topic, const int16_t min, const int16_t max, float step, const String& unit_of_measure, const String& icon, const StateClassType state_class, const CategoryType category);
static void createInverterInfo(JsonDocument& doc, std::shared_ptr<InverterAbstract> inv);
static void createDtuInfo(JsonDocument& doc);
static void createDeviceInfo(JsonDocument& doc, const String& name, const String& identifiers, const String& configuration_url, const String& manufacturer, const String& model, const String& sw_version, const String& via_device = "");
static void createDeviceInfo(DynamicJsonDocument& 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();

View File

@ -0,0 +1,44 @@
// 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>
class MqttHandleHuaweiClass {
public:
void init(Scheduler& scheduler);
private:
void loop();
enum class Topic : unsigned {
LimitOnlineVoltage,
LimitOnlineCurrent,
LimitOfflineVoltage,
LimitOfflineCurrent,
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

@ -5,22 +5,17 @@
#include <Hoymiles.h>
#include <TaskSchedulerDeclarations.h>
#include <espMqttClient.h>
#include <frozen/map.h>
#include <frozen/string.h>
class MqttHandleInverterClass {
public:
MqttHandleInverterClass();
void init(Scheduler& scheduler);
static String getTopic(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId);
void subscribeTopics();
void unsubscribeTopics();
private:
void loop();
void publishField(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId);
void onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, const size_t len, const size_t index, const size_t total);
Task _loopTask;
@ -42,29 +37,6 @@ private:
FLD_IRR,
FLD_Q
};
enum class Topic : unsigned {
LimitPersistentRelative,
LimitPersistentAbsolute,
LimitNonPersistentRelative,
LimitNonPersistentAbsolute,
Power,
Restart,
ResetRfStats,
};
static constexpr frozen::string _cmdtopic = "+/cmd/";
static constexpr frozen::map<frozen::string, Topic, 7> _subscriptions = {
{ "limit_persistent_relative", Topic::LimitPersistentRelative },
{ "limit_persistent_absolute", Topic::LimitPersistentAbsolute },
{ "limit_nonpersistent_relative", Topic::LimitNonPersistentRelative },
{ "limit_nonpersistent_absolute", Topic::LimitNonPersistentAbsolute },
{ "power", Topic::Power },
{ "restart", Topic::Restart },
{ "reset_rf_stats", Topic::ResetRfStats },
};
void onMqttMessage(Topic t, const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, const size_t len, const size_t index, const size_t total);
};
extern MqttHandleInverterClass MqttHandleInverter;

View File

@ -5,7 +5,6 @@
class MqttHandleInverterTotalClass {
public:
MqttHandleInverterTotalClass();
void init(Scheduler& scheduler);
private:

View File

@ -0,0 +1,31 @@
// 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>
class MqttHandlePowerLimiterClass {
public:
void init(Scheduler& scheduler);
private:
void loop();
void onCmdMode(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 MqttHandlePylontechHassClass {
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);
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 _wasConnected = false;
bool _updateForced = false;
String serial = "0001"; // pseudo-serial, can be replaced in future with real serialnumber
};
extern MqttHandlePylontechHassClass MqttHandlePylontechHass;

View File

@ -0,0 +1,36 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "VeDirectMpptController.h"
#include "Configuration.h"
#include <Arduino.h>
#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();
VeDirectMpptController::veMpptStruct _kvFrame{};
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;
};
extern MqttHandleVedirectClass MqttHandleVedirect;

View File

@ -0,0 +1,27 @@
// 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);
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 _wasConnected = false;
bool _updateForced = false;
};
extern MqttHandleVedirectHassClass MqttHandleVedirectHass;

View File

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

View File

@ -1,7 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "W5500.h"
#include <DNSServer.h>
#include <TaskSchedulerDeclarations.h>
#include <WiFi.h>
@ -24,18 +23,18 @@ enum class network_event {
NETWORK_EVENT_MAX
};
typedef std::function<void(network_event event)> DtuNetworkEventCb;
typedef std::function<void(network_event event)> NetworkEventCb;
typedef struct DtuNetworkEventCbList {
DtuNetworkEventCb cb;
typedef struct NetworkEventCbList {
NetworkEventCb cb;
network_event event;
DtuNetworkEventCbList()
NetworkEventCbList()
: cb(nullptr)
, event(network_event::NETWORK_UNKNOWN)
{
}
} DtuNetworkEventCbList_t;
} NetworkEventCbList_t;
class NetworkSettingsClass {
public:
@ -54,7 +53,7 @@ public:
bool isConnected() const;
network_mode NetworkMode() const;
bool onEvent(DtuNetworkEventCb cbEvent, const network_event event = network_event::NETWORK_EVENT_MAX);
bool onEvent(NetworkEventCb cbEvent, const network_event event = network_event::NETWORK_EVENT_MAX);
void raiseEvent(const network_event event);
private:
@ -63,7 +62,7 @@ private:
void setStaticIp();
void handleMDNS();
void setupMode();
void NetworkEvent(const WiFiEvent_t event, WiFiEventInfo_t info);
void NetworkEvent(const WiFiEvent_t event);
Task _loopTask;
@ -82,9 +81,8 @@ private:
bool _dnsServerStatus = false;
network_mode _networkMode = network_mode::Undefined;
bool _ethConnected = false;
std::vector<DtuNetworkEventCbList_t> _cbEventList;
std::vector<NetworkEventCbList_t> _cbEventList;
bool _lastMdnsEnabled = false;
std::unique_ptr<W5500> _w5500;
};
extern NetworkSettingsClass NetworkSettings;

View File

@ -12,7 +12,6 @@
struct PinMapping_t {
char name[MAPPING_NAME_STRLEN + 1];
int8_t nrf24_miso;
int8_t nrf24_mosi;
int8_t nrf24_clk;
@ -27,14 +26,6 @@ struct PinMapping_t {
int8_t cmt_gpio3;
int8_t cmt_sdio;
int8_t w5500_mosi;
int8_t w5500_miso;
int8_t w5500_sclk;
int8_t w5500_cs;
int8_t w5500_int;
int8_t w5500_rst;
#if CONFIG_ETH_USE_ESP32_EMAC
int8_t eth_phy_addr;
bool eth_enabled;
int eth_power;
@ -42,14 +33,23 @@ struct PinMapping_t {
int eth_mdio;
eth_phy_type_t eth_type;
eth_clock_mode_t eth_clk_mode;
#endif
uint8_t display_type;
uint8_t display_data;
uint8_t display_clk;
uint8_t display_cs;
uint8_t display_reset;
int8_t victron_tx;
int8_t victron_rx;
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 led[PINMAPPING_LED_COUNT];
};
@ -59,19 +59,13 @@ public:
bool init(const String& deviceMapping);
PinMapping_t& get();
bool isMappingSelected() const { return _mappingSelected; }
bool isValidNrf24Config() const;
bool isValidCmt2300Config() const;
bool isValidW5500Config() const;
#if CONFIG_ETH_USE_ESP32_EMAC
bool isValidEthConfig() const;
#endif
bool isValidHuaweiConfig() const;
private:
PinMapping_t _pinMapping;
bool _mappingSelected = false;
};
extern PinMappingClass PinMapping;

106
include/PowerLimiter.h Normal file
View File

@ -0,0 +1,106 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Configuration.h"
#include <espMqttClient.h>
#include <Arduino.h>
#include <Hoymiles.h>
#include <memory>
#include <functional>
#include <TaskSchedulerDeclarations.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
#define PL_MODE_ENABLE_NORMAL_OP 0
#define PL_MODE_FULL_DISABLE 1
#define PL_MODE_SOLAR_PT_ONLY 2
typedef enum {
EMPTY_WHEN_FULL= 0,
EMPTY_AT_NIGHT
} batDrainStrategy;
class PowerLimiterClass {
public:
enum class Status : unsigned {
Initializing,
DisabledByConfig,
DisabledByMqtt,
WaitingForValidTimestamp,
PowerMeterDisabled,
PowerMeterTimeout,
PowerMeterPending,
InverterInvalid,
InverterChanged,
InverterOffline,
InverterCommandsDisabled,
InverterLimitPending,
InverterPowerCmdPending,
InverterDevInfoPending,
InverterStatsPending,
UnconditionalSolarPassthrough,
NoVeDirect,
Settling,
Stable,
};
void init(Scheduler& scheduler);
uint8_t getPowerLimiterState();
int32_t getLastRequestedPowerLimit();
enum class Mode : unsigned {
Normal = 0,
Disabled = 1,
UnconditionalFullSolarPassthrough = 2
};
void setMode(Mode m) { _mode = m; }
Mode getMode() const { return _mode; }
void calcNextInverterRestart();
private:
void loop();
Task _loopTask;
int32_t _lastRequestedPowerLimit = 0;
uint32_t _lastPowerLimitMillis = 0;
uint32_t _shutdownTimeout = 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::shared_ptr<InverterAbstract> _inverter = nullptr;
bool _batteryDischargeEnabled = false;
uint32_t _nextInverterRestart = 0; // Values: 0->not calculated / 1->no restart configured / >1->time of next inverter restart in millis()
uint32_t _nextCalculateCheck = 5000; // time in millis for next NTP check to calulate restart
bool _fullSolarPassThroughEnabled = false;
bool _verboseLogging = true;
std::string const& getStatusText(Status status);
void announceStatus(Status status);
bool shutdown(Status status);
bool shutdown() { return shutdown(_lastStatus); }
int32_t inverterPowerDcToAc(std::shared_ptr<InverterAbstract> inverter, int32_t dcPower);
void unconditionalSolarPassthrough(std::shared_ptr<InverterAbstract> inverter);
bool canUseDirectSolarPower();
int32_t calcPowerLimit(std::shared_ptr<InverterAbstract> inverter, bool solarPowerEnabled, bool batteryDischargeEnabled);
void commitPowerLimit(std::shared_ptr<InverterAbstract> inverter, int32_t limit, bool enablePowerProduction);
bool setNewPowerLimit(std::shared_ptr<InverterAbstract> inverter, int32_t newPowerLimit);
int32_t getSolarChargePower();
float getLoadCorrectedVoltage();
bool testThreshold(float socThreshold, float voltThreshold,
std::function<bool(float, float)> compare);
bool isStartThresholdReached();
bool isStopThresholdReached();
bool isBelowStopThreshold();
bool useFullSolarPassthrough();
};
extern PowerLimiterClass PowerLimiter;

79
include/PowerMeter.h Normal file
View File

@ -0,0 +1,79 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Configuration.h"
#include <espMqttClient.h>
#include <Arduino.h>
#include <map>
#include <list>
#include "SDM.h"
#include "sml.h"
#include <TaskSchedulerDeclarations.h>
#ifndef SDM_RX_PIN
#define SDM_RX_PIN 13
#endif
#ifndef SDM_TX_PIN
#define SDM_TX_PIN 32
#endif
#ifndef SML_RX_PIN
#define SML_RX_PIN 35
#endif
typedef struct {
const unsigned char OBIS[6];
void (*Fn)(double&);
float* Arg;
} OBISHandler;
class PowerMeterClass {
public:
enum SOURCE {
SOURCE_MQTT = 0,
SOURCE_SDM1PH = 1,
SOURCE_SDM3PH = 2,
SOURCE_HTTP = 3,
SOURCE_SML = 4
};
void init(Scheduler& scheduler);
float getPowerTotal(bool forceUpdate = true);
uint32_t getLastPowerMeterUpdate();
private:
void loop();
void mqtt();
void onMqttMessage(const espMqttClientTypes::MessageProperties& properties,
const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total);
Task _loopTask;
bool _verboseLogging = true;
uint32_t _lastPowerMeterCheck;
// Used in Power limiter for safety check
uint32_t _lastPowerMeterUpdate;
float _powerMeter1Power = 0.0;
float _powerMeter2Power = 0.0;
float _powerMeter3Power = 0.0;
float _powerMeter1Voltage = 0.0;
float _powerMeter2Voltage = 0.0;
float _powerMeter3Voltage = 0.0;
float _powerMeterImport = 0.0;
float _powerMeterExport = 0.0;
std::map<String, float*> _mqttSubscriptions;
void readPowerMeter();
bool smlReadLoop();
const std::list<OBISHandler> smlHandlerList{
{{0x01, 0x00, 0x10, 0x07, 0x00, 0xff}, &smlOBISW, &_powerMeter1Power},
{{0x01, 0x00, 0x01, 0x08, 0x00, 0xff}, &smlOBISWh, &_powerMeterImport},
{{0x01, 0x00, 0x02, 0x08, 0x00, 0xff}, &smlOBISWh, &_powerMeterExport}
};
};
extern PowerMeterClass PowerMeter;

View File

@ -0,0 +1,29 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Configuration.h"
#include "Battery.h"
#include <espMqttClient.h>
#include <driver/twai.h>
#include <Arduino.h>
#include <memory>
class PylontechCanReceiver : public BatteryProvider {
public:
bool init(bool verboseLogging) final;
void deinit() final;
void loop() final;
std::shared_ptr<BatteryStats> getStats() const final { return _stats; }
private:
uint16_t readUnsignedInt16(uint8_t *data);
int16_t readSignedInt16(uint8_t *data);
float scaleValue(int16_t value, float factor);
bool getBit(uint8_t value, uint8_t bit);
void dummyData();
bool _verboseLogging = true;
std::shared_ptr<PylontechBatteryStats> _stats =
std::make_shared<PylontechBatteryStats>();
};

View File

@ -1,18 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <TaskSchedulerDeclarations.h>
class RestartHelperClass {
public:
RestartHelperClass();
void init(Scheduler& scheduler);
void triggerRestart();
private:
void loop();
Task _rebootTask;
};
extern RestartHelperClass RestartHelper;

View File

@ -1,8 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <ArduinoJson.h>
#include <LittleFS.h>
#include <cstdint>
class Utils {
@ -10,8 +8,5 @@ public:
static uint32_t getChipId();
static uint64_t generateDtuSerial();
static int getTimezoneOffset();
static bool checkJsonAlloc(const JsonDocument& doc, const char* function, const uint16_t line);
static void removeAllFiles();
static String generateMd5FromFile(String file);
static void skipBom(File& f);
static void restartDtu();
};

52
include/VictronMppt.h Normal file
View File

@ -0,0 +1,52 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <mutex>
#include <memory>
#include "VeDirectMpptController.h"
#include <TaskSchedulerDeclarations.h>
class VictronMpptClass {
public:
VictronMpptClass() = default;
~VictronMpptClass() = default;
void init(Scheduler& scheduler);
void updateSettings();
bool isDataValid() const;
// returns the data age of all controllers,
// i.e, the youngest data's age is returned.
uint32_t getDataAgeMillis() const;
VeDirectMpptController::spData_t getData(size_t idx = 0) const;
// total output of all MPPT charge controllers in Watts
int32_t getPowerOutputWatts() const;
// total panel input power of all MPPT charge controllers in Watts
int32_t getPanelPowerWatts() const;
// sum of total yield of all MPPT charge controllers in kWh
double getYieldTotal() const;
// sum of today's yield of all MPPT charge controllers in kWh
double getYieldDay() const;
private:
void loop();
VictronMpptClass(VictronMpptClass const& other) = delete;
VictronMpptClass(VictronMpptClass&& other) = delete;
VictronMpptClass& operator=(VictronMpptClass const& other) = delete;
VictronMpptClass& operator=(VictronMpptClass&& other) = delete;
Task _loopTask;
mutable std::mutex _mutex;
using controller_t = std::unique_ptr<VeDirectMpptController>;
std::vector<controller_t> _controllers;
};
extern VictronMpptClass VictronMppt;

View File

@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Battery.h"
class VictronSmartShunt : public BatteryProvider {
public:
bool init(bool verboseLogging) final;
void deinit() final { }
void loop() final;
std::shared_ptr<BatteryStats> getStats() const final { return _stats; }
private:
std::shared_ptr<VictronSmartShuntStats> _stats =
std::make_shared<VictronSmartShuntStats>();
};

View File

@ -1,29 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <Arduino.h>
#include <driver/spi_master.h>
#include <esp_eth.h> // required for esp_eth_handle_t
#include <esp_netif.h>
#include <memory>
class W5500 {
private:
explicit W5500(spi_device_handle_t spi, gpio_num_t pin_int);
public:
W5500(const W5500&) = delete;
W5500& operator=(const W5500&) = delete;
~W5500();
static std::unique_ptr<W5500> setup(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, int8_t pin_int, int8_t pin_rst);
String macAddress();
private:
static bool connection_check_spi(spi_device_handle_t spi);
static bool connection_check_interrupt(gpio_num_t pin_int);
esp_eth_handle_t eth_handle;
esp_netif_t* eth_netif;
};

View File

@ -1,15 +1,14 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "WebApi_battery.h"
#include "WebApi_config.h"
#include "WebApi_device.h"
#include "WebApi_devinfo.h"
#include "WebApi_dtu.h"
#include "WebApi_errors.h"
#include "WebApi_eventlog.h"
#include "WebApi_file.h"
#include "WebApi_firmware.h"
#include "WebApi_gridprofile.h"
#include "WebApi_i18n.h"
#include "WebApi_inverter.h"
#include "WebApi_limit.h"
#include "WebApi_maintenance.h"
@ -17,13 +16,19 @@
#include "WebApi_network.h"
#include "WebApi_ntp.h"
#include "WebApi_power.h"
#include "WebApi_powermeter.h"
#include "WebApi_powerlimiter.h"
#include "WebApi_prometheus.h"
#include "WebApi_security.h"
#include "WebApi_sysstatus.h"
#include "WebApi_webapp.h"
#include "WebApi_ws_console.h"
#include "WebApi_ws_live.h"
#include <AsyncJson.h>
#include "WebApi_ws_vedirect_live.h"
#include "WebApi_vedirect.h"
#include "WebApi_ws_Huawei.h"
#include "WebApi_Huawei.h"
#include "WebApi_ws_battery.h"
#include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
@ -31,30 +36,27 @@ class WebApiClass {
public:
WebApiClass();
void init(Scheduler& scheduler);
void reload();
static bool checkCredentials(AsyncWebServerRequest* request);
static bool checkCredentialsReadonly(AsyncWebServerRequest* request);
static void sendTooManyRequests(AsyncWebServerRequest* request);
static void writeConfig(JsonVariant& retMsg, const WebApiError code = WebApiError::GenericSuccess, const String& message = "Settings saved!");
static bool parseRequestData(AsyncWebServerRequest* request, AsyncJsonResponse* response, JsonDocument& json_document);
static uint64_t parseSerialFromRequest(AsyncWebServerRequest* request, String param_name = "inv");
static bool sendJsonResponse(AsyncWebServerRequest* request, AsyncJsonResponse* response, const char* function, const uint16_t line);
private:
void loop();
Task _loopTask;
AsyncWebServer _server;
WebApiBatteryClass _webApiBattery;
WebApiConfigClass _webApiConfig;
WebApiDeviceClass _webApiDevice;
WebApiDevInfoClass _webApiDevInfo;
WebApiDtuClass _webApiDtu;
WebApiEventlogClass _webApiEventlog;
WebApiFileClass _webApiFile;
WebApiFirmwareClass _webApiFirmware;
WebApiGridProfileClass _webApiGridprofile;
WebApiI18nClass _webApiI18n;
WebApiInverterClass _webApiInverter;
WebApiLimitClass _webApiLimit;
WebApiMaintenanceClass _webApiMaintenance;
@ -62,12 +64,19 @@ private:
WebApiNetworkClass _webApiNetwork;
WebApiNtpClass _webApiNtp;
WebApiPowerClass _webApiPower;
WebApiPowerMeterClass _webApiPowerMeter;
WebApiPowerLimiterClass _webApiPowerLimiter;
WebApiPrometheusClass _webApiPrometheus;
WebApiSecurityClass _webApiSecurity;
WebApiSysstatusClass _webApiSysstatus;
WebApiWebappClass _webApiWebapp;
WebApiWsConsoleClass _webApiWsConsole;
WebApiWsLiveClass _webApiWsLive;
WebApiWsVedirectLiveClass _webApiWsVedirectLive;
WebApiVedirectClass _webApiVedirect;
WebApiHuaweiClass _webApiHuaweiClass;
WebApiWsHuaweiLiveClass _webApiWsHuaweiLive;
WebApiWsBatteryLiveClass _webApiWsBatteryLive;
};
extern WebApiClass WebApi;

19
include/WebApi_Huawei.h Normal file
View File

@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <ESPAsyncWebServer.h>
#include <AsyncJson.h>
class WebApiHuaweiClass {
public:
void init(AsyncWebServer& server);
void loop();
void getJsonData(JsonObject& root);
private:
void onStatus(AsyncWebServerRequest* request);
void onAdminGet(AsyncWebServerRequest* request);
void onAdminPost(AsyncWebServerRequest* request);
void onPost(AsyncWebServerRequest* request);
AsyncWebServer* _server;
};

18
include/WebApi_battery.h Normal file
View File

@ -0,0 +1,18 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <ESPAsyncWebServer.h>
class WebApiBatteryClass {
public:
void init(AsyncWebServer& server);
void loop();
private:
void onStatus(AsyncWebServerRequest* request);
void onAdminGet(AsyncWebServerRequest* request);
void onAdminPost(AsyncWebServerRequest* request);
AsyncWebServer* _server;
};

19
include/WebApi_config.h Normal file
View File

@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <ESPAsyncWebServer.h>
class WebApiConfigClass {
public:
void init(AsyncWebServer& server);
void loop();
private:
void onConfigGet(AsyncWebServerRequest* request);
void onConfigDelete(AsyncWebServerRequest* request);
void onConfigListGet(AsyncWebServerRequest* request);
void onConfigUploadFinish(AsyncWebServerRequest* request);
void onConfigUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final);
AsyncWebServer* _server;
};

View File

@ -2,13 +2,15 @@
#pragma once
#include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiDeviceClass {
public:
void init(AsyncWebServer& server, Scheduler& scheduler);
void init(AsyncWebServer& server);
void loop();
private:
void onDeviceAdminGet(AsyncWebServerRequest* request);
void onDeviceAdminPost(AsyncWebServerRequest* request);
AsyncWebServer* _server;
};

View File

@ -2,12 +2,14 @@
#pragma once
#include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiDevInfoClass {
public:
void init(AsyncWebServer& server, Scheduler& scheduler);
void init(AsyncWebServer& server);
void loop();
private:
void onDevInfoStatus(AsyncWebServerRequest* request);
AsyncWebServer* _server;
};

View File

@ -2,17 +2,15 @@
#pragma once
#include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiDtuClass {
public:
WebApiDtuClass();
void init(AsyncWebServer& server, Scheduler& scheduler);
void init(AsyncWebServer& server);
void loop();
private:
void onDtuAdminGet(AsyncWebServerRequest* request);
void onDtuAdminPost(AsyncWebServerRequest* request);
Task _applyDataTask;
void applyDataTaskCb();
AsyncWebServer* _server;
};

View File

@ -5,23 +5,19 @@ enum WebApiError {
GenericBase = 1000,
GenericSuccess,
GenericNoValueFound,
GenericDataTooLarge, // not used anymore
GenericDataTooLarge,
GenericParseError,
GenericValueMissing,
GenericWriteFailed,
GenericInternalServerError,
DtuBase = 2000,
DtuSerialZero,
DtuPollZero,
DtuInvalidPowerLevel,
DtuInvalidCmtFrequency,
DtuInvalidCmtCountry,
FileBase = 3000,
FileNotDeleted,
FileSuccess,
FileDeleteSuccess,
ConfigBase = 3000,
ConfigNotDeleted,
ConfigSuccess,
InverterBase = 4000,
InverterSerialZero,
@ -33,7 +29,6 @@ enum WebApiError {
InverterChanged,
InverterDeleted,
InverterOrdered,
InverterStatsResetted,
LimitBase = 5000,
LimitSerialZero,
@ -62,7 +57,6 @@ enum WebApiError {
MqttHassTopicLength,
MqttHassTopicCharacter,
MqttLwtQos,
MqttClientIdLength,
NetworkBase = 8000,
NetworkIpInvalid,

View File

@ -2,12 +2,14 @@
#pragma once
#include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiEventlogClass {
public:
void init(AsyncWebServer& server, Scheduler& scheduler);
void init(AsyncWebServer& server);
void loop();
private:
void onEventlogStatus(AsyncWebServerRequest* request);
AsyncWebServer* _server;
};

View File

@ -1,18 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiFileClass {
public:
void init(AsyncWebServer& server, Scheduler& scheduler);
private:
void onFileGet(AsyncWebServerRequest* request);
void onFileDelete(AsyncWebServerRequest* request);
void onFileDeleteAll(AsyncWebServerRequest* request);
void onFileListGet(AsyncWebServerRequest* request);
void onFileUploadFinish(AsyncWebServerRequest* request);
void onFileUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final);
};

View File

@ -2,13 +2,15 @@
#pragma once
#include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiFirmwareClass {
public:
void init(AsyncWebServer& server, Scheduler& scheduler);
void init(AsyncWebServer& server);
void loop();
private:
void onFirmwareUpdateFinish(AsyncWebServerRequest* request);
void onFirmwareUpdateUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final);
AsyncWebServer* _server;
};

View File

@ -2,13 +2,15 @@
#pragma once
#include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiGridProfileClass {
public:
void init(AsyncWebServer& server, Scheduler& scheduler);
void init(AsyncWebServer& server);
void loop();
private:
void onGridProfileStatus(AsyncWebServerRequest* request);
void onGridProfileRawdata(AsyncWebServerRequest* request);
AsyncWebServer* _server;
};

View File

@ -1,14 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiI18nClass {
public:
void init(AsyncWebServer& server, Scheduler& scheduler);
private:
void onI18nLanguages(AsyncWebServerRequest* request);
void onI18nLanguage(AsyncWebServerRequest* request);
};

View File

@ -2,11 +2,11 @@
#pragma once
#include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiInverterClass {
public:
void init(AsyncWebServer& server, Scheduler& scheduler);
void init(AsyncWebServer& server);
void loop();
private:
void onInverterList(AsyncWebServerRequest* request);
@ -14,5 +14,6 @@ private:
void onInverterEdit(AsyncWebServerRequest* request);
void onInverterDelete(AsyncWebServerRequest* request);
void onInverterOrder(AsyncWebServerRequest* request);
void onInverterStatReset(AsyncWebServerRequest* request);
AsyncWebServer* _server;
};

View File

@ -2,13 +2,15 @@
#pragma once
#include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiLimitClass {
public:
void init(AsyncWebServer& server, Scheduler& scheduler);
void init(AsyncWebServer& server);
void loop();
private:
void onLimitStatus(AsyncWebServerRequest* request);
void onLimitPost(AsyncWebServerRequest* request);
AsyncWebServer* _server;
};

View File

@ -2,12 +2,14 @@
#pragma once
#include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiMaintenanceClass {
public:
void init(AsyncWebServer& server, Scheduler& scheduler);
void init(AsyncWebServer& server);
void loop();
private:
void onRebootPost(AsyncWebServerRequest* request);
AsyncWebServer* _server;
};

View File

@ -2,15 +2,19 @@
#pragma once
#include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
#define MQTT_JSON_DOC_SIZE 10240
class WebApiMqttClass {
public:
void init(AsyncWebServer& server, Scheduler& scheduler);
void init(AsyncWebServer& server);
void loop();
private:
void onMqttStatus(AsyncWebServerRequest* request);
void onMqttAdminGet(AsyncWebServerRequest* request);
void onMqttAdminPost(AsyncWebServerRequest* request);
String getTlsCertInfo(const char* cert);
AsyncWebServer* _server;
};

View File

@ -2,14 +2,16 @@
#pragma once
#include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiNetworkClass {
public:
void init(AsyncWebServer& server, Scheduler& scheduler);
void init(AsyncWebServer& server);
void loop();
private:
void onNetworkStatus(AsyncWebServerRequest* request);
void onNetworkAdminGet(AsyncWebServerRequest* request);
void onNetworkAdminPost(AsyncWebServerRequest* request);
AsyncWebServer* _server;
};

View File

@ -2,11 +2,11 @@
#pragma once
#include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiNtpClass {
public:
void init(AsyncWebServer& server, Scheduler& scheduler);
void init(AsyncWebServer& server);
void loop();
private:
void onNtpStatus(AsyncWebServerRequest* request);
@ -14,4 +14,6 @@ private:
void onNtpAdminPost(AsyncWebServerRequest* request);
void onNtpTimeGet(AsyncWebServerRequest* request);
void onNtpTimePost(AsyncWebServerRequest* request);
AsyncWebServer* _server;
};

View File

@ -2,13 +2,15 @@
#pragma once
#include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiPowerClass {
public:
void init(AsyncWebServer& server, Scheduler& scheduler);
void init(AsyncWebServer& server);
void loop();
private:
void onPowerStatus(AsyncWebServerRequest* request);
void onPowerPost(AsyncWebServerRequest* request);
AsyncWebServer* _server;
};

View File

@ -0,0 +1,18 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <ESPAsyncWebServer.h>
class WebApiPowerLimiterClass {
public:
void init(AsyncWebServer& server);
void loop();
private:
void onStatus(AsyncWebServerRequest* request);
void onAdminGet(AsyncWebServerRequest* request);
void onAdminPost(AsyncWebServerRequest* request);
AsyncWebServer* _server;
};

View File

@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <ESPAsyncWebServer.h>
class WebApiPowerMeterClass {
public:
void init(AsyncWebServer& server);
void loop();
private:
void onStatus(AsyncWebServerRequest* request);
void onAdminGet(AsyncWebServerRequest* request);
void onAdminPost(AsyncWebServerRequest* request);
void onTestHttpRequest(AsyncWebServerRequest* request);
AsyncWebServer* _server;
};

View File

@ -3,12 +3,12 @@
#include <ESPAsyncWebServer.h>
#include <Hoymiles.h>
#include <TaskSchedulerDeclarations.h>
#include <map>
class WebApiPrometheusClass {
public:
void init(AsyncWebServer& server, Scheduler& scheduler);
void init(AsyncWebServer& server);
void loop();
private:
void onPrometheusMetricsGet(AsyncWebServerRequest* request);
@ -17,6 +17,8 @@ private:
void addPanelInfo(AsyncResponseStream* stream, const String& serial, const uint8_t idx, std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel);
AsyncWebServer* _server;
enum MetricType_t {
NONE = 0,
GAUGE,

View File

@ -2,15 +2,17 @@
#pragma once
#include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiSecurityClass {
public:
void init(AsyncWebServer& server, Scheduler& scheduler);
void init(AsyncWebServer& server);
void loop();
private:
void onSecurityGet(AsyncWebServerRequest* request);
void onSecurityPost(AsyncWebServerRequest* request);
void onAuthenticateGet(AsyncWebServerRequest* request);
AsyncWebServer* _server;
};

View File

@ -2,12 +2,14 @@
#pragma once
#include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiSysstatusClass {
public:
void init(AsyncWebServer& server, Scheduler& scheduler);
void init(AsyncWebServer& server);
void loop();
private:
void onSystemStatus(AsyncWebServerRequest* request);
AsyncWebServer* _server;
};

18
include/WebApi_vedirect.h Normal file
View File

@ -0,0 +1,18 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <ESPAsyncWebServer.h>
class WebApiVedirectClass {
public:
void init(AsyncWebServer& server);
void loop();
private:
void onVedirectStatus(AsyncWebServerRequest* request);
void onVedirectAdminGet(AsyncWebServerRequest* request);
void onVedirectAdminPost(AsyncWebServerRequest* request);
AsyncWebServer* _server;
};

View File

@ -2,12 +2,12 @@
#pragma once
#include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiWebappClass {
public:
void init(AsyncWebServer& server, Scheduler& scheduler);
void init(AsyncWebServer& server);
void loop();
private:
void responseBinaryDataWithETagCache(AsyncWebServerRequest* request, const String &contentType, const String &contentEncoding, const uint8_t *content, size_t len);
AsyncWebServer* _server;
};

View File

@ -0,0 +1,24 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "ArduinoJson.h"
#include <ESPAsyncWebServer.h>
//#include <HuaweiFrameHandler.h>
class WebApiWsHuaweiLiveClass {
public:
WebApiWsHuaweiLiveClass();
void init(AsyncWebServer& server);
void loop();
private:
void generateJsonResponse(JsonVariant& root);
void onLivedataStatus(AsyncWebServerRequest* request);
void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len);
AsyncWebServer* _server;
AsyncWebSocket _ws;
uint32_t _lastWsCleanup = 0;
uint32_t _lastUpdateCheck = 0;
};

View File

@ -0,0 +1,24 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "ArduinoJson.h"
#include <ESPAsyncWebServer.h>
class WebApiWsBatteryLiveClass {
public:
WebApiWsBatteryLiveClass();
void init(AsyncWebServer& server);
void loop();
private:
void generateJsonResponse(JsonVariant& root);
void onLivedataStatus(AsyncWebServerRequest* request);
void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len);
AsyncWebServer* _server;
AsyncWebSocket _ws;
uint32_t _lastWsCleanup = 0;
uint32_t _lastUpdateCheck = 0;
static constexpr uint16_t _responseSize = 1024 + 512;
};

View File

@ -2,18 +2,16 @@
#pragma once
#include <ESPAsyncWebServer.h>
#include <TaskSchedulerDeclarations.h>
class WebApiWsConsoleClass {
public:
WebApiWsConsoleClass();
void init(AsyncWebServer& server, Scheduler& scheduler);
void reload();
void init(AsyncWebServer& server);
void loop();
private:
AsyncWebServer* _server;
AsyncWebSocket _ws;
AsyncAuthenticationMiddleware _simpleDigestAuth;
Task _wsCleanupTask;
void wsCleanupTaskCb();
uint32_t _lastWsCleanup = 0;
};

View File

@ -1,39 +1,30 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Configuration.h"
#include <ArduinoJson.h>
#include <ESPAsyncWebServer.h>
#include <Hoymiles.h>
#include <TaskSchedulerDeclarations.h>
class WebApiWsLiveClass {
public:
WebApiWsLiveClass();
void init(AsyncWebServer& server, Scheduler& scheduler);
void reload();
void init(AsyncWebServer& server);
void loop();
private:
static void generateInverterCommonJsonResponse(JsonObject& root, std::shared_ptr<InverterAbstract> inv);
static void generateInverterChannelJsonResponse(JsonObject& root, std::shared_ptr<InverterAbstract> inv);
static void generateCommonJsonResponse(JsonVariant& root);
static void addField(JsonObject& root, std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId, String topic = "");
static void addTotalField(JsonObject& root, const String& name, const float value, const String& unit, const uint8_t digits);
void generateJsonResponse(JsonVariant& root);
void addField(JsonObject& root, uint8_t idx, std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId, String topic = "");
void addTotalField(JsonObject& root, const String& name, const float value, const String& unit, const uint8_t digits);
void onLivedataStatus(AsyncWebServerRequest* request);
void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len);
AsyncWebServer* _server;
AsyncWebSocket _ws;
AsyncAuthenticationMiddleware _simpleDigestAuth;
uint32_t _lastPublishStats[INV_MAX_COUNT] = { 0 };
uint32_t _lastWsPublish = 0;
uint32_t _lastInvUpdateCheck = 0;
uint32_t _lastWsCleanup = 0;
uint32_t _newestInverterTimestamp = 0;
std::mutex _mutex;
Task _wsCleanupTask;
void wsCleanupTaskCb();
Task _sendDataTask;
void sendDataTaskCb();
};

View File

@ -0,0 +1,26 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "ArduinoJson.h"
#include <ESPAsyncWebServer.h>
#include <VeDirectMpptController.h>
class WebApiWsVedirectLiveClass {
public:
WebApiWsVedirectLiveClass();
void init(AsyncWebServer& server);
void loop();
private:
void generateJsonResponse(JsonVariant& root);
void onLivedataStatus(AsyncWebServerRequest* request);
void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len);
AsyncWebServer* _server;
AsyncWebSocket _ws;
uint32_t _lastWsPublish = 0;
uint32_t _lastWsCleanup = 0;
uint32_t _dataAgeMillis = 0;
static constexpr uint16_t _responseSize = 1024 + 128;
};

View File

@ -1,9 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
// The referenced values are generated by pio-scripts/auto_firmware_version.py
extern const char *__COMPILED_GIT_HASH__;
extern const char *__COMPILED_GIT_BRANCH__;
// extern const char *__COMPILED_DATE_TIME_UTC_STR__;

View File

@ -9,11 +9,11 @@
#define ACCESS_POINT_NAME "OpenDTU-"
#define ACCESS_POINT_PASSWORD "openDTU42"
#define ACCESS_POINT_TIMEOUT 3
#define ACCESS_POINT_TIMEOUT 3;
#define AUTH_USERNAME "admin"
#define SECURITY_ALLOW_READONLY true
#define WIFI_RECONNECT_TIMEOUT 30
#define WIFI_RECONNECT_TIMEOUT 15
#define WIFI_RECONNECT_REDO_TIMEOUT 600
#define WIFI_SSID ""
@ -22,8 +22,7 @@
#define MDNS_ENABLED false
#define NTP_SERVER_OLD "pool.ntp.org"
#define NTP_SERVER "opendtu.pool.ntp.org"
#define NTP_SERVER "pool.ntp.org"
#define NTP_TIMEZONE "CET-1CEST,M3.5.0,M10.5.0/3"
#define NTP_TIMEZONEDESCR "Europe/Berlin"
#define NTP_LONGITUDE 10.4515f
@ -84,8 +83,7 @@
#define DTU_POLL_INTERVAL 5U
#define DTU_NRF_PA_LEVEL 0U
#define DTU_CMT_PA_LEVEL 0
#define DTU_CMT_FREQUENCY 865000000U
#define DTU_CMT_COUNTRY_MODE 0U
#define DTU_CMT_FREQUENCY 865000U
#define MQTT_HASS_ENABLED false
#define MQTT_HASS_EXPIRE true
@ -99,14 +97,61 @@
#define DISPLAY_SCREENSAVER true
#define DISPLAY_ROTATION 2U
#define DISPLAY_CONTRAST 60U
#define DISPLAY_LOCALE "en"
#define DISPLAY_LANGUAGE 0U
#define DISPLAY_DIAGRAM_DURATION (10UL * 60UL * 60UL)
#define DISPLAY_DIAGRAM_MODE 1U
#define REACHABLE_THRESHOLD 2U
#define LED_BRIGHTNESS 100U
#define MAX_INVERTER_LIMIT 2250
#define VEDIRECT_ENABLED false
#define VEDIRECT_VERBOSE_LOGGING false
#define VEDIRECT_UPDATESONLY true
#define LANG_PACK_SUFFIX ".lang.json"
#define POWERMETER_ENABLED false
#define POWERMETER_INTERVAL 10
#define POWERMETER_SOURCE 2
#define POWERMETER_SDMBAUDRATE 9600
#define POWERMETER_SDMADDRESS 1
#define POWERLIMITER_ENABLED false
#define POWERLIMITER_SOLAR_PASSTHROUGH_ENABLED true
#define POWERLIMITER_SOLAR_PASSTHROUGH_LOSSES 3
#define POWERLIMITER_BATTERY_DRAIN_STRATEGY 0
#define POWERLIMITER_INTERVAL 10
#define POWERLIMITER_IS_INVERTER_BEHIND_POWER_METER true
#define POWERLIMITER_INVERTER_ID 0
#define POWERLIMITER_INVERTER_CHANNEL_ID 0
#define POWERLIMITER_TARGET_POWER_CONSUMPTION 0
#define POWERLIMITER_TARGET_POWER_CONSUMPTION_HYSTERESIS 0
#define POWERLIMITER_LOWER_POWER_LIMIT 10
#define POWERLIMITER_UPPER_POWER_LIMIT 800
#define POWERLIMITER_BATTERY_SOC_START_THRESHOLD 80
#define POWERLIMITER_BATTERY_SOC_STOP_THRESHOLD 20
#define POWERLIMITER_VOLTAGE_START_THRESHOLD 50.0
#define POWERLIMITER_VOLTAGE_STOP_THRESHOLD 49.0
#define POWERLIMITER_VOLTAGE_LOAD_CORRECTION_FACTOR 0.001
#define POWERLIMITER_RESTART_HOUR -1
#define POWERLIMITER_FULL_SOLAR_PASSTHROUGH_SOC 100
#define POWERLIMITER_FULL_SOLAR_PASSTHROUGH_START_VOLTAGE 100.0
#define POWERLIMITER_FULL_SOLAR_PASSTHROUGH_STOP_VOLTAGE 100.0
#define BATTERY_ENABLED false
#define BATTERY_PROVIDER 0 // Pylontech CAN receiver
#define BATTERY_JKBMS_INTERFACE 0
#define BATTERY_JKBMS_POLLING_INTERVAL 5
#define HUAWEI_ENABLED false
#define HUAWEI_CAN_CONTROLLER_FREQUENCY 8000000UL
#define HUAWEI_AUTO_POWER_VOLTAGE_LIMIT 42.0
#define HUAWEI_AUTO_POWER_ENABLE_VOLTAGE_LIMIT 42.0
#define HUAWEI_AUTO_POWER_LOWER_POWER_LIMIT 150
#define HUAWEI_AUTO_POWER_UPPER_POWER_LIMIT 2000
#define VERBOSE_LOGGING true
#define LED_BRIGHTNESS 100U
#define MAX_INVERTER_LIMIT 2250

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