Prepare Release 2024.06.03 (#1024)
This commit is contained in:
commit
5ab6fe913b
24
README.md
24
README.md
@ -1,23 +1,23 @@
|
||||
- [OpenDTU-onBattery](#opendtu-onbattery)
|
||||
- [What is OpenDTU-onBattery](#what-is-opendtu-onbattery)
|
||||
- [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)
|
||||
- [Highlights of OpenDTU-OnBattery](#highlights-of-opendtu-onbattery)
|
||||
- [Documentation](#documentation)
|
||||
- [Acknowledgment](#acknowledgment)
|
||||
|
||||
# OpenDTU-onBattery
|
||||
# OpenDTU-OnBattery
|
||||
|
||||
This is a fork from the Hoymiles project [OpenDTU](https://github.com/tbnobody/OpenDTU).
|
||||
|
||||

|
||||
|
||||
[](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/build.yml)
|
||||
[](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/build.yml)
|
||||
[](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/cpplint.yml)
|
||||
[](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/yarnlint.yml)
|
||||
|
||||
## What is OpenDTU-onBattery
|
||||
## 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.
|
||||
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
|
||||
|
||||
@ -25,7 +25,7 @@ The original OpenDTU project was started from [this](https://www.mikrocontroller
|
||||
|
||||
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
|
||||
## Highlights of OpenDTU-OnBattery
|
||||
|
||||
This project is still under development and adds following features:
|
||||
|
||||
@ -40,11 +40,11 @@ This project is still under development and adds following features:
|
||||
|
||||
## Documentation
|
||||
|
||||
[Full documentation of OpenDTU-onBattery extensions can be found at the project's wiki](https://github.com/helgeerbe/OpenDTU-OnBattery/wiki).
|
||||
Documentation of OpenDTU-OnBattery extensions can be found in [the project's wiki](https://github.com/helgeerbe/OpenDTU-OnBattery/wiki).
|
||||
|
||||
For documentation of openDTU core functionality I refer to the original [repo](https://github.com/tbnobody/OpenDTU) and its [documentation](https://tbnobody.github.io/OpenDTU-docs/).
|
||||
For documentation of OpenDTU core functionality refer to the original [repo](https://github.com/tbnobody/OpenDTU) and its [documentation](https://opendtu.solar).
|
||||
|
||||
Please note that openDTU-onBattery may change significantly during its development.
|
||||
Please note that OpenDTU-OnBattery may change significantly during its development.
|
||||
Bug reports, comments, feature requests and fixes are most welcome!
|
||||
|
||||
To find out what's new or improved have a look at the [changelog](https://github.com/helgeerbe/OpenDTU-OnBattery/releases).
|
||||
@ -53,4 +53,4 @@ To find out what's new or improved have a look at the [changelog](https://github
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
@ -6,7 +6,7 @@ This is a fork from the Hoymiles project [OpenDTU](https://github.com/tbnobody/O
|
||||
|
||||
> **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.
|
||||
> 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
|
||||
|
||||
@ -106,7 +106,7 @@ Some points for consideration are:
|
||||
|
||||
#### Operation modes
|
||||
|
||||
openDTU-onBattery supports three operation modes for the Huawei PSU:
|
||||
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)
|
||||
@ -135,7 +135,7 @@ It was the goal to replace the original Hoymiles DTU (Telemetry Gateway) with th
|
||||
|
||||
### Status
|
||||
|
||||
[](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/build.yml)
|
||||
[](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/build.yml)
|
||||
[](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/cpplint.yml)
|
||||
[](https://github.com/helgeerbe/OpenDTU-OnBattery/actions/workflows/yarnlint.yml)
|
||||
|
||||
|
||||
@ -1,91 +1,3 @@
|
||||
# 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
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
This documentation has been [moved to the wiki](https://github.com/helgeerbe/OpenDTU-OnBattery/wiki/Device-Profiles).
|
||||
@ -2,15 +2,28 @@
|
||||
|
||||
## Hardware you need
|
||||
|
||||
For a highly integrated board with most peripherals already included, have a
|
||||
look at the [OpenDTU Fusion](https://shop.allianceapps.io/products/opendtu-fusion-community-edition)
|
||||
board. There are also [OpenDTU-OnBattery-specific addons](https://github.com/markusdd/OpenDTUFusionDocs/blob/main/CANIso.md).
|
||||
|
||||
Otherwise, read on to assemble your own components.
|
||||
|
||||
### 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.
|
||||
For ease of use, buy a "ESP32 DEVKIT DOIT" or "ESP32 NodeMCU Development Board"
|
||||
with an ESP32-S3 chipset on it, **with at least 8 MB of flash memory**.
|
||||
Preferrably, the ESP32 should also have some embedded PSRAM. Look out for
|
||||
labels like "N8R2", "N16R8", where the number after the N is the amount of
|
||||
flash in Megabytes, and the number after the R is the amount of PSRAM in
|
||||
Megabytes.
|
||||
|
||||
Sample Picture:
|
||||
|
||||

|
||||
|
||||
Also supported: Board with Ethernet-Connector and Power-over-Ethernet [Olimex ESP32-POE](https://www.olimex.com/Products/IoT/ESP32/ESP32-POE/open-source-hardware)
|
||||
Also supported: Board with Ethernet-Connector and Power-over-Ethernet (PoE)
|
||||
(select version with 8 MB of flash memory or more)
|
||||
[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)
|
||||
|
||||
@ -41,7 +54,10 @@ The CMT2300A uses 3-Wire half duplex SPI communication. Due to this fact it curr
|
||||
|
||||
### 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.
|
||||
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. A commonly used digital isolator
|
||||
is the ADUM1201.
|
||||
|
||||
### SN65HVD230 CAN bus transceiver
|
||||
|
||||
@ -49,7 +65,7 @@ The SN65HVD230 CAN bus transceiver is used to interface with the Pylontech batte
|
||||
|
||||
### MCP2515 CAN bus module
|
||||
|
||||
See [Wiki](https://github.com/helgeerbe/OpenDTU-OnBattery/wiki/Huawei-AC-PSU) for details.
|
||||
Used to connect to the Huawei PSU (AC charger). See [the Wiki](https://github.com/helgeerbe/OpenDTU-OnBattery/wiki/Huawei-AC-PSU) for details.
|
||||
|
||||
### Relay module
|
||||
|
||||
@ -71,30 +87,8 @@ Use a power supply with 5 V and 1 A. The USB cable connected to your PC/Notebook
|
||||
|
||||
### 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.
|
||||
It is possible to change all the pins of the NRF24L01+ module, the Display, the LED etc.
|
||||
The way to change the pin assignment is by creating a custom [device profile](https://github.com/helgeerbe/OpenDTU-OnBattery/wiki/Device-Profiles).
|
||||
|
||||
## Flashing and starting up
|
||||
|
||||
|
||||
@ -14,7 +14,6 @@ public:
|
||||
virtual void deinit() = 0;
|
||||
virtual void loop() = 0;
|
||||
virtual std::shared_ptr<BatteryStats> getStats() const = 0;
|
||||
virtual bool usesHwPort2() const { return false; }
|
||||
};
|
||||
|
||||
class BatteryClass {
|
||||
|
||||
@ -58,6 +58,8 @@ class BatteryStats {
|
||||
}
|
||||
|
||||
String _manufacturer = "unknown";
|
||||
String _hwversion = "";
|
||||
String _fwversion = "";
|
||||
uint32_t _lastUpdate = 0;
|
||||
|
||||
private:
|
||||
@ -157,8 +159,9 @@ class VictronSmartShuntStats : public BatteryStats {
|
||||
uint32_t _timeToGo;
|
||||
float _chargedEnergy;
|
||||
float _dischargedEnergy;
|
||||
String _modelName;
|
||||
int32_t _instantaneousPower;
|
||||
float _midpointVoltage;
|
||||
float _midpointDeviation;
|
||||
float _consumedAmpHours;
|
||||
int32_t _lastFullCharge;
|
||||
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
#include "Battery.h"
|
||||
#include "JkBmsSerialMessage.h"
|
||||
|
||||
//#define JKBMS_DUMMY_SERIAL
|
||||
|
||||
class DataPointContainer;
|
||||
|
||||
namespace JkBms {
|
||||
@ -19,11 +21,16 @@ class Controller : public BatteryProvider {
|
||||
void deinit() final;
|
||||
void loop() final;
|
||||
std::shared_ptr<BatteryStats> getStats() const final { return _stats; }
|
||||
bool usesHwPort2() const final {
|
||||
return ARDUINO_USB_CDC_ON_BOOT != 1;
|
||||
}
|
||||
|
||||
private:
|
||||
static char constexpr _serialPortOwner[] = "JK BMS";
|
||||
|
||||
#ifdef JKBMS_DUMMY_SERIAL
|
||||
std::unique_ptr<DummySerial> _upSerial;
|
||||
#else
|
||||
std::unique_ptr<HardwareSerial> _upSerial;
|
||||
#endif
|
||||
|
||||
enum class Status : unsigned {
|
||||
Initializing,
|
||||
Timeout,
|
||||
|
||||
@ -45,6 +45,8 @@ struct PinMapping_t {
|
||||
int8_t victron_rx;
|
||||
int8_t victron_tx2;
|
||||
int8_t victron_rx2;
|
||||
int8_t victron_tx3;
|
||||
int8_t victron_rx3;
|
||||
int8_t battery_rx;
|
||||
int8_t battery_rxen;
|
||||
int8_t battery_tx;
|
||||
|
||||
@ -60,6 +60,8 @@ private:
|
||||
|
||||
mutable std::mutex _mutex;
|
||||
|
||||
static char constexpr _sdmSerialPortOwner[] = "SDM power meter";
|
||||
std::unique_ptr<HardwareSerial> _upSdmSerial = nullptr;
|
||||
std::unique_ptr<SDM> _upSdm = nullptr;
|
||||
std::unique_ptr<SoftwareSerial> _upSmlSerial = nullptr;
|
||||
|
||||
|
||||
@ -1,27 +1,21 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
class SerialPortManagerClass {
|
||||
public:
|
||||
bool allocateMpptPort(int port);
|
||||
bool allocateBatteryPort(int port);
|
||||
void invalidateBatteryPort();
|
||||
void invalidateMpptPorts();
|
||||
void init();
|
||||
|
||||
std::optional<uint8_t> allocatePort(std::string const& owner);
|
||||
void freePort(std::string const& owner);
|
||||
|
||||
private:
|
||||
enum Owner {
|
||||
BATTERY,
|
||||
MPPT
|
||||
};
|
||||
|
||||
std::map<uint8_t, Owner> allocatedPorts;
|
||||
|
||||
bool allocatePort(uint8_t port, Owner owner);
|
||||
void invalidate(Owner owner);
|
||||
|
||||
static const char* print(Owner owner);
|
||||
// the amount of hardare UARTs available on supported ESP32 chips
|
||||
static size_t constexpr _num_controllers = 3;
|
||||
std::array<std::string, _num_controllers> _ports = { "" };
|
||||
};
|
||||
|
||||
extern SerialPortManagerClass SerialPortManager;
|
||||
|
||||
@ -55,7 +55,9 @@ private:
|
||||
using controller_t = std::unique_ptr<VeDirectMpptController>;
|
||||
std::vector<controller_t> _controllers;
|
||||
|
||||
bool initController(int8_t rx, int8_t tx, bool logging, int hwSerialPort);
|
||||
std::vector<String> _serialPortOwners;
|
||||
bool initController(int8_t rx, int8_t tx, bool logging,
|
||||
uint8_t instance);
|
||||
};
|
||||
|
||||
extern VictronMpptClass VictronMppt;
|
||||
|
||||
@ -6,14 +6,13 @@
|
||||
class VictronSmartShunt : public BatteryProvider {
|
||||
public:
|
||||
bool init(bool verboseLogging) final;
|
||||
void deinit() final { }
|
||||
void deinit() final;
|
||||
void loop() final;
|
||||
std::shared_ptr<BatteryStats> getStats() const final { return _stats; }
|
||||
bool usesHwPort2() const final {
|
||||
return ARDUINO_USB_CDC_ON_BOOT != 1;
|
||||
}
|
||||
|
||||
private:
|
||||
static char constexpr _serialPortOwner[] = "SmartShunt";
|
||||
|
||||
uint32_t _lastUpdate = 0;
|
||||
std::shared_ptr<VictronSmartShuntStats> _stats =
|
||||
std::make_shared<VictronSmartShuntStats>();
|
||||
|
||||
@ -137,6 +137,51 @@ frozen::string const& veStruct::getPidAsString() const
|
||||
return getAsString(values, productID_PID);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function returns the firmware version as an integer, disregarding
|
||||
* release candidate marks.
|
||||
*/
|
||||
uint32_t veStruct::getFwVersionAsInteger() const
|
||||
{
|
||||
char const* strVersion = firmwareVer_FW;
|
||||
|
||||
// VE.Direct protocol manual states that the first char can be a non-digit,
|
||||
// in which case that char represents a release candidate version
|
||||
if (strVersion[0] < '0' || strVersion[0] > '9') { ++strVersion; }
|
||||
|
||||
return static_cast<uint32_t>(strtoul(strVersion, nullptr, 10));
|
||||
}
|
||||
|
||||
/*
|
||||
* This function returns the firmware version as readable text.
|
||||
*/
|
||||
String veStruct::getFwVersionFormatted() const
|
||||
{
|
||||
char const* strVersion = firmwareVer_FW;
|
||||
char rc = 0;
|
||||
|
||||
// VE.Direct protocol manual states that the first char can be a non-digit,
|
||||
// in which case that char represents a release candidate version
|
||||
if (strVersion[0] < '0' || strVersion[0] > '9') {
|
||||
rc = strVersion[0];
|
||||
++strVersion;
|
||||
}
|
||||
|
||||
// SmartShunt firmware version is transmitted with leading zero(es)
|
||||
while (strVersion[0] == '0') { ++strVersion; }
|
||||
|
||||
String res(strVersion[0]);
|
||||
res += ".";
|
||||
res += strVersion + 1;
|
||||
|
||||
if (rc != 0) {
|
||||
res += "-rc-";
|
||||
res += rc;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function returns the state of operations (CS) as readable text.
|
||||
*/
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include <frozen/string.h>
|
||||
#include <frozen/map.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
#define VE_MAX_VALUE_LEN 33 // VE.Direct Protocol: max value size is 33 including /0
|
||||
#define VE_MAX_HEX_LEN 100 // Maximum size of hex frame - max payload 34 byte (=68 char) + safe buffer
|
||||
@ -9,12 +10,14 @@
|
||||
typedef struct {
|
||||
uint16_t productID_PID = 0; // product id
|
||||
char serialNr_SER[VE_MAX_VALUE_LEN]; // serial number
|
||||
char firmwareNr_FW[VE_MAX_VALUE_LEN]; // firmware release number
|
||||
char firmwareVer_FW[VE_MAX_VALUE_LEN]; // firmware release number
|
||||
uint32_t batteryVoltage_V_mV = 0; // battery voltage in mV
|
||||
int32_t batteryCurrent_I_mA = 0; // battery current in mA (can be negative)
|
||||
float mpptEfficiency_Percent = 0; // efficiency in percent (calculated, moving average)
|
||||
|
||||
frozen::string const& getPidAsString() const; // product ID as string
|
||||
uint32_t getFwVersionAsInteger() const;
|
||||
String getFwVersionFormatted() const;
|
||||
} veStruct;
|
||||
|
||||
struct veMpptStruct : veStruct {
|
||||
@ -79,6 +82,8 @@ struct veShuntStruct : veStruct {
|
||||
int32_t H16; // Maximum auxiliary (battery) voltage
|
||||
int32_t H17; // Amount of discharged energy
|
||||
int32_t H18; // Amount of charged energy
|
||||
int32_t VM; // Mid-point voltage of the battery bank
|
||||
int32_t DM; // Mid-point deviation of the battery bank
|
||||
int8_t dcMonitorMode_MON; // DC monitor mode
|
||||
};
|
||||
|
||||
|
||||
@ -62,7 +62,8 @@ VeDirectFrameHandler<T>::VeDirectFrameHandler() :
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void VeDirectFrameHandler<T>::init(char const* who, int8_t rx, int8_t tx, Print* msgOut, bool verboseLogging, uint16_t hwSerialPort)
|
||||
void VeDirectFrameHandler<T>::init(char const* who, int8_t rx, int8_t tx,
|
||||
Print* msgOut, bool verboseLogging, uint8_t hwSerialPort)
|
||||
{
|
||||
_vedirectSerial = std::make_unique<HardwareSerial>(hwSerialPort);
|
||||
_vedirectSerial->end(); // make sure the UART will be re-initialized
|
||||
@ -242,12 +243,12 @@ void VeDirectFrameHandler<T>::processTextData(std::string const& name, std::stri
|
||||
}
|
||||
|
||||
if (name == "SER") {
|
||||
strcpy(_tmpFrame.serialNr_SER, value.c_str());
|
||||
strncpy(_tmpFrame.serialNr_SER, value.c_str(), sizeof(_tmpFrame.serialNr_SER));
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "FW") {
|
||||
strcpy(_tmpFrame.firmwareNr_FW, value.c_str());
|
||||
strncpy(_tmpFrame.firmwareVer_FW, value.c_str(), sizeof(_tmpFrame.firmwareVer_FW));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -30,7 +30,8 @@ public:
|
||||
|
||||
protected:
|
||||
VeDirectFrameHandler();
|
||||
void init(char const* who, int8_t rx, int8_t tx, Print* msgOut, bool verboseLogging, uint16_t hwSerialPort);
|
||||
void init(char const* who, int8_t rx, int8_t tx, Print* msgOut,
|
||||
bool verboseLogging, uint8_t hwSerialPort);
|
||||
virtual bool hexDataHandler(VeDirectHexData const &data) { return false; } // handles the disassembeled hex response
|
||||
|
||||
bool _verboseLogging;
|
||||
|
||||
@ -12,9 +12,11 @@
|
||||
|
||||
//#define PROCESS_NETWORK_STATE
|
||||
|
||||
void VeDirectMpptController::init(int8_t rx, int8_t tx, Print* msgOut, bool verboseLogging, uint16_t hwSerialPort)
|
||||
void VeDirectMpptController::init(int8_t rx, int8_t tx, Print* msgOut,
|
||||
bool verboseLogging, uint8_t hwSerialPort)
|
||||
{
|
||||
VeDirectFrameHandler::init("MPPT", rx, tx, msgOut, verboseLogging, hwSerialPort);
|
||||
VeDirectFrameHandler::init("MPPT", rx, tx, msgOut,
|
||||
verboseLogging, hwSerialPort);
|
||||
}
|
||||
|
||||
bool VeDirectMpptController::processTextDataDerived(std::string const& name, std::string const& value)
|
||||
@ -110,7 +112,7 @@ void VeDirectMpptController::frameValidEvent() {
|
||||
// charger periodically sends human readable (TEXT) data to the serial port. For firmware
|
||||
// versions v1.53 and above, the charger always periodically sends TEXT data to the serial port.
|
||||
// --> We just use hex commandes for firmware >= 1.53 to keep text messages alive
|
||||
if (atoi(_tmpFrame.firmwareNr_FW) < 153) { return; }
|
||||
if (_tmpFrame.getFwVersionAsInteger() < 153) { return; }
|
||||
|
||||
using Command = VeDirectHexCommand;
|
||||
using Register = VeDirectHexRegister;
|
||||
|
||||
@ -40,7 +40,8 @@ class VeDirectMpptController : public VeDirectFrameHandler<veMpptStruct> {
|
||||
public:
|
||||
VeDirectMpptController() = default;
|
||||
|
||||
void init(int8_t rx, int8_t tx, Print* msgOut, bool verboseLogging, uint16_t hwSerialPort);
|
||||
void init(int8_t rx, int8_t tx, Print* msgOut,
|
||||
bool verboseLogging, uint8_t hwSerialPort);
|
||||
|
||||
using data_t = veMpptStruct;
|
||||
|
||||
|
||||
@ -3,10 +3,11 @@
|
||||
|
||||
VeDirectShuntController VeDirectShunt;
|
||||
|
||||
void VeDirectShuntController::init(int8_t rx, int8_t tx, Print* msgOut, bool verboseLogging)
|
||||
void VeDirectShuntController::init(int8_t rx, int8_t tx, Print* msgOut,
|
||||
bool verboseLogging, uint8_t hwSerialPort)
|
||||
{
|
||||
VeDirectFrameHandler::init("SmartShunt", rx, tx, msgOut, verboseLogging,
|
||||
((ARDUINO_USB_CDC_ON_BOOT != 1)?2:0));
|
||||
VeDirectFrameHandler::init("SmartShunt", rx, tx, msgOut,
|
||||
verboseLogging, hwSerialPort);
|
||||
}
|
||||
|
||||
bool VeDirectShuntController::processTextDataDerived(std::string const& name, std::string const& value)
|
||||
@ -108,6 +109,14 @@ bool VeDirectShuntController::processTextDataDerived(std::string const& name, st
|
||||
_tmpFrame.H17 = atoi(value.c_str());
|
||||
return true;
|
||||
}
|
||||
if (name == "VM") {
|
||||
_tmpFrame.VM = atoi(value.c_str());
|
||||
return true;
|
||||
}
|
||||
if (name == "DM") {
|
||||
_tmpFrame.DM = atoi(value.c_str());
|
||||
return true;
|
||||
}
|
||||
if (name == "H18") {
|
||||
_tmpFrame.H18 = atoi(value.c_str());
|
||||
return true;
|
||||
|
||||
@ -8,7 +8,8 @@ class VeDirectShuntController : public VeDirectFrameHandler<veShuntStruct> {
|
||||
public:
|
||||
VeDirectShuntController() = default;
|
||||
|
||||
void init(int8_t rx, int8_t tx, Print* msgOut, bool verboseLogging);
|
||||
void init(int8_t rx, int8_t tx, Print* msgOut,
|
||||
bool verboseLogging, uint8_t hwSerialPort);
|
||||
|
||||
using data_t = veShuntStruct;
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ Import("env")
|
||||
platform = env.PioPlatform()
|
||||
|
||||
import sys
|
||||
from os.path import join
|
||||
from os.path import join, getsize
|
||||
|
||||
sys.path.append(join(platform.get_package_dir("tool-esptoolpy")))
|
||||
import esptool
|
||||
@ -60,6 +60,14 @@ def esp32_create_combined_bin(source, target, env):
|
||||
flash_size,
|
||||
]
|
||||
|
||||
# platformio estimates the amount of flash used to store the firmware. this
|
||||
# estimate is not accurate. we perform a final check on the firmware bin
|
||||
# size by comparing it against the respective partition size.
|
||||
max_size = env.BoardConfig().get("upload.maximum_size", 1)
|
||||
fw_size = getsize(firmware_name)
|
||||
if (fw_size > max_size):
|
||||
raise Exception("firmware binary too large: %d > %d" % (fw_size, max_size))
|
||||
|
||||
print(" Offset | File")
|
||||
for section in sections:
|
||||
sect_adr, sect_file = section.split(" ", 1)
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
#include "JkBmsController.h"
|
||||
#include "VictronSmartShunt.h"
|
||||
#include "MqttBattery.h"
|
||||
#include "SerialPortManager.h"
|
||||
|
||||
BatteryClass Battery;
|
||||
|
||||
@ -39,7 +38,6 @@ void BatteryClass::updateSettings()
|
||||
_upProvider->deinit();
|
||||
_upProvider = nullptr;
|
||||
}
|
||||
SerialPortManager.invalidateBatteryPort();
|
||||
|
||||
CONFIG_T& config = Configuration.get();
|
||||
if (!config.Battery.Enabled) { return; }
|
||||
@ -60,22 +58,11 @@ void BatteryClass::updateSettings()
|
||||
_upProvider = std::make_unique<VictronSmartShunt>();
|
||||
break;
|
||||
default:
|
||||
MessageOutput.printf("Unknown battery provider: %d\r\n", config.Battery.Provider);
|
||||
MessageOutput.printf("[Battery] Unknown provider: %d\r\n", config.Battery.Provider);
|
||||
return;
|
||||
}
|
||||
|
||||
if(_upProvider->usesHwPort2()) {
|
||||
if (!SerialPortManager.allocateBatteryPort(2)) {
|
||||
MessageOutput.printf("[Battery] Serial port %d already in use. Initialization aborted!\r\n", 2);
|
||||
_upProvider = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_upProvider->init(verboseLogging)) {
|
||||
SerialPortManager.invalidateBatteryPort();
|
||||
_upProvider = nullptr;
|
||||
}
|
||||
if (!_upProvider->init(verboseLogging)) { _upProvider = nullptr; }
|
||||
}
|
||||
|
||||
void BatteryClass::loop()
|
||||
|
||||
@ -62,6 +62,12 @@ bool BatteryStats::updateAvailable(uint32_t since) const
|
||||
void BatteryStats::getLiveViewData(JsonVariant& root) const
|
||||
{
|
||||
root["manufacturer"] = _manufacturer;
|
||||
if (!_fwversion.isEmpty()) {
|
||||
root["fwversion"] = _fwversion;
|
||||
}
|
||||
if (!_hwversion.isEmpty()) {
|
||||
root["hwversion"] = _hwversion;
|
||||
}
|
||||
root["data_age"] = getAgeSeconds();
|
||||
|
||||
addLiveViewValue(root, "SoC", _soc, "%", _socPrecision);
|
||||
@ -375,22 +381,42 @@ void JkBmsBatteryStats::updateFrom(JkBms::DataPointContainer const& dp)
|
||||
_cellVoltageTimestamp = millis();
|
||||
}
|
||||
|
||||
auto oVersion = _dataPoints.get<Label::BmsSoftwareVersion>();
|
||||
if (oVersion.has_value()) {
|
||||
// raw: "11.XW_S11.262H_"
|
||||
// => Hardware "V11.XW" (displayed in Android app)
|
||||
// => Software "V11.262H" (displayed in Android app)
|
||||
auto first = oVersion->find('_');
|
||||
if (first != std::string::npos) {
|
||||
_hwversion = oVersion->substr(0, first).c_str();
|
||||
|
||||
auto second = oVersion->find('_', first + 1);
|
||||
|
||||
// the 'S' seems to be merely an indicator for "software"?
|
||||
if (oVersion->at(first + 1) == 'S') { first++; }
|
||||
|
||||
_fwversion = oVersion->substr(first + 1, second - first - 1).c_str();
|
||||
}
|
||||
}
|
||||
|
||||
_lastUpdate = millis();
|
||||
}
|
||||
|
||||
void VictronSmartShuntStats::updateFrom(VeDirectShuntController::data_t const& shuntData) {
|
||||
BatteryStats::setVoltage(shuntData.batteryVoltage_V_mV / 1000.0, millis());
|
||||
BatteryStats::setSoC(static_cast<float>(shuntData.SOC) / 10, 1/*precision*/, millis());
|
||||
_fwversion = shuntData.getFwVersionFormatted();
|
||||
|
||||
_current = static_cast<float>(shuntData.batteryCurrent_I_mA) / 1000;
|
||||
_modelName = shuntData.getPidAsString().data();
|
||||
_chargeCycles = shuntData.H4;
|
||||
_timeToGo = shuntData.TTG / 60;
|
||||
_chargedEnergy = static_cast<float>(shuntData.H18) / 100;
|
||||
_dischargedEnergy = static_cast<float>(shuntData.H17) / 100;
|
||||
_manufacturer = "Victron " + _modelName;
|
||||
_manufacturer = String("Victron ") + shuntData.getPidAsString().data();
|
||||
_temperature = shuntData.T;
|
||||
_tempPresent = shuntData.tempPresent;
|
||||
_midpointVoltage = static_cast<float>(shuntData.VM) / 1000;
|
||||
_midpointDeviation = static_cast<float>(shuntData.DM) / 10;
|
||||
_instantaneousPower = shuntData.P;
|
||||
_consumedAmpHours = static_cast<float>(shuntData.CE) / 1000;
|
||||
_lastFullCharge = shuntData.H9 / 60;
|
||||
@ -414,6 +440,8 @@ void VictronSmartShuntStats::getLiveViewData(JsonVariant& root) const {
|
||||
addLiveViewValue(root, "dischargedEnergy", _dischargedEnergy, "kWh", 2);
|
||||
addLiveViewValue(root, "instantaneousPower", _instantaneousPower, "W", 0);
|
||||
addLiveViewValue(root, "consumedAmpHours", _consumedAmpHours, "Ah", 3);
|
||||
addLiveViewValue(root, "midpointVoltage", _midpointVoltage, "V", 2);
|
||||
addLiveViewValue(root, "midpointDeviation", _midpointDeviation, "%", 1);
|
||||
addLiveViewValue(root, "lastFullCharge", _lastFullCharge, "min", 0);
|
||||
if (_tempPresent) {
|
||||
addLiveViewValue(root, "temperature", _temperature, "°C", 0);
|
||||
@ -436,4 +464,6 @@ void VictronSmartShuntStats::mqttPublish() const {
|
||||
MqttSettings.publish("battery/instantaneousPower", String(_instantaneousPower));
|
||||
MqttSettings.publish("battery/consumedAmpHours", String(_consumedAmpHours));
|
||||
MqttSettings.publish("battery/lastFullCharge", String(_lastFullCharge));
|
||||
MqttSettings.publish("battery/midpointVoltage", String(_midpointVoltage));
|
||||
MqttSettings.publish("battery/midpointDeviation", String(_midpointDeviation));
|
||||
}
|
||||
|
||||
@ -389,8 +389,10 @@ void HuaweiCanClass::loop()
|
||||
MessageOutput.printf("[HuaweiCanClass::loop] newPowerLimit: %f, output_power: %f \r\n", newPowerLimit, _rp.output_power);
|
||||
}
|
||||
|
||||
// Check whether the battery SoC limit setting is enabled
|
||||
if (config.Battery.Enabled && config.Huawei.Auto_Power_BatterySoC_Limits_Enabled) {
|
||||
uint8_t _batterySoC = Battery.getStats()->getSoC();
|
||||
// Sets power limit to 0 if the BMS reported SoC reaches or exceeds the user configured value
|
||||
if (_batterySoC >= config.Huawei.Auto_Power_Stop_BatterySoC_Threshold) {
|
||||
newPowerLimit = 0;
|
||||
if (verboseLogging) {
|
||||
@ -427,7 +429,7 @@ void HuaweiCanClass::loop()
|
||||
float calculatedCurrent = efficiency * (newPowerLimit / _rp.output_voltage);
|
||||
|
||||
// Limit output current to value requested by BMS
|
||||
float permissableCurrent = stats->getChargeCurrentLimitation() - (stats->getChargeCurrent() - _rp.output_current); // BMS current limit - current from other sources
|
||||
float permissableCurrent = stats->getChargeCurrentLimitation() - (stats->getChargeCurrent() - _rp.output_current); // BMS current limit - current from other sources, e.g. Victron MPPT charger
|
||||
float outputCurrent = std::min(calculatedCurrent, permissableCurrent);
|
||||
outputCurrent= outputCurrent > 0 ? outputCurrent : 0;
|
||||
|
||||
|
||||
@ -5,9 +5,10 @@
|
||||
#include "MessageOutput.h"
|
||||
#include "JkBmsDataPoints.h"
|
||||
#include "JkBmsController.h"
|
||||
#include "SerialPortManager.h"
|
||||
#include <frozen/map.h>
|
||||
|
||||
//#define JKBMS_DUMMY_SERIAL
|
||||
namespace JkBms {
|
||||
|
||||
#ifdef JKBMS_DUMMY_SERIAL
|
||||
class DummySerial {
|
||||
@ -196,13 +197,8 @@ class DummySerial {
|
||||
size_t _msg_idx = 0;
|
||||
size_t _byte_idx = 0;
|
||||
};
|
||||
DummySerial HwSerial;
|
||||
#else
|
||||
HardwareSerial HwSerial((ARDUINO_USB_CDC_ON_BOOT != 1)?2:0);
|
||||
#endif
|
||||
|
||||
namespace JkBms {
|
||||
|
||||
bool Controller::init(bool verboseLogging)
|
||||
{
|
||||
_verboseLogging = verboseLogging;
|
||||
@ -220,9 +216,18 @@ bool Controller::init(bool verboseLogging)
|
||||
return false;
|
||||
}
|
||||
|
||||
HwSerial.end(); // make sure the UART will be re-initialized
|
||||
HwSerial.begin(115200, SERIAL_8N1, pin.battery_rx, pin.battery_tx);
|
||||
HwSerial.flush();
|
||||
#ifdef JKBMS_DUMMY_SERIAL
|
||||
_upSerial = std::make_unique<DummySerial>();
|
||||
#else
|
||||
auto oHwSerialPort = SerialPortManager.allocatePort(_serialPortOwner);
|
||||
if (!oHwSerialPort) { return false; }
|
||||
|
||||
_upSerial = std::make_unique<HardwareSerial>(*oHwSerialPort);
|
||||
#endif
|
||||
|
||||
_upSerial->end(); // make sure the UART will be re-initialized
|
||||
_upSerial->begin(115200, SERIAL_8N1, pin.battery_rx, pin.battery_tx);
|
||||
_upSerial->flush();
|
||||
|
||||
if (Interface::Transceiver != getInterface()) { return true; }
|
||||
|
||||
@ -242,10 +247,12 @@ bool Controller::init(bool verboseLogging)
|
||||
|
||||
void Controller::deinit()
|
||||
{
|
||||
HwSerial.end();
|
||||
_upSerial->end();
|
||||
|
||||
if (_rxEnablePin > 0) { pinMode(_rxEnablePin, INPUT); }
|
||||
if (_txEnablePin > 0) { pinMode(_txEnablePin, INPUT); }
|
||||
|
||||
SerialPortManager.freePort(_serialPortOwner);
|
||||
}
|
||||
|
||||
Controller::Interface Controller::getInterface() const
|
||||
@ -296,7 +303,7 @@ void Controller::sendRequest(uint8_t pollInterval)
|
||||
return announceStatus(Status::WaitingForPollInterval);
|
||||
}
|
||||
|
||||
if (!HwSerial.availableForWrite()) {
|
||||
if (!_upSerial->availableForWrite()) {
|
||||
return announceStatus(Status::HwSerialNotAvailableForWrite);
|
||||
}
|
||||
|
||||
@ -307,10 +314,10 @@ void Controller::sendRequest(uint8_t pollInterval)
|
||||
digitalWrite(_txEnablePin, HIGH); // enable transmission
|
||||
}
|
||||
|
||||
HwSerial.write(readAll.data(), readAll.size());
|
||||
_upSerial->write(readAll.data(), readAll.size());
|
||||
|
||||
if (Interface::Transceiver == getInterface()) {
|
||||
HwSerial.flush();
|
||||
_upSerial->flush();
|
||||
digitalWrite(_rxEnablePin, LOW); // enable reception
|
||||
digitalWrite(_txEnablePin, LOW); // disable transmission (free the bus)
|
||||
}
|
||||
@ -326,8 +333,8 @@ void Controller::loop()
|
||||
CONFIG_T& config = Configuration.get();
|
||||
uint8_t pollInterval = config.Battery.JkBmsPollingInterval;
|
||||
|
||||
while (HwSerial.available()) {
|
||||
rxData(HwSerial.read());
|
||||
while (_upSerial->available()) {
|
||||
rxData(_upSerial->read());
|
||||
}
|
||||
|
||||
sendRequest(pollInterval);
|
||||
|
||||
@ -92,10 +92,10 @@ void MqttHandleVedirectHassClass::publishConfig()
|
||||
publishSensor("VE.Smart network total DC input power", "mdi:solar-power", "NetworkTotalDcInputPower", "power", "measurement", "W", *optMpptData);
|
||||
}
|
||||
if (optMpptData->MpptTemperatureMilliCelsius.first != 0) {
|
||||
publishSensor("MPPT temperature", "mdi:temperature-celsius", "MpptTemperature", "temperature", "measurement", "W", *optMpptData);
|
||||
publishSensor("MPPT temperature", "mdi:temperature-celsius", "MpptTemperature", "temperature", "measurement", "°C", *optMpptData);
|
||||
}
|
||||
if (optMpptData->SmartBatterySenseTemperatureMilliCelsius.first != 0) {
|
||||
publishSensor("Smart Battery Sense temperature", "mdi:temperature-celsius", "SmartBatterySenseTemperature", "temperature", "measurement", "W", *optMpptData);
|
||||
publishSensor("Smart Battery Sense temperature", "mdi:temperature-celsius", "SmartBatterySenseTemperature", "temperature", "measurement", "°C", *optMpptData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -87,6 +87,8 @@ void MqttHandleBatteryHassClass::loop()
|
||||
publishSensor("Current", "mdi:current-dc", "BatteryCurrentMilliAmps", "current", "measurement", "mA");
|
||||
publishSensor("BMS Temperature", "mdi:thermometer", "BmsTempCelsius", "temperature", "measurement", "°C");
|
||||
publishSensor("Cell Voltage Diff", "mdi:battery-alert", "CellDiffMilliVolt", "voltage", "measurement", "mV");
|
||||
publishSensor("Battery Temperature 1", "mdi:thermometer", "BatteryTempOneCelsius", "temperature", "measurement", "°C");
|
||||
publishSensor("Battery Temperature 2", "mdi:thermometer", "BatteryTempTwoCelsius", "temperature", "measurement", "°C");
|
||||
publishSensor("Charge Cycles", "mdi:counter", "BatteryCycles");
|
||||
publishSensor("Cycle Capacity", "mdi:battery-sync", "BatteryCycleCapacity");
|
||||
|
||||
@ -120,6 +122,8 @@ void MqttHandleBatteryHassClass::loop()
|
||||
publishSensor("Charge Cycles", "mdi:counter", "chargeCycles");
|
||||
publishSensor("Consumed Amp Hours", NULL, "consumedAmpHours", NULL, "measurement", "Ah");
|
||||
publishSensor("Last Full Charge", "mdi:timelapse", "lastFullCharge", NULL, NULL, "min");
|
||||
publishSensor("Midpoint Voltage", NULL, "midpointVoltage", "voltage", "measurement", "V");
|
||||
publishSensor("Midpoint Deviation", NULL, "midpointDeviation", "battery", "measurement", "%");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@ -110,7 +110,7 @@ void MqttHandleVedirectClass::publish_mppt_data(const VeDirectMpptController::da
|
||||
|
||||
PUBLISH(productID_PID, "PID", currentData.getPidAsString().data());
|
||||
PUBLISH(serialNr_SER, "SER", currentData.serialNr_SER);
|
||||
PUBLISH(firmwareNr_FW, "FW", currentData.firmwareNr_FW);
|
||||
PUBLISH(firmwareVer_FW, "FW", currentData.firmwareVer_FW);
|
||||
PUBLISH(loadOutputState_LOAD, "LOAD", (currentData.loadOutputState_LOAD ? "ON" : "OFF"));
|
||||
PUBLISH(currentState_CS, "CS", currentData.getCsAsString().data());
|
||||
PUBLISH(errorCode_ERR, "ERR", currentData.getErrAsString().data());
|
||||
|
||||
@ -100,6 +100,14 @@
|
||||
#define VICTRON_PIN_RX2 -1
|
||||
#endif
|
||||
|
||||
#ifndef VICTRON_PIN_TX3
|
||||
#define VICTRON_PIN_TX3 -1
|
||||
#endif
|
||||
|
||||
#ifndef VICTRON_PIN_RX3
|
||||
#define VICTRON_PIN_RX3 -1
|
||||
#endif
|
||||
|
||||
#ifndef BATTERY_PIN_RX
|
||||
#define BATTERY_PIN_RX -1
|
||||
#endif
|
||||
@ -207,8 +215,11 @@ PinMappingClass::PinMappingClass()
|
||||
_pinMapping.victron_rx = VICTRON_PIN_RX;
|
||||
_pinMapping.victron_tx = VICTRON_PIN_TX;
|
||||
|
||||
_pinMapping.victron_rx2 = VICTRON_PIN_RX;
|
||||
_pinMapping.victron_tx2 = VICTRON_PIN_TX;
|
||||
_pinMapping.victron_rx2 = VICTRON_PIN_RX2;
|
||||
_pinMapping.victron_tx2 = VICTRON_PIN_TX2;
|
||||
|
||||
_pinMapping.victron_rx3 = VICTRON_PIN_RX3;
|
||||
_pinMapping.victron_tx3 = VICTRON_PIN_TX3;
|
||||
|
||||
_pinMapping.battery_rx = BATTERY_PIN_RX;
|
||||
_pinMapping.battery_rxen = BATTERY_PIN_RXEN;
|
||||
@ -292,6 +303,8 @@ bool PinMappingClass::init(const String& deviceMapping)
|
||||
_pinMapping.victron_tx = doc[i]["victron"]["tx"] | VICTRON_PIN_TX;
|
||||
_pinMapping.victron_rx2 = doc[i]["victron"]["rx2"] | VICTRON_PIN_RX2;
|
||||
_pinMapping.victron_tx2 = doc[i]["victron"]["tx2"] | VICTRON_PIN_TX2;
|
||||
_pinMapping.victron_rx3 = doc[i]["victron"]["rx3"] | VICTRON_PIN_RX3;
|
||||
_pinMapping.victron_tx3 = doc[i]["victron"]["tx3"] | VICTRON_PIN_TX3;
|
||||
|
||||
_pinMapping.battery_rx = doc[i]["battery"]["rx"] | BATTERY_PIN_RX;
|
||||
_pinMapping.battery_rxen = doc[i]["battery"]["rxen"] | BATTERY_PIN_RXEN;
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#include "MqttSettings.h"
|
||||
#include "NetworkSettings.h"
|
||||
#include "MessageOutput.h"
|
||||
#include "SerialPortManager.h"
|
||||
#include <ctime>
|
||||
#include <SMA_HM.h>
|
||||
|
||||
@ -57,16 +58,22 @@ void PowerMeterClass::init(Scheduler& scheduler)
|
||||
}
|
||||
|
||||
case Source::SDM1PH:
|
||||
case Source::SDM3PH:
|
||||
case Source::SDM3PH: {
|
||||
if (pin.powermeter_rx < 0 || pin.powermeter_tx < 0) {
|
||||
MessageOutput.println("[PowerMeter] invalid pin config for SDM power meter (RX and TX pins must be defined)");
|
||||
return;
|
||||
}
|
||||
|
||||
_upSdm = std::make_unique<SDM>(Serial2, 9600, pin.powermeter_dere,
|
||||
auto oHwSerialPort = SerialPortManager.allocatePort(_sdmSerialPortOwner);
|
||||
if (!oHwSerialPort) { return; }
|
||||
|
||||
_upSdmSerial = std::make_unique<HardwareSerial>(*oHwSerialPort);
|
||||
_upSdmSerial->end(); // make sure the UART will be re-initialized
|
||||
_upSdm = std::make_unique<SDM>(*_upSdmSerial, 9600, pin.powermeter_dere,
|
||||
SERIAL_8N1, pin.powermeter_rx, pin.powermeter_tx);
|
||||
_upSdm->begin();
|
||||
break;
|
||||
}
|
||||
|
||||
case Source::HTTP:
|
||||
HttpPowerMeter.init();
|
||||
|
||||
@ -2,59 +2,46 @@
|
||||
#include "SerialPortManager.h"
|
||||
#include "MessageOutput.h"
|
||||
|
||||
#define MAX_CONTROLLERS 3
|
||||
|
||||
SerialPortManagerClass SerialPortManager;
|
||||
|
||||
bool SerialPortManagerClass::allocateBatteryPort(int port)
|
||||
void SerialPortManagerClass::init()
|
||||
{
|
||||
return allocatePort(port, Owner::BATTERY);
|
||||
}
|
||||
|
||||
bool SerialPortManagerClass::allocateMpptPort(int port)
|
||||
{
|
||||
return allocatePort(port, Owner::MPPT);
|
||||
}
|
||||
|
||||
bool SerialPortManagerClass::allocatePort(uint8_t port, Owner owner)
|
||||
{
|
||||
if (port >= MAX_CONTROLLERS) {
|
||||
MessageOutput.printf("[SerialPortManager] Invalid serial port = %d \r\n", port);
|
||||
return false;
|
||||
if (ARDUINO_USB_CDC_ON_BOOT != 1) {
|
||||
_ports[0] = "Serial Console";
|
||||
MessageOutput.println("[SerialPortManager] HW UART port 0 now in use "
|
||||
"by 'Serial Console'");
|
||||
}
|
||||
|
||||
return allocatedPorts.insert({port, owner}).second;
|
||||
}
|
||||
|
||||
void SerialPortManagerClass::invalidateBatteryPort()
|
||||
std::optional<uint8_t> SerialPortManagerClass::allocatePort(std::string const& owner)
|
||||
{
|
||||
invalidate(Owner::BATTERY);
|
||||
}
|
||||
|
||||
void SerialPortManagerClass::invalidateMpptPorts()
|
||||
{
|
||||
invalidate(Owner::MPPT);
|
||||
}
|
||||
|
||||
void SerialPortManagerClass::invalidate(Owner owner)
|
||||
{
|
||||
for (auto it = allocatedPorts.begin(); it != allocatedPorts.end();) {
|
||||
if (it->second == owner) {
|
||||
MessageOutput.printf("[SerialPortManager] Removing port = %d, owner = %s \r\n", it->first, print(owner));
|
||||
it = allocatedPorts.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
for (size_t i = 0; i < _ports.size(); ++i) {
|
||||
if (_ports[i] != "") {
|
||||
MessageOutput.printf("[SerialPortManager] HW UART %d already "
|
||||
"in use by '%s'\r\n", i, _ports[i].c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
_ports[i] = owner;
|
||||
|
||||
MessageOutput.printf("[SerialPortManager] HW UART %d now in use "
|
||||
"by '%s'\r\n", i, owner.c_str());
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
MessageOutput.printf("[SerialPortManager] Cannot assign another HW "
|
||||
"UART port to '%s'\r\n", owner.c_str());
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const char* SerialPortManagerClass::print(Owner owner)
|
||||
void SerialPortManagerClass::freePort(std::string const& owner)
|
||||
{
|
||||
switch (owner) {
|
||||
case BATTERY:
|
||||
return "BATTERY";
|
||||
case MPPT:
|
||||
return "MPPT";
|
||||
for (size_t i = 0; i < _ports.size(); ++i) {
|
||||
if (_ports[i] != owner) { continue; }
|
||||
|
||||
MessageOutput.printf("[SerialPortManager] Freeing HW UART %d, owner "
|
||||
"was '%s'\r\n", i, owner.c_str());
|
||||
_ports[i] = "";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
@ -22,39 +22,46 @@ void VictronMpptClass::updateSettings()
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
_controllers.clear();
|
||||
SerialPortManager.invalidateMpptPorts();
|
||||
for (auto const& o: _serialPortOwners) {
|
||||
SerialPortManager.freePort(o.c_str());
|
||||
}
|
||||
_serialPortOwners.clear();
|
||||
|
||||
CONFIG_T& config = Configuration.get();
|
||||
if (!config.Vedirect.Enabled) { return; }
|
||||
|
||||
const PinMapping_t& pin = PinMapping.get();
|
||||
|
||||
int hwSerialPort = 1;
|
||||
bool initSuccess = initController(pin.victron_rx, pin.victron_tx, config.Vedirect.VerboseLogging, hwSerialPort);
|
||||
if (initSuccess) {
|
||||
hwSerialPort++;
|
||||
}
|
||||
initController(pin.victron_rx, pin.victron_tx,
|
||||
config.Vedirect.VerboseLogging, 1);
|
||||
|
||||
initController(pin.victron_rx2, pin.victron_tx2, config.Vedirect.VerboseLogging, hwSerialPort);
|
||||
initController(pin.victron_rx2, pin.victron_tx2,
|
||||
config.Vedirect.VerboseLogging, 2);
|
||||
|
||||
initController(pin.victron_rx3, pin.victron_tx3,
|
||||
config.Vedirect.VerboseLogging, 3);
|
||||
}
|
||||
|
||||
bool VictronMpptClass::initController(int8_t rx, int8_t tx, bool logging, int hwSerialPort)
|
||||
bool VictronMpptClass::initController(int8_t rx, int8_t tx, bool logging,
|
||||
uint8_t instance)
|
||||
{
|
||||
MessageOutput.printf("[VictronMppt] rx = %d, tx = %d, hwSerialPort = %d\r\n", rx, tx, hwSerialPort);
|
||||
MessageOutput.printf("[VictronMppt Instance %d] rx = %d, tx = %d\r\n",
|
||||
instance, rx, tx);
|
||||
|
||||
if (rx < 0) {
|
||||
MessageOutput.printf("[VictronMppt] invalid pin config rx = %d, tx = %d\r\n", rx, tx);
|
||||
MessageOutput.printf("[VictronMppt Instance %d] invalid pin config\r\n", instance);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SerialPortManager.allocateMpptPort(hwSerialPort)) {
|
||||
MessageOutput.printf("[VictronMppt] Serial port %d already in use. Initialization aborted!\r\n",
|
||||
hwSerialPort);
|
||||
return false;
|
||||
}
|
||||
String owner("Victron MPPT ");
|
||||
owner += String(instance);
|
||||
auto oHwSerialPort = SerialPortManager.allocatePort(owner.c_str());
|
||||
if (!oHwSerialPort) { return false; }
|
||||
|
||||
_serialPortOwners.push_back(owner);
|
||||
|
||||
auto upController = std::make_unique<VeDirectMpptController>();
|
||||
upController->init(rx, tx, &MessageOutput, logging, hwSerialPort);
|
||||
upController->init(rx, tx, &MessageOutput, logging, *oHwSerialPort);
|
||||
_controllers.push_back(std::move(upController));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -3,7 +3,12 @@
|
||||
#include "Configuration.h"
|
||||
#include "PinMapping.h"
|
||||
#include "MessageOutput.h"
|
||||
#include "SerialPortManager.h"
|
||||
|
||||
void VictronSmartShunt::deinit()
|
||||
{
|
||||
SerialPortManager.freePort(_serialPortOwner);
|
||||
}
|
||||
|
||||
bool VictronSmartShunt::init(bool verboseLogging)
|
||||
{
|
||||
@ -21,7 +26,10 @@ bool VictronSmartShunt::init(bool verboseLogging)
|
||||
auto tx = static_cast<gpio_num_t>(pin.battery_tx);
|
||||
auto rx = static_cast<gpio_num_t>(pin.battery_rx);
|
||||
|
||||
VeDirectShunt.init(rx, tx, &MessageOutput, verboseLogging);
|
||||
auto oHwSerialPort = SerialPortManager.allocatePort(_serialPortOwner);
|
||||
if (!oHwSerialPort) { return false; }
|
||||
|
||||
VeDirectShunt.init(rx, tx, &MessageOutput, verboseLogging, *oHwSerialPort);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -91,6 +91,8 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request)
|
||||
victronPinObj["tx"] = pin.victron_tx;
|
||||
victronPinObj["rx2"] = pin.victron_rx2;
|
||||
victronPinObj["tx2"] = pin.victron_tx2;
|
||||
victronPinObj["rx3"] = pin.victron_rx3;
|
||||
victronPinObj["tx3"] = pin.victron_tx3;
|
||||
|
||||
auto batteryPinObj = curPin["battery"].to<JsonObject>();
|
||||
batteryPinObj["rx"] = pin.battery_rx;
|
||||
|
||||
@ -48,6 +48,7 @@ void WebApiSysstatusClass::onSystemStatus(AsyncWebServerRequest* request)
|
||||
root["chiprevision"] = ESP.getChipRevision();
|
||||
root["chipmodel"] = ESP.getChipModel();
|
||||
root["chipcores"] = ESP.getChipCores();
|
||||
root["flashsize"] = ESP.getFlashChipSize();
|
||||
|
||||
String reason;
|
||||
reason = ResetReason::get_reset_reason_verbose(0);
|
||||
|
||||
@ -139,7 +139,7 @@ void WebApiWsVedirectLiveClass::generateCommonJsonResponse(JsonVariant& root, bo
|
||||
|
||||
void WebApiWsVedirectLiveClass::populateJson(const JsonObject &root, const VeDirectMpptController::data_t &mpptData) {
|
||||
root["product_id"] = mpptData.getPidAsString();
|
||||
root["firmware_version"] = String(mpptData.firmwareNr_FW);
|
||||
root["firmware_version"] = mpptData.getFwVersionFormatted();
|
||||
|
||||
const JsonObject values = root["values"].to<JsonObject>();
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include "InverterSettings.h"
|
||||
#include "Led_Single.h"
|
||||
#include "MessageOutput.h"
|
||||
#include "SerialPortManager.h"
|
||||
#include "VictronMppt.h"
|
||||
#include "Battery.h"
|
||||
#include "Huawei_can.h"
|
||||
@ -96,6 +97,8 @@ void setup()
|
||||
const auto& pin = PinMapping.get();
|
||||
MessageOutput.println("done");
|
||||
|
||||
SerialPortManager.init();
|
||||
|
||||
// Initialize WiFi
|
||||
MessageOutput.print("Initialize Network... ");
|
||||
NetworkSettings.init(scheduler);
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<link rel="apple-touch-icon" href="/favicon.png">
|
||||
<link rel="manifest" href='/site.webmanifest' />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>OpenDTU-onBattery</title>
|
||||
<title>OpenDTU-OnBattery</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
|
||||
@ -18,6 +18,12 @@
|
||||
<div style="padding-right: 2em;">
|
||||
{{ $t('battery.battery') }}: {{ batteryData.manufacturer }}
|
||||
</div>
|
||||
<div style="padding-right: 2em;" v-if="'fwversion' in batteryData">
|
||||
{{ $t('battery.FwVersion') }}: {{ batteryData.fwversion }}
|
||||
</div>
|
||||
<div style="padding-right: 2em;" v-if="'hwversion' in batteryData">
|
||||
{{ $t('battery.HwVersion') }}: {{ batteryData.hwversion }}
|
||||
</div>
|
||||
<div style="padding-right: 2em;">
|
||||
{{ $t('battery.DataAge') }} {{ $t('battery.Seconds', { 'val': batteryData.data_age }) }}
|
||||
</div>
|
||||
|
||||
@ -19,6 +19,13 @@
|
||||
<th>{{ $t('hardwareinfo.CpuFrequency') }}</th>
|
||||
<td>{{ systemStatus.cpufreq }} {{ $t('hardwareinfo.Mhz') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{ $t('hardwareinfo.FlashSize') }}</th>
|
||||
<td>
|
||||
{{ systemStatus.flashsize }} {{ $t('hardwareinfo.Bytes') }}
|
||||
({{ systemStatus.flashsize / 1024 / 1024 }} {{ $t('hardwareinfo.MegaBytes') }})
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
<BIconSun v-else width="30" height="30" class="d-inline-block align-text-top text-warning" />
|
||||
|
||||
<span style="margin-left: .5rem">
|
||||
OpenDTU-onBattery
|
||||
OpenDTU-OnBattery
|
||||
</span>
|
||||
<span class="text-info">
|
||||
<BIconBatteryCharging width="20" height="20" class="d-inline-block align-text-center" />
|
||||
|
||||
@ -21,13 +21,13 @@
|
||||
{{ item.product_id }}
|
||||
</div>
|
||||
<div style="padding-right: 2em;">
|
||||
{{ $t('vedirecthome.SerialNumber') }} {{ serial }}
|
||||
{{ $t('vedirecthome.SerialNumber') }}: {{ serial }}
|
||||
</div>
|
||||
<div style="padding-right: 2em;">
|
||||
{{ $t('vedirecthome.FirmwareNumber') }} {{ item.firmware_version }}
|
||||
{{ $t('vedirecthome.FirmwareVersion') }}: {{ item.firmware_version }}
|
||||
</div>
|
||||
<div style="padding-right: 2em;">
|
||||
{{ $t('vedirecthome.DataAge') }} {{ $t('vedirecthome.Seconds', {'val': Math.floor(item.data_age_ms / 1000)}) }}
|
||||
{{ $t('vedirecthome.DataAge') }}: {{ $t('vedirecthome.Seconds', {'val': Math.floor(item.data_age_ms / 1000)}) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -152,9 +152,9 @@
|
||||
"LoadingInverter": "Warte auf Daten... (kann bis zu 10 Sekunden dauern)"
|
||||
},
|
||||
"vedirecthome": {
|
||||
"SerialNumber": "Seriennummer: ",
|
||||
"FirmwareNumber": "Firmware Version: ",
|
||||
"DataAge": "letzte Aktualisierung: ",
|
||||
"SerialNumber": "Seriennummer",
|
||||
"FirmwareVersion": "Firmware-Version",
|
||||
"DataAge": "letzte Aktualisierung",
|
||||
"Seconds": "vor {val} Sekunden",
|
||||
"Property": "Eigenschaft",
|
||||
"Value": "Wert",
|
||||
@ -250,7 +250,10 @@
|
||||
"ChipRevision": "Chip-Revision",
|
||||
"ChipCores": "Chip-Kerne",
|
||||
"CpuFrequency": "CPU-Frequenz",
|
||||
"Mhz": "MHz"
|
||||
"Mhz": "MHz",
|
||||
"FlashSize": "Flash-Speichergröße",
|
||||
"Bytes": "Bytes",
|
||||
"MegaBytes": "MB"
|
||||
},
|
||||
"memoryinfo": {
|
||||
"MemoryInformation": "Speicherinformationen",
|
||||
@ -569,7 +572,10 @@
|
||||
"sdmaddress": "Modbus Adresse",
|
||||
"HTTP": "HTTP(S) + JSON - Allgemeine Konfiguration",
|
||||
"httpIndividualRequests": "Individuelle HTTP requests pro Phase",
|
||||
"httpUrlDescription": "Die URL muss mit http:// oder https:// beginnen. Manche Zeichen wie Leerzeichen und = müssen mit URL-Kodierung kodiert werden (%xx). Achtung: Ein Überprüfung von SSL Server Zertifikaten ist nicht implementiert (MITM-Attacken sind möglich)! Beispiele gibt es unten.",
|
||||
"urlExamplesHeading": "Beispiele für URLs",
|
||||
"jsonPathExamplesHeading": "Beispiele für JSON Pfade",
|
||||
"jsonPathExamplesExplanation": "Die folgenden Pfade finden jeweils den Wert '123.4' im jeweiligen Beispiel-JSON.",
|
||||
"httpUrlDescription": "Die URL muss mit http:// oder https:// beginnen. Manche Zeichen wie Leerzeichen und = müssen mit URL-Kodierung kodiert werden (%xx). Achtung: Ein Überprüfung von SSL Server Zertifikaten ist nicht implementiert (MITM-Attacken sind möglich)!.",
|
||||
"httpPhase": "HTTP(S) + JSON Konfiguration - Phase {phaseNumber}",
|
||||
"httpEnabled": "Phase aktiviert",
|
||||
"httpUrl": "URL",
|
||||
@ -628,7 +634,8 @@
|
||||
"VoltageSolarPassthroughStopThreshold": "Full-Solar-Passthrough Stop-Schwellwert",
|
||||
"VoltageLoadCorrectionFactor": "Lastkorrekturfaktor",
|
||||
"BatterySocInfo": "<b>Hinweis:</b> Die Akku SoC (State of Charge) Werte werden nur benutzt, wenn die Batterie-Kommunikationsschnittstelle innerhalb der letzten Minute gültige Werte geschickt hat. Andernfalls werden als Fallback-Option die Spannungseinstellungen verwendet.",
|
||||
"InverterIsBehindPowerMeter": "Stromzählermessung beinhaltet Wechselrichterleistung",
|
||||
"InverterIsBehindPowerMeter": "Stromzählermessung beinhaltet Wechselrichter",
|
||||
"InverterIsBehindPowerMeterHint": "Aktivieren falls sich der Stromzähler-Messwert um die Ausgangsleistung des Wechselrichters verringert, wenn dieser Strom produziert. Normalerweise ist das zutreffend.",
|
||||
"InverterIsSolarPowered": "Wechselrichter wird von Solarmodulen gespeist",
|
||||
"VoltageThresholds": "Batterie Spannungs-Schwellwerte ",
|
||||
"VoltageLoadCorrectionInfo": "<b>Hinweis:</b> Wenn Leistung von der Batterie abgegeben wird, bricht ihre Spannung etwas ein. Der Spannungseinbruch skaliert mit dem Entladestrom. Damit nicht vorzeitig der Wechselrichter ausgeschaltet wird sobald der Stop-Schwellenwert unterschritten wurde, wird der hier angegebene Korrekturfaktor mit einberechnet um die Spannung zu errechnen die der Akku in Ruhe hätte. Korrigierte Spannung = DC Spannung + (Aktuelle Leistung (W) * Korrekturfaktor).",
|
||||
@ -867,6 +874,8 @@
|
||||
},
|
||||
"battery": {
|
||||
"battery": "Batterie",
|
||||
"FwVersion": "Firmware-Version",
|
||||
"HwVersion": "Hardware-Version",
|
||||
"DataAge": "letzte Aktualisierung: ",
|
||||
"Seconds": "vor {val} Sekunden",
|
||||
"status": "Status",
|
||||
@ -935,6 +944,8 @@
|
||||
"dischargedEnergy": "Entladene Energie",
|
||||
"instantaneousPower": "Aktuelle Leistung",
|
||||
"consumedAmpHours": "Verbrauchte Amperestunden",
|
||||
"midpointVoltage": "Mittelpunktspannung",
|
||||
"midpointDeviation": "Mittelpunktsabweichung",
|
||||
"lastFullCharge": "Letztes mal Vollgeladen"
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,9 +152,9 @@
|
||||
"LoadingInverter": "Waiting for data... (can take up to 10 seconds)"
|
||||
},
|
||||
"vedirecthome": {
|
||||
"SerialNumber": "Serial Number: ",
|
||||
"FirmwareNumber": "Firmware Number: ",
|
||||
"DataAge": "Data Age: ",
|
||||
"SerialNumber": "Serial Number",
|
||||
"FirmwareVersion": "Firmware Version",
|
||||
"DataAge": "Data Age",
|
||||
"Seconds": "{val} seconds",
|
||||
"Property": "Property",
|
||||
"Value": "Value",
|
||||
@ -251,7 +251,10 @@
|
||||
"ChipRevision": "Chip Revision",
|
||||
"ChipCores": "Chip Cores",
|
||||
"CpuFrequency": "CPU Frequency",
|
||||
"Mhz": "MHz"
|
||||
"Mhz": "MHz",
|
||||
"FlashSize": "Flash Memory Size",
|
||||
"Bytes": "Bytes",
|
||||
"MegaBytes": "MB"
|
||||
},
|
||||
"memoryinfo": {
|
||||
"MemoryInformation": "Memory Information",
|
||||
@ -571,10 +574,13 @@
|
||||
"sdmaddress": "Modbus Address",
|
||||
"HTTP": "HTTP(S) + Json - General configuration",
|
||||
"httpIndividualRequests": "Individual HTTP requests per phase",
|
||||
"urlExamplesHeading": "URL Examples",
|
||||
"jsonPathExamplesHeading": "JSON Path Examples",
|
||||
"jsonPathExamplesExplanation": "The following paths each find the value '123.4' in the respective example JSON.",
|
||||
"httpPhase": "HTTP(S) + Json configuration - Phase {phaseNumber}",
|
||||
"httpEnabled": "Phase enabled",
|
||||
"httpUrl": "URL",
|
||||
"httpUrlDescription": "URL must start with http:// or https://. Some characters like spaces and = have to be encoded with URL encoding (%xx). Warning: SSL server certificate check is not implemented (MITM attacks are possible)! See below for some examples.",
|
||||
"httpUrlDescription": "URL must start with http:// or https://. Some characters like spaces and = have to be encoded with URL encoding (%xx). Warning: SSL server certificate check is not implemented (MITM attacks are possible)!",
|
||||
"httpAuthorization": "Authorization Type",
|
||||
"httpUsername": "Username",
|
||||
"httpPassword": "Password",
|
||||
@ -635,6 +641,7 @@
|
||||
"VoltageLoadCorrectionFactor": "Load correction factor",
|
||||
"BatterySocInfo": "<b>Hint:</b> The battery SoC (State of Charge) values are only used if the battery communication interface reported SoC updates in the last minute. Otherwise the voltage thresholds will be used as fallback.",
|
||||
"InverterIsBehindPowerMeter": "PowerMeter reading includes inverter output",
|
||||
"InverterIsBehindPowerMeterHint": "Enable this option if the power meter reading is reduced by the inverter's output when it produces power. This is typically true.",
|
||||
"InverterIsSolarPowered": "Inverter is powered by solar modules",
|
||||
"VoltageThresholds": "Battery Voltage Thresholds",
|
||||
"VoltageLoadCorrectionInfo": "<b>Hint:</b> When the battery is discharged, its voltage drops. The voltage drop scales with the discharge current. In order to not stop the inverter too early (stop threshold), this load correction factor can be specified to calculate the battery voltage if it was idle. Corrected voltage = DC Voltage + (Current power * correction factor).",
|
||||
@ -874,6 +881,8 @@
|
||||
},
|
||||
"battery": {
|
||||
"battery": "Battery",
|
||||
"FwVersion": "Firmware Version",
|
||||
"HwVersion": "Hardware Version",
|
||||
"DataAge": "Data Age: ",
|
||||
"Seconds": " {val} seconds",
|
||||
"status": "Status",
|
||||
@ -942,6 +951,8 @@
|
||||
"dischargedEnergy": "Discharged energy",
|
||||
"instantaneousPower": "Instantaneous Power",
|
||||
"consumedAmpHours": "Consumed Amp Hours",
|
||||
"midpointVoltage": "Midpoint Voltage",
|
||||
"midpointDeviation": "Midpoint Deviation",
|
||||
"lastFullCharge": "Last full Charge"
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,10 +152,10 @@
|
||||
"LoadingInverter": "Waiting for data... (can take up to 10 seconds)"
|
||||
},
|
||||
"vedirecthome": {
|
||||
"SerialNumber": "Serial Number: ",
|
||||
"FirmwareNumber": "Firmware Number: ",
|
||||
"DataAge": "Data Age: ",
|
||||
"Seconds": "{val} seconds",
|
||||
"SerialNumber": "Numéro de série",
|
||||
"FirmwareVersion": "Version du Firmware",
|
||||
"DataAge": "Âge des données",
|
||||
"Seconds": "{val} secondes",
|
||||
"Property": "Property",
|
||||
"Value": "Value",
|
||||
"Unit": "Unit",
|
||||
@ -285,7 +285,10 @@
|
||||
"ChipRevision": "Révision de la puce",
|
||||
"ChipCores": "Nombre de cœurs",
|
||||
"CpuFrequency": "Fréquence du CPU",
|
||||
"Mhz": "MHz"
|
||||
"Mhz": "MHz",
|
||||
"FlashSize": "Taille de la mémoire flash",
|
||||
"Bytes": "octets",
|
||||
"MegaBytes": "Mo"
|
||||
},
|
||||
"memoryinfo": {
|
||||
"MemoryInformation": "Informations sur la mémoire",
|
||||
@ -714,6 +717,7 @@
|
||||
"VoltageLoadCorrectionFactor": "Load correction factor",
|
||||
"BatterySocInfo": "<b>Hint:</b> The battery SoC (State of Charge) values are only used if the battery communication interface reported SoC updates in the last minute. Otherwise the voltage thresholds will be used as fallback.",
|
||||
"InverterIsBehindPowerMeter": "PowerMeter reading includes inverter output",
|
||||
"InverterIsBehindPowerMeterHint": "Enable this option if the power meter reading is reduced by the inverter's output when it produces power. This is typically true.",
|
||||
"InverterIsSolarPowered": "Inverter is powered by solar modules",
|
||||
"VoltageThresholds": "Battery Voltage Thresholds",
|
||||
"VoltageLoadCorrectionInfo": "<b>Hint:</b> When the battery is discharged, its voltage drops. The voltage drop scales with the discharge current. In order to not stop the inverter too early (stop threshold), this load correction factor can be specified to calculate the battery voltage if it was idle. Corrected voltage = DC Voltage + (Current power * correction factor)."
|
||||
@ -862,6 +866,8 @@
|
||||
},
|
||||
"battery": {
|
||||
"battery": "Battery",
|
||||
"FwVersion": "Firmware Version",
|
||||
"HwVersion": "Hardware Version",
|
||||
"DataAge": "Data Age: ",
|
||||
"Seconds": " {val} seconds",
|
||||
"status": "Status",
|
||||
@ -930,6 +936,8 @@
|
||||
"dischargedEnergy": "Discharged energy",
|
||||
"instantaneousPower": "Instantaneous Power",
|
||||
"consumedAmpHours": "Consumed Amp Hours",
|
||||
"midpointVoltage": "Midpoint Voltage",
|
||||
"midpointDeviation": "Midpoint Deviation",
|
||||
"lastFullCharge": "Last full Charge"
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,8 @@ type BatteryData = (ValueObject | string)[];
|
||||
|
||||
export interface Battery {
|
||||
manufacturer: string;
|
||||
fwversion: string;
|
||||
hwversion: string;
|
||||
data_age: number;
|
||||
values: BatteryData[];
|
||||
issues: number[];
|
||||
|
||||
@ -4,6 +4,7 @@ export interface SystemStatus {
|
||||
chiprevision: number;
|
||||
chipcores: number;
|
||||
cpufreq: number;
|
||||
flashsize: number;
|
||||
// FirmwareInfo
|
||||
hostname: string;
|
||||
sdkversion: string;
|
||||
|
||||
@ -112,7 +112,7 @@
|
||||
<div class="input-group">
|
||||
<input type="number" class="form-control" id="stopBatterySoCThreshold"
|
||||
placeholder="95" v-model="acChargerConfigList.stop_batterysoc_threshold"
|
||||
aria-describedby="stopBatterySoCThresholdDescription" min="2" max="100" required/>
|
||||
aria-describedby="stopBatterySoCThresholdDescription" min="2" max="99" required/>
|
||||
<span class="input-group-text" id="stopBatterySoCThresholdDescription">%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -102,6 +102,7 @@
|
||||
<InputElement v-show="hasPowerMeter()"
|
||||
:label="$t('powerlimiteradmin.InverterIsBehindPowerMeter')"
|
||||
v-model="powerLimiterConfigList.is_inverter_behind_powermeter"
|
||||
:tooltip="$t('powerlimiteradmin.InverterIsBehindPowerMeterHint')"
|
||||
type="checkbox" wide/>
|
||||
|
||||
<div class="row mb-3" v-if="!powerLimiterConfigList.is_inverter_solar_powered">
|
||||
|
||||
@ -106,7 +106,7 @@
|
||||
</CardElement>
|
||||
|
||||
<div class="alert alert-secondary mt-5" role="alert">
|
||||
<h2>URL examples:</h2>
|
||||
<h2>{{ $t('powermeteradmin.urlExamplesHeading') }}:</h2>
|
||||
<ul>
|
||||
<li>http://admin:secret@shelly3em.home/status</li>
|
||||
<li>https://admin:secret@shelly3em.home/status</li>
|
||||
@ -114,10 +114,12 @@
|
||||
<li>http://12.34.56.78/emeter/0</li>
|
||||
</ul>
|
||||
|
||||
<h2>JSON path examples:</h2>
|
||||
<h2>{{ $t('powermeteradmin.jsonPathExamplesHeading') }}:</h2>
|
||||
{{ $t('powermeteradmin.jsonPathExamplesExplanation') }}
|
||||
<ul>
|
||||
<li><code>power/total/watts</code> - Finds 123.4 in <code>{ "power": { "phase1": { "factor": 0.98, "watts": 42 }, "total": { "watts": 123.4 } } }</code></li>
|
||||
<li><code>total</code> - Finds 123.4 in <code>{ "othervalue": 66, "total": 123.4 }</code></li>
|
||||
<li><code>power/total/watts</code> — <code>{ "power": { "phase1": { "factor": 0.98, "watts": 42 }, "total": { "watts": 123.4 } } }</code></li>
|
||||
<li><code>data/[1]/power</code> — <code>{ "data": [ { "factor": 0.98, "power": 42 }, { "factor": 1.0, "power": 123.4 } ] } }</code></li>
|
||||
<li><code>total</code> — <code>{ "othervalue": 66, "total": 123.4 }</code></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user