Skip to content

Commit

Permalink
correct Meanwell charge controller algorithm and add SBS CAN Receiver
Browse files Browse the repository at this point in the history
modul
  • Loading branch information
skippermeister committed Aug 19, 2024
1 parent 6fe81cb commit 5178ae5
Show file tree
Hide file tree
Showing 20 changed files with 410 additions and 53 deletions.
1 change: 1 addition & 0 deletions include/BatteryCanReceiver.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class BatteryCanReceiver : public BatteryProvider {
uint8_t readUnsignedInt8(uint8_t *data);
uint16_t readUnsignedInt16(uint8_t *data);
int16_t readSignedInt16(uint8_t *data);
int32_t readSignedInt24(uint8_t *data);
uint32_t readUnsignedInt32(uint8_t *data);
float scaleValue(int16_t value, float factor);
bool getBit(uint8_t value, uint8_t bit);
Expand Down
51 changes: 51 additions & 0 deletions include/BatteryStats.h
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,57 @@ class PytesBatteryStats : public BatteryStats {
};
#endif

#ifdef USE_SBS_CAN_RECEIVER
class SBSBatteryStats : public BatteryStats {
friend class SBSCanReceiver;

public:
void getLiveViewData(JsonVariant& root) const final;
void generatePackCommonJsonResponse(JsonObject& packObject, const uint8_t m) const final;
void mqttPublish() /*const*/ final;
float getChargeCurrent() const { return _current; } ;
float getChargeCurrentLimitation() const { return _chargeCurrentLimit; } ;

uint8_t get_number_of_packs() const final { return 1; };

const Alarm_t& getAlarm() const final { return Alarm; };
const Warning_t& getWarning() const final { return Warning; };

bool getChargeEnabled() const final { return _chargeEnabled; };
bool getDischargeEnabled() const final { return _dischargeEnabled; };

bool isChargeTemperatureValid() const final
{
const Battery_CONFIG_T& cBattery = Configuration.get().Battery;
return (_temperature >= cBattery.MinChargeTemperature) && (_temperature <= cBattery.MaxChargeTemperature);
};
bool isDischargeTemperatureValid() const final
{
const Battery_CONFIG_T& cBattery = Configuration.get().Battery;
return (_temperature >= cBattery.MinDischargeTemperature) && (_temperature <= cBattery.MaxDischargeTemperature);
};

private:
void setManufacturer(String&& m) { _manufacturer = std::move(m); }
void setLastUpdate(uint32_t ts) { _lastUpdate = ts; }

float _chargeVoltage;
float _chargeCurrentLimit;
float _dischargeCurrentLimit;
uint16_t _stateOfHealth;
float _current;
float _temperature;

Alarm_t Alarm;
Warning_t Warning;

bool _chargeEnabled;
bool _dischargeEnabled;

const char *_state = nullptr;
};
#endif

#ifdef USE_JKBMS_CONTROLLER
class JkBmsBatteryStats : public BatteryStats {
friend class Controller;
Expand Down
2 changes: 1 addition & 1 deletion include/PylontechCanReceiver.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

class PylontechCanReceiver : public BatteryCanReceiver {
public:
explicit PylontechCanReceiver() {}
PylontechCanReceiver() {}

bool init() final;
void onMessage(twai_message_t rx_message) final;
Expand Down
2 changes: 1 addition & 1 deletion include/PytesCanReceiver.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

class PytesCanReceiver : public BatteryCanReceiver {
public:
explicit PytesCanReceiver() {}
PytesCanReceiver() {}

bool init() final;
void onMessage(twai_message_t rx_message) final;
Expand Down
24 changes: 24 additions & 0 deletions include/SBSCanReceiver.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once

#ifdef USE_SBS_CAN_RECEIVER

#include "BatteryCanReceiver.h"
#include <driver/twai.h>
#include <Arduino.h>

class SBSCanReceiver : public BatteryCanReceiver {
public:
bool init() final;
void onMessage(twai_message_t rx_message) final;

std::shared_ptr<BatteryStats> getStats() const final { return _stats; }

bool initialized() const final { return _initialized; };

private:
void dummyData();
std::shared_ptr<SBSBatteryStats> _stats = std::make_shared<SBSBatteryStats>();
};

#endif
2 changes: 1 addition & 1 deletion include/Statistic.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
template <typename T>
class WeightedAVG {
public:
WeightedAVG(int16_t factor)
explicit WeightedAVG(int16_t factor)
: _countMax(factor)
, _count(0), _countNum(0), _avgV(0), _minV(0), _maxV(0), _lastV(0) {}

Expand Down
1 change: 1 addition & 0 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ build_flags = ${env.build_flags}
-DUSE_DALYBMS_CONTROLLER=1
-DUSE_VICTRON_SMART_SHUNT=1
-DUSE_PYTES_CAN_RECEIVER=1
-DUSE_SBS_CAN_RECEIVER=1
-DUSE_BATTERY_CAN0=1
-DUSE_BATTERY_MCP2515=1
-DUSE_BATTERY_I2C=1
Expand Down
32 changes: 10 additions & 22 deletions platformio_override.ini
Original file line number Diff line number Diff line change
Expand Up @@ -36,26 +36,16 @@ build_flags = ${env.build_flags}
; -DUSE_RADIO_CMT=1
-DVICTRON_PIN_TX=-1 ; HW Serial Pins for Victron (RS232), only Text messages
-DVICTRON_PIN_RX=22
-DVICTRON_PIN_TX2=-1 ; HW Serial Pins for Victron 2 (RS232)
-DVICTRON_PIN_RX2=-1
-DVICTRON_PIN_TX3=-1 ; HW Serial Pins for Victron 3 (RS232)
-DVICTRON_PIN_RX3=-1
; -DUSE_REFUsol_INVERTER=1
; -DREFUSOL_PIN_TX=-1 ; HW Serial Pins for REFUsol Inverter (RS485)
; -DREFUSOL_PIN_RX=-1
; -DREFUSOL_PIN_RTS=-1
-DMCP2515_PIN_MISO=12 ; HSPI MCP2515
-DMCP2515_PIN_MOSI=13
-DMCP2515_PIN_SCLK=14
-DMCP2515_PIN_CS=15
-DMCP2515_PIN_IRQ=35 ; GPIO35 input only
-DUSE_CHARGER_MEANWELL=1
-DUSE_CHARGER_CAN0=1
; -DUSE_CHARGER_HUAWEI=1 ; not implemented and not yet supported
; -DHUAWEI_PIN_POWER=32
-DCAN0_PIN_RX=26 ; CAN0 (internal CAN Bus) connected to Meanwell Charger
-DCAN0_PIN_TX=27
-DCAN0_PIN_STB=-1
; -DUSE_MQTT_BATTERY=1
; -DUSE_PYLONTECH_CAN_RECEIVER=1
; -DUSE_JKBMS_CONTROLLER=1
Expand All @@ -66,25 +56,23 @@ build_flags = ${env.build_flags}
-DBATTERY_PIN_RX=16 ; RS485 HW Serial (Pylontech Battery via RS485, Daly BMS via RS485/RS232 or JK BMS via RS485/RS232)
-DBATTERY_PIN_TX=17
-DBATTERY_PIN_RTS=4
-DBATTERY_PIN_WAKEUP=-1
-DPRE_CHARGE_PIN=32 ; MosFET Transitor
-DFULL_POWER_PIN=33 ; MosFET Transitor
; -DUSE_POWERMETER_HWSERIAL ; if not enabled, Powermeter RS485 connected uses SoftwareSerial
-DPOWERMETER_PIN_RX=39 ; RS485 SoftwareSerial (SDM630 via RS485 or SML Reader)
-DPOWERMETER_PIN_TX=0
-DPOWERMETER_PIN_RTS=-1
-DPRE_CHARGE_PIN=32 ; MosFET Transitor
-DFULL_POWER_PIN=33 ; MosFET Transitor
; -DUSE_LED_SINGLE=1
; -DUSE_LED_STRIP=1
; Board ESP32-S3: RGB Pin 38 / 48 (Default 48)
-DLED_RGB=15
; -DLED_RGB=15
; -DUSE_DISPLAY_GRAPHIC=1
-DDISPLAY_TYPE=5 ; DisplayType_t::SSD1309
-DDISPLAY_RESET=12
-DDISPLAY_DATA=13
-DDISPLAY_CLK=14
-DDISPLAY_CS=-1
-DDISPLAY_BUSY=255
-DDISPLAY_DC=255
; -DDISPLAY_TYPE=5 ; DisplayType_t::SSD1309
; -DDISPLAY_RESET=12
; -DDISPLAY_DATA=13
; -DDISPLAY_CLK=14
; -DDISPLAY_CS=-1
; -DDISPLAY_BUSY=255
; -DDISPLAY_DC=255
; -DUSE_PROMETHEUS=1
; -DUSE_HASS=1
; -DUSE_ModbusDTU=1
Expand Down
21 changes: 20 additions & 1 deletion src/Battery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "MqttBattery.h"
#include "DalyBmsController.h"
#include "PytesCanReceiver.h"
#include "SBSCanReceiver.h"

BatteryClass Battery;

Expand Down Expand Up @@ -95,7 +96,25 @@ void BatteryClass::updateSettings()
}
break;
#endif

#ifdef USE_SBS_CAN_RECEIVER
case 2:
switch (ioProvider) {
#ifdef USE_SBS_RS485_RECEIVER
case Battery_Provider_t::RS485:
break;
#endif
#ifdef USE_SBS_CAN_RECEIVER
case Battery_Provider_t::CAN0:
case Battery_Provider_t::MCP2515:
case Battery_Provider_t::I2C0:
case Battery_Provider_t::I2C1:
_upProvider = std::make_unique<SBSCanReceiver>();
break;
#endif
default:;
}
break;
#endif
#ifdef USE_JKBMS_CONTROLLER
case 3:
if (ioProvider == Battery_Provider_t::RS232 || ioProvider == Battery_Provider_t::RS485)
Expand Down
7 changes: 6 additions & 1 deletion src/BatteryCanReceiver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

bool BatteryCanReceiver::init(char const* providerName)
{
snprintf(_providerName, 31, "[%s %s]", providerName, PinMapping.get().battery.providerName);
snprintf(_providerName, sizeof(_providerName), "[%s %s]", providerName, PinMapping.get().battery.providerName);

MessageOutput.printf("%s Initialize interface...", _providerName);

Expand Down Expand Up @@ -339,6 +339,11 @@ int16_t BatteryCanReceiver::readSignedInt16(uint8_t *data)
return this->readUnsignedInt16(data);
}

int32_t BatteryCanReceiver::readSignedInt24(uint8_t *data)
{
return (data[2] << 16) | (data[1] << 8) | data[0];
}

uint32_t BatteryCanReceiver::readUnsignedInt32(uint8_t *data)
{
return (data[3] << 24) | (data[2] << 16) | (data[1] << 8) | data[0];
Expand Down
71 changes: 71 additions & 0 deletions src/BatteryStats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,56 @@ void PytesBatteryStats::getLiveViewData(JsonVariant& root) const
}
#endif

#ifdef USE_SBS_CAN_RECEIVER
void SBSBatteryStats::generatePackCommonJsonResponse(JsonObject& packObject, const uint8_t module) const
{
packObject["moduleNumber"] = 1;
packObject["moduleName"] = "";

packObject["device_name"] = "";

packObject["software_version"] = "";

packObject["moduleSerialNumber"] = _serial;
/*
addLiveViewPackCellBalance(packObject, "cellMinVoltage", static_cast<float>(_cellMinMilliVolt)/1000, "V", 3);
addLiveViewPackCellBalance(packObject, "cellMaxVoltage", static_cast<float>(_cellMaxMilliVolt)/1000, "V", 3);
addLiveViewPackCellBalance(packObject, "cellDiffVoltage", (_cellMaxMilliVolt - _cellMinMilliVolt), "mV", 0);
addLiveViewPackValue(packObject, "cellMinTemperature", _cellMinTemperature, "V", 3);
addLiveViewPackValue(packObject, "cellMaxTemperature", _cellMaxTemperature, "V", 3);
addLiveViewPackText(packObject, "cellMinVoltageName", _cellMinVoltageName.c_str(), false);
addLiveViewPackText(packObject, "cellMaxVoltageName", _cellMaxVoltageName.c_str(), false);
addLiveViewPackText(packObject, "cellMinTemperatureName", _cellMinTemperatureName.c_str(), false);
addLiveViewPackText(packObject, "cellMaxTemperatureName", _cellMaxTemperatureName.c_str(), false);
*/
}

void SBSBatteryStats::getLiveViewData(JsonVariant& root) const
{
BatteryStats::getLiveViewData(root);

// values go into the "Status" card of the web application
addLiveViewValue(root, "chargeVoltage", _chargeVoltage, "V", 1);
addLiveViewValue(root, "chargeCurrentLimit", _chargeCurrentLimit, "A", 1);
addLiveViewValue(root, "dischargeCurrentLimit", _dischargeCurrentLimit, "A", 1);
addLiveViewValue(root, "stateOfHealth", _stateOfHealth, "%", 0);
addLiveViewValue(root, "current", _current, "A", 1);
addLiveViewValue(root, "temperature", _temperature, "°C", 1);
addLiveViewText(root, "chargeEnabled", (_chargeEnabled?"yes":"no"));
addLiveViewText(root, "dischargeEnabled", (_dischargeEnabled?"yes":"no"));

// alarms and warnings go into the "Issues" card of the web application
ISSUE(Warning, highCurrentDischarge);
ISSUE(Warning, highCurrentCharge);
ISSUE(Alarm, underVoltage);
ISSUE(Alarm, overVoltage);
ISSUE(Alarm, underTemperature);
ISSUE(Alarm, overTemperature);
}
#endif

#ifdef USE_JKBMS_CONTROLLER
void JkBmsBatteryStats::generatePackCommonJsonResponse(JsonObject& packObject, const uint8_t module) const
{
Expand Down Expand Up @@ -1113,6 +1163,27 @@ void PytesBatteryStats::mqttPublish() /* const */
}
#endif

#ifdef USE_SBS_CAN_RECEIVER
void SBSBatteryStats::mqttPublish() // const
{
BatteryStats::mqttPublish();

MqttSettings.publish("battery/settings/chargeVoltage", String(_chargeVoltage));
MqttSettings.publish("battery/settings/chargeCurrentLimitation", String(_chargeCurrentLimit));
MqttSettings.publish("battery/settings/dischargeCurrentLimitation", String(_dischargeCurrentLimit));
MqttSettings.publish("battery/stateOfHealth", String(_stateOfHealth));
MqttSettings.publish("battery/current", String(_current));
MqttSettings.publish("battery/temperature", String(_temperature));
MqttSettings.publish("battery/alarm/underVoltage", String(Alarm.underVoltage));
MqttSettings.publish("battery/alarm/overVoltage", String(Alarm.overVoltage));
MqttSettings.publish("battery/alarm/bmsInternal", String(Alarm.bmsInternal));
MqttSettings.publish("battery/warning/highCurrentDischarge", String(Warning.highCurrentDischarge));
MqttSettings.publish("battery/warning/highCurrentCharge", String(Warning.highCurrentCharge));
MqttSettings.publish("battery/charging/chargeEnabled", String(_chargeEnabled));
MqttSettings.publish("battery/charging/dischargeEnabled", String(_dischargeEnabled));
}
#endif

#ifdef USE_JKBMS_CONTROLLER
void JkBmsBatteryStats::mqttPublish() /* const */
{
Expand Down
Loading

0 comments on commit 5178ae5

Please sign in to comment.