Skip to content

Commit

Permalink
Add hysteresis on detecting over voltage errors
Browse files Browse the repository at this point in the history
This allows time to reduce the charge current before shutting down
  • Loading branch information
bjpirt committed Nov 30, 2023
1 parent 6e85a3b commit 763c272
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 4 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ Which will produce `build/out/tesla-bms-emulator.uf2`. You can drag and drop thi

## To Do

- [ ] Dynamically scale back the charge current to avoid going over voltage
- [x] Add hysteresis on over voltage faults
- [x] Simulator for integration testing over socat virtual serial port
- [x] Cell balancing
- [x] Battery Management System controlling contactors
Expand Down Expand Up @@ -123,8 +125,7 @@ Which will produce `build/out/tesla-bms-emulator.uf2`. You can drag and drop thi
- [x] Handle communication faults
- [x] Re-work fault level settings
- [x] State of charge from current sensor
- [ ] MQTT Support
- [ ] Design hardware
- [ ] Support for contactors per string
- [x] MQTT Support
- [x] Design hardware
- [ ] HTTPS
- [ ] Basic Auth
15 changes: 14 additions & 1 deletion battery/battery_cell.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from __future__ import annotations

from hal import get_interval
from .constants import OVER_VOLTAGE, UNDER_VOLTAGE
from typing import TYPE_CHECKING, List
if TYPE_CHECKING:
Expand All @@ -13,10 +15,21 @@ def __init__(self, config: Config):
self.over_voltage_fault_override = False
self.under_voltage_fault_override = False
self._config = config
self._over_voltage_interval = get_interval()
self._over_voltage_fault = False

@property
def over_voltage_fault(self) -> bool:
return self.voltage >= self._config.high_voltage_fault_level or self.over_voltage_fault_override
if self.voltage >= self._config.high_voltage_fault_level or self.over_voltage_fault_override:
if not self._over_voltage_fault:
self._over_voltage_fault = True
self._over_voltage_interval.set(
self._config.over_voltage_hysteresis_time)
else:
return self._over_voltage_interval.ready
else:
self._over_voltage_fault = False
return False

@property
def under_voltage_fault(self) -> bool:
Expand Down
2 changes: 2 additions & 0 deletions bms/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ def __init__(self, file="config.json"):
self.charge_hysteresis_voltage: float = 0.1
# The hysteresis offset time (in seconds) for enabling and disabling charging and discharging
self.charge_hysteresis_time: float = 30.0
# The hysteresis time for over voltage faults in seconds
self.over_voltage_hysteresis_time: float = 10.0

self.read()

Expand Down
8 changes: 8 additions & 0 deletions test/battery/test_BatteryCell.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
from battery import BatteryCell
import unittest
from battery.constants import OVER_VOLTAGE, UNDER_VOLTAGE
from time import sleep

from bms import Config


class BatteryCellTestCase(unittest.TestCase):
def setUp(self):
c = Config("config.default.json")
c.over_voltage_hysteresis_time = 0.01
self.cell = BatteryCell(c)

def test_store_highest_voltage(self):
Expand All @@ -34,6 +36,8 @@ def test_store_lowest_voltage(self):

def test_over_voltage_fault(self):
self.cell.voltage = 4.2
self.assertFalse(self.cell.over_voltage_fault)
sleep(0.01)
self.assertTrue(self.cell.over_voltage_fault)

def test_over_voltage_alert(self):
Expand All @@ -54,6 +58,8 @@ def test_fault(self):
self.cell.voltage = 3.0
self.assertTrue(self.cell.fault)
self.cell.voltage = 5.0
self.assertFalse(self.cell.fault)
sleep(0.01)
self.assertTrue(self.cell.fault)

def test_faults(self):
Expand All @@ -62,6 +68,8 @@ def test_faults(self):
self.cell.voltage = 3.0
self.assertEqual(self.cell.faults, [UNDER_VOLTAGE])
self.cell.voltage = 5.0
self.assertEqual(self.cell.faults, [])
sleep(0.01)
self.assertEqual(self.cell.faults, [OVER_VOLTAGE])

def test_alerts(self):
Expand Down
4 changes: 4 additions & 0 deletions test/battery/test_BatteryModule.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
from battery.battery_module import BatteryModule
import unittest
from battery.constants import COMMS, OVER_TEMPERATURE, OVER_VOLTAGE, UNDER_TEMPERATURE, UNDER_VOLTAGE
from time import sleep

from bms import Config


class BatteryModuleTestCase(unittest.TestCase):
def setUp(self):
c = Config("config.default.json")
c.over_voltage_hysteresis_time = 0.01
self.module = BatteryModule(c)
for i in range(4):
cell = BatteryCell(c)
Expand Down Expand Up @@ -129,6 +131,8 @@ def test_over_voltage_faults(self):
self.module.cells[1].voltage = 4.2
self.module.cells[2].voltage = 4.2
self.module.cells[3].voltage = 4.2
self.assertEqual(self.module.faults, [])
sleep(0.01)
self.assertEqual(self.module.faults, [OVER_VOLTAGE])
self.assertTrue(self.module.fault)

Expand Down
4 changes: 4 additions & 0 deletions test/battery/test_BatteryPack.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
import unittest
from battery.constants import COMMS, OVER_VOLTAGE, OVER_TEMPERATURE, UNDER_VOLTAGE, UNDER_TEMPERATURE, BALANCE
from bms import Config
from time import sleep


class BatteryPackTestCase(unittest.TestCase):
def setUp(self):
self.config = Config("config.default.json")
self.config.parallel_string_count = 2
self.config.over_voltage_hysteresis_time = 0.01
self.pack = BatteryPack(self.config)
for m in range(4):
module = BatteryModule(self.config)
Expand Down Expand Up @@ -103,6 +105,8 @@ def test_over_voltage_faults(self):
self.pack.modules[0].cells[1].voltage = 4.15
self.pack.modules[0].cells[2].voltage = 4.15
self.pack.modules[0].cells[3].voltage = 4.15
self.assertEqual(self.pack.faults, [])
sleep(0.01)
self.assertEqual(self.pack.faults, [OVER_VOLTAGE])
self.assertTrue(self.pack.fault)

Expand Down

0 comments on commit 763c272

Please sign in to comment.