Skip to content

Commit

Permalink
Store SoC into flash and restore on power up
Browse files Browse the repository at this point in the history
  • Loading branch information
stuartpittaway committed Mar 7, 2024
1 parent 5ca8ddb commit 8cb7524
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 52 deletions.
8 changes: 7 additions & 1 deletion ESPController/include/CurrentMonitorINA229.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,12 @@ class CurrentMonitorINA229
uint16_t shunttempcoefficient,
bool TemperatureCompEnabled);

void GuessSOC();
void DefaultSOC();
void TakeReadings();

uint32_t raw_milliamphour_out() const{ return milliamphour_out; }
uint32_t raw_milliamphour_in() const{ return milliamphour_in; }

uint32_t calc_milliamphour_out() const{ return milliamphour_out - milliamphour_out_offset; }
uint32_t calc_milliamphour_in() const{ return milliamphour_in - milliamphour_in_offset; }
uint32_t calc_daily_milliamphour_out() const{ return daily_milliamphour_out; }
Expand Down Expand Up @@ -264,12 +267,15 @@ class CurrentMonitorINA229
return registers.R_DIAG_ALRT & ALL_ALERT_BITS;
}
void SetSOC(uint16_t value);
void SetSOCByMilliAmpCounter(uint32_t in,uint32_t out);

void ResetDailyAmpHourCounters() {
daily_milliamphour_out=0;
daily_milliamphour_in=0;
}

uint16_t raw_stateofcharge() const { return SOC; }

private:
uint16_t SOC = 0;
float voltage = 0;
Expand Down
1 change: 1 addition & 0 deletions ESPController/include/defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ struct diybms_eeprom_settings
char homeassist_apikey[24+1];

/// @brief State of health variables - total lifetime mAh output (discharge)
// Might need to watch overflow on the uint32 (max value 4,294,967,295mAh) = approx 15339 cycles of 280Ah battery
uint32_t soh_total_milliamphour_out;
/// @brief State of health variables - total lifetime mAh input (charge)
uint32_t soh_total_milliamphour_in;
Expand Down
3 changes: 3 additions & 0 deletions ESPController/include/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,7 @@ void writeSetting(nvs_handle_t handle, const char *key, int8_t value);
void writeSetting(nvs_handle_t handle, const char *key, const char *value);
void writeSettingBlob(nvs_handle_t handle, const char *key, const void *value, size_t length);

bool GetStateOfCharge(uint32_t *in,uint32_t *out);
void SaveStateOfCharge(uint32_t,uint32_t);

#endif
60 changes: 20 additions & 40 deletions ESPController/src/CurrentMonitorINA229.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,34 @@ void CurrentMonitorINA229::CalculateLSB()
// registers.R_SHUNT_CAL = ((uint32_t)registers.R_SHUNT_CAL * 985) / 1000;
}


void CurrentMonitorINA229::SetSOCByMilliAmpCounter(uint32_t in,uint32_t out) {
// Assume battery is fully charged
milliamphour_in = in;
// And we have consumed this much...
milliamphour_out = out;

// Zero out readings using the offsets
milliamphour_out_offset = milliamphour_out;
milliamphour_in_offset = milliamphour_in;

ESP_LOGI(TAG, "SetSOCByMilliAmpCounter mA in=%u, mA out=%u",milliamphour_in,milliamphour_out);
}

// Sets SOC by setting "fake" in/out amphour counts
// value=8212 = 82.12%
void CurrentMonitorINA229::SetSOC(uint16_t value)
{
// Assume battery is fully charged
milliamphour_in = 1000 * (uint32_t)registers.batterycapacity_amphour;
// And we have consumed this much...
milliamphour_out = (uint32_t)((1.0F - ((float)value / 10000.0F)) * milliamphour_in);
milliamphour_out = (uint32_t)((1.0F - ((float)value / 10000.0F)) * (float)milliamphour_in);

// Zero out readings using the offsets
milliamphour_out_offset = milliamphour_out;
milliamphour_in_offset = milliamphour_in;

ESP_LOGI(TAG, "SetSOC mA in=%u, mA out=%u",milliamphour_in,milliamphour_out);
}

uint8_t CurrentMonitorINA229::readRegisterValue(INA_REGISTER r) const
Expand Down Expand Up @@ -391,47 +407,11 @@ void CurrentMonitorINA229::CalculateAmpHourCounts()
}
}

