Skip to content

Commit

Permalink
add support for multiple Pylontech batteries
Browse files Browse the repository at this point in the history
This release supports more than one Pylontech batteries via RS485.
Set all battery ADD switches to X000 (default Address and 115200 Baud)
Alle batteries linked via the Link Ports
First battery is the Master battery and has do be connected via RS485 to
the OpenDTU-onBattery (my special edition)
the other batteries are Slave-1 to Slave-n
Currently this version only supports 2 batteries (Master + Slave-1)
Set the "Number of batteries" in the Battery Admin GUI to the amount of
batteries you have,
The GUI exepts values between 1 and 5 for future releases but actually
it will be limited to 2 in the WebApi_battery.cpp backend.
  • Loading branch information
skippermeister committed Mar 5, 2024
1 parent b3bb1e1 commit da4ba3c
Show file tree
Hide file tree
Showing 5 changed files with 15 additions and 5 deletions.
3 changes: 2 additions & 1 deletion include/BatteryStats.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "JkBmsDataPoints.h"
#include "VeDirectShuntController.h"
#include "Configuration.h"
#include "defaults.h"

#pragma pack(push, 1)
typedef union {
Expand Down Expand Up @@ -417,7 +418,7 @@ class PylontechRS485BatteryStats : public BatteryStats {
ModuleSerialNumber_t ModuleSerialNumber;
};

Pack_t Pack[2];
Pack_t Pack[MAX_BATTERIES];
/*
String softwareVersion;
String _manufacturerVersion;
Expand Down
1 change: 1 addition & 0 deletions include/defaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@
#define POWERLIMITER_FULL_SOLAR_PASSTHROUGH_START_VOLTAGE 100
#define POWERLIMITER_FULL_SOLAR_PASSTHROUGH_STOP_VOLTAGE 100

#define MAX_BATTERIES 2 // currently only 2 Pylontech Batteries are supported (Master and one Slave)
#define BATTERY_ENABLED false
#define BATTERY_PROVIDER 0 // Pylontech RS485 receiver
#define BATTERY_JKBMS_INTERFACE 0
Expand Down
6 changes: 5 additions & 1 deletion src/PylontechRS485Receiver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "PinMapping.h"
#include <Arduino.h>
#include <ctime>
#include "defaults.h"

static constexpr char TAG[] = "[Pylontech RS485]";

Expand Down Expand Up @@ -70,10 +71,11 @@ bool PylontechRS485Receiver::init()
Battery._verboseLogging = true;

_masterBatteryID = 2; // Master battery starts with ID = 2
_lastSlaveBatteryID = _masterBatteryID + Configuration.get().Battery.numberOfBatteries;

get_pack_count(REQUEST_AND_GET, _masterBatteryID);

_lastSlaveBatteryID = _masterBatteryID + _stats->_number_of_packs;

for (uint8_t i=_masterBatteryID; i<_lastSlaveBatteryID; i++) {
MessageOutput.printf("%s %s Battery Pack %d\r\n", TAG, i==2 ? "Master" : "Slave", i);
get_protocol_version(REQUEST_AND_GET, i);
Expand Down Expand Up @@ -319,6 +321,8 @@ void PylontechRS485Receiver::get_pack_count(const PylontechRS485Receiver::Functi
}

_stats->_number_of_packs = f->info[0];
if (_stats->_number_of_packs > MAX_BATTERIES) _stats->_number_of_packs = MAX_BATTERIES; // currently limited to max 2 batteries (see default.h)
if (_stats->_number_of_packs > Configuration.get().Battery.numberOfBatteries) _stats->_number_of_packs = Configuration.get().Battery.numberOfBatteries;

if (Battery._verboseLogging) MessageOutput.printf("%s::%s Number of Battery Packs %u\r\n", TAG, __FUNCTION__, _stats->_number_of_packs);
}
Expand Down
4 changes: 4 additions & 0 deletions src/WebApi_battery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "WebApi_battery.h"
#include "WebApi_errors.h"
#include "helper.h"
#include "defaults.h"

void WebApiBatteryClass::init(AsyncWebServer& server, Scheduler& scheduler)
{
Expand Down Expand Up @@ -147,6 +148,9 @@ void WebApiBatteryClass::onAdminPost(AsyncWebServerRequest* request)
cBattery.MaxChargeTemperature = root["max_charge_temp"].as<int8_t>();
cBattery.MinDischargeTemperature = root["min_discharge_temp"].as<int8_t>();
cBattery.MaxDischargeTemperature = root["max_discharge_temp"].as<int8_t>();
cBattery.numberOfBatteries = root["numberOfBatteries"].as<int8_t>();
if (Configuration.get().Battery.numberOfBatteries > MAX_BATTERIES) Configuration.get().Battery.numberOfBatteries = MAX_BATTERIES;

#ifdef USE_MQTT_BATTERY
strlcpy(cBattery.Mqtt.SocTopic, root["mqtt_soc_topic"].as<String>().c_str(), sizeof(cBattery.Mqtt.SocTopic));
strlcpy(cBattery.Mqtt.VoltageTopic, root["mqtt_voltage_topic"].as<String>().c_str(), sizeof(cBattery.Mqtt.VoltageTopic));
Expand Down
6 changes: 3 additions & 3 deletions src/WebApi_ws_battery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ void WebApiWsBatteryLiveClass::sendDataTaskCb()
_lastUpdateCheck = millis();

for (uint8_t i=0; i< Battery.getStats()->get_number_of_packs(); i++) {
// for (uint8_t i=0; i<2; i++) {
// for (uint8_t i=0; i<MAX_BATTERIES; i++) {

try {
std::lock_guard<std::mutex> lock(_mutex);
Expand Down Expand Up @@ -114,14 +114,14 @@ void WebApiWsBatteryLiveClass::onLivedataStatus(AsyncWebServerRequest* request)

try {
std::lock_guard<std::mutex> lock(_mutex);
AsyncJsonResponse* response = new AsyncJsonResponse(false, _responseSize*3);
AsyncJsonResponse* response = new AsyncJsonResponse(false, _responseSize + MAX_BATTERIES*_responseSize);
auto& root = response->getRoot();

generateJsonResponse(root);

JsonArray packsArray = root.createNestedArray("packs");
for (uint8_t i=0; i< Battery.getStats()->get_number_of_packs(); i++) {
// for (uint8_t i=0; i<2; i++) {
// for (uint8_t i=0; i<MAX_BATTERIES; i++) {
JsonObject packObject = packsArray.createNestedObject();
Battery.getStats()->generatePackCommonJsonResponse(packObject, i);
}
Expand Down

0 comments on commit da4ba3c

Please sign in to comment.