// Guess the SoC % based on battery voltage - not accurate, just a guess!
void CurrentMonitorINA229::GuessSOC()
// Set SoC to default values
void CurrentMonitorINA229::DefaultSOC()
{
// Default SOC% at 60%
uint16_t soc = 6000;

// We apply a "guestimate" to SoC based on voltage - not really accurate, but somewhere to start
// only applicable to 24V/48V (16S) LIFEPO4 setups. These voltages should be the unloaded (no current flowing) voltage.
// Assumption that its LIFEPO4 cells we are using...
float v = BusVoltage();

if (v > 20 && v < 30)
{
// Scale up 24V battery to use the 48V scale
v = v * 2;
}

if (v > 40 && v < 60)
{
// 16S LIFEPO4...
if (v >= 40.0)
soc = 500;
if (v >= 48.0)
soc = 900;
if (v >= 50.0)
soc = 1400;
if (v >= 51.2)
soc = 1700;
if (v >= 51.6)
soc = 2000;
if (v >= 52.0)
soc = 3000;
if (v >= 52.4)
soc = 4000;
if (v >= 52.8)
soc = 7000;
if (v >= 53.2)
soc = 9000;
}

SetSOC(soc);
SetSOC(6000);

// Reset the daily counters
daily_milliamphour_in = 0;
Expand Down
31 changes: 25 additions & 6 deletions ESPController/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3704,7 +3704,7 @@ struct log_level_t
};

// Default log levels to use for various components.
const std::array<log_level_t, 22> log_levels =
const std::array<log_level_t, 23> log_levels =
{
log_level_t{.tag = "*", .level = ESP_LOG_DEBUG},
{.tag = "wifi", .level = ESP_LOG_WARN},
Expand All @@ -3725,6 +3725,7 @@ const std::array<log_level_t, 22> log_levels =
{.tag = "diybms-web", .level = ESP_LOG_INFO},
{.tag = "diybms-set", .level = ESP_LOG_INFO},
{.tag = "diybms-mqtt", .level = ESP_LOG_INFO},
{.tag = "diybms-ctrl", .level = ESP_LOG_INFO},
{.tag = "diybms-pylon", .level = ESP_LOG_INFO},
{.tag = "diybms-pyforce", .level = ESP_LOG_INFO},
{.tag = "curmon", .level = ESP_LOG_INFO}};
Expand Down Expand Up @@ -3916,10 +3917,19 @@ ESP32 Chip model = %u, Rev %u, Cores=%u, Features=%u)",
mysettings.currentMonitoring_shunttempcoefficient,
mysettings.currentMonitoring_tempcompenabled);

currentmon_internal.GuessSOC();
currentmon_internal.DefaultSOC();

uint32_t in;
uint32_t out;
if (GetStateOfCharge(&in,&out))
{
currentmon_internal.SetSOCByMilliAmpCounter(in,out);
}

currentmon_internal.TakeReadings();



CalculateStateOfHealth(&mysettings);
}
else
Expand Down Expand Up @@ -4061,13 +4071,15 @@ void ESPCoreDumpToJSON(JsonObject &doc)
ultoa(summary->ex_info.exc_vaddr, outputString, 16);
core["exc_vaddr"] = outputString;

auto exc_a = core["exc_a"].to<JsonArray>();;
auto exc_a = core["exc_a"].to<JsonArray>();
;
for (auto value : summary->ex_info.exc_a)
{
ultoa(value, outputString, 16);
exc_a.add(outputString);
}
auto epcx = core["epcx"].to<JsonArray>();;
auto epcx = core["epcx"].to<JsonArray>();
;
for (auto value : summary->ex_info.epcx)
{
ultoa(value, outputString, 16);
Expand Down Expand Up @@ -4227,7 +4239,14 @@ void loop()
heap.free_blocks,
heap.total_blocks);

// Report again in 30 seconds
heaptimer = currentMillis + 30000;
// Report again in 60 seconds
heaptimer = currentMillis + 60000;

//Once per minute, store the state of charge into flash, just in case the controller is rebooted and we can restore this value
//on power up.
if (mysettings.currentMonitoringEnabled && mysettings.currentMonitoringDevice == CurrentMonitorDevice::DIYBMS_CURRENT_MON_INTERNAL)
{
SaveStateOfCharge(currentmon_internal.raw_milliamphour_in(),currentmon_internal.raw_milliamphour_out());
}
}
}
75 changes: 70 additions & 5 deletions ESPController/src/settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@ static const char soh_total_milliamphour_in_NVSKEY[] = "soh_mah_in";
static const char soh_lifetime_battery_cycles_NVSKEY[] = "soh_batcycle";
static const char soh_discharge_depth_NVSKEY[] = "soh_disdepth";

static const char soc_milliamphour_out_NVSKEY[] = "soc_mah_out";
static const char soc_milliamphour_in_NVSKEY[] = "soc_mah_in";

#define MACRO_NVSWRITE(VARNAME) writeSetting(nvs_handle, VARNAME##_NVSKEY, settings->VARNAME);
#define MACRO_NVSWRITE_UINT8(VARNAME) writeSetting(nvs_handle, VARNAME##_NVSKEY, (uint8_t)settings->VARNAME);
#define MACRO_NVSWRITESTRING(VARNAME) writeSetting(nvs_handle, VARNAME##_NVSKEY, &settings->VARNAME[0]);
Expand Down Expand Up @@ -346,6 +349,64 @@ void writeSettingBlob(nvs_handle_t handle, const char *key, const void *value, s
ESP_ERROR_CHECK(nvs_set_blob(handle, key, value, length));
}

/// @brief Reads state of charge (milliamp hour counts) from flash
/// @param in pointer to milliamp in count
/// @param out pointer to milliamp out count
/// @return true if values are valid
bool GetStateOfCharge(uint32_t *in, uint32_t *out)
{
const char *partname = "diybms-ctrl";
ESP_LOGI(TAG, "Read state of charge from flash");

nvs_handle_t nvs_handle;
auto err = nvs_open(partname, NVS_READONLY, &nvs_handle);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Error (%s) opening NVS handle", esp_err_to_name(err));
}
else
{
// Open
auto ret1 = getSetting(nvs_handle, soc_milliamphour_in_NVSKEY, in);
auto ret2 = getSetting(nvs_handle, soc_milliamphour_out_NVSKEY, out);

nvs_close(nvs_handle);

if (ret1 && ret2)
{
return true;
}

ESP_LOGI(TAG, "SoC value doesn't exist in flash");
}
return false;
}

/// @brief Stores the milliamp current values to allow restore of state of charge on power up
/// @param in milliamp hours in
/// @param out milliamp hours out
void SaveStateOfCharge(uint32_t in, uint32_t out)
{
const char *partname = "diybms-ctrl";
ESP_LOGI(TAG, "Write SoC to flash in=%u out=%u", in, out);

nvs_handle_t nvs_handle;
esp_err_t err = nvs_open(partname, NVS_READWRITE, &nvs_handle);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Error %s opening NVS handle!", esp_err_to_name(err));
}
else
{
// Save settings
writeSetting(nvs_handle, soc_milliamphour_in_NVSKEY, in);
writeSetting(nvs_handle, soc_milliamphour_out_NVSKEY, out);

ESP_ERROR_CHECK(nvs_commit(nvs_handle));
nvs_close(nvs_handle);
}
}

void SaveConfiguration(const diybms_eeprom_settings *settings)
{
const char *partname = "diybms-ctrl";
Expand Down Expand Up @@ -1004,8 +1065,9 @@ void ValidateConfiguration(diybms_eeprom_settings *settings)
settings->stateofchargeresumevalue = settings->stateofchargeresumevalue;
}

if (settings->soh_discharge_depth==0 || settings->soh_discharge_depth>100) {
settings->soh_discharge_depth=80;
if (settings->soh_discharge_depth == 0 || settings->soh_discharge_depth > 100)
{
settings->soh_discharge_depth = 80;
}
}

Expand Down Expand Up @@ -1074,8 +1136,10 @@ void GenerateSettingsJSONDocument(JsonDocument &doc, diybms_eeprom_settings *set
influxdb[influxdb_loggingFreqSeconds_JSONKEY] = settings->influxdb_loggingFreqSeconds;

JsonObject outputs = root["outputs"].to<JsonObject>();
JsonArray d = outputs["default"].to<JsonArray>();;
JsonArray t = outputs["type"].to<JsonArray>();;
JsonArray d = outputs["default"].to<JsonArray>();
;
JsonArray t = outputs["type"].to<JsonArray>();
;
for (uint8_t i = 0; i < RELAY_TOTAL; i++)
{
d.add(settings->rulerelaydefault[i]);
Expand Down Expand Up @@ -1104,7 +1168,8 @@ void GenerateSettingsJSONDocument(JsonDocument &doc, diybms_eeprom_settings *set
state["value"] = settings->rulevalue[rr];
state["hysteresis"] = settings->rulehysteresis[rr];

JsonArray relaystate = state["state"].to<JsonArray>();;
JsonArray relaystate = state["state"].to<JsonArray>();
;
for (uint8_t rt = 0; rt < RELAY_TOTAL; rt++)
{
relaystate.add(settings->rulerelaystate[rr][rt]);
Expand Down

0 comments on commit 8cb7524

Please sign in to comment.