hitl_tester.test_cases.bms.test_2590_faults
Testing requirements on BB2590_v2 before live cells are used.
All of these faults should be read out through the flags. We should be able to read the runtime flags and ensure each bit corresponds to the proper pre fault, fault, and permanent fault.
Used in these test plans:
- 2590_assembly_b ⠀⠀⠀(bms/2590_assembly_b.plan)
- bb2590_a_firmware ⠀⠀⠀(bms/bb2590_a_firmware.plan)
- bb2590_b_firmware ⠀⠀⠀(bms/bb2590_b_firmware.plan)
Example Command (warning: test plan may run other test cases):
./hitl_tester.py 2590_assembly_b -DCELL_VOLTAGE=3.8002
1""" 2Testing requirements on BB2590_v2 before live cells are used. 3 4All of these faults should be read out through the flags. We should be able to read the runtime flags and ensure each 5bit corresponds to the proper pre fault, fault, and permanent fault. 6""" 7 8from __future__ import annotations 9 10import time 11 12import pytest 13 14from hitl_tester.modules.bms import smbus_types 15from hitl_tester.modules.bms.bms_hw import BMSHardware 16from hitl_tester.modules.bms.cell import Cell 17from hitl_tester.modules.bms.event_watcher import SerialWatcher 18from hitl_tester.modules.bms.plateset import Plateset 19from hitl_tester.modules.bms.smbus import SMBus 20from hitl_tester.modules.bms.smbus_types import SMBusReg 21from hitl_tester.modules.logger import logger 22 23CELL_VOLTAGE = 3.8002 24 25bms_hardware = BMSHardware(pytest.flags) # type: ignore[arg-type] 26bms_hardware.init() 27 28serial_watcher = SerialWatcher() 29plateset = Plateset() 30smbus = SMBus() 31 32 33def test_reset_cell_sims(): 34 """Activate cell sims and set appropriate temperatures.""" 35 logger.write_info_to_report("Powering down cell sims") 36 for cell in bms_hardware.cells.values(): 37 cell.disengage_safety_protocols = True 38 cell.volts = 0.0001 39 time.sleep(5) 40 logger.write_info_to_report("Powering up cell sims") 41 for cell in bms_hardware.cells.values(): 42 cell.volts = CELL_VOLTAGE 43 cell.disengage_safety_protocols = False 44 for cell in bms_hardware.cells.values(): 45 cell.exact_volts = CELL_VOLTAGE 46 logger.write_info_to_report("Setting temperature to 15°C") 47 plateset.thermistor1 = 15 48 plateset.thermistor2 = 15 49 logger.write_info_to_report("Enabling faults") 50 time.sleep(1) 51 smbus.write_register(SMBusReg.MANUFACTURING_ACCESS, smbus_types.BMSCommands.FAULT_ENABLE) # Enable faults 52 53 54def test_wakeup_1(): 55 """ 56 The BMS should be in a state of slumber if the current measured is between -46mA and 19ma. This is done using 57 internal comparators on the board to a logical AND chip feeding into an interrupt pin. To test this, 3 different 58 currents should be set. One current below -46ma, another current between -46mA and 19ma, and another current above 59 19ma. If the current is within the allowable range, we should read logic 1 on the N_WAKEUP pin. If the current is 60 outside (above or below) we should read logic 0 on the pin. 61 62 # Test 63 - Default state at 0 mA (between -46mA and 19ma) 64 - n_wakeup_gpio = 1 65 - Discharging at 100 mA (below -46 mA) 66 - n_wakeup_gpio = 0 67 - Discharging at 10 mA (between -46mA and 19ma) 68 - n_wakeup_gpio = 1 69 - Charging at 100 mA (above 19 mA) 70 - n_wakeup_gpio = 0 71 """ 72 logger.write_info_to_report("Testing Wakeup") 73 test_reset_cell_sims() 74 serial_watcher.start() 75 76 logger.write_info_to_report("Default state") 77 serial_watcher.assert_true("n_wakeup_gpio", True, 1) 78 79 logger.write_info_to_report("Discharging 100 mA") 80 bms_hardware.load.mode_cc() 81 bms_hardware.load.amps = 0.100 82 bms_hardware.load.enable() 83 serial_watcher.assert_true("n_wakeup_gpio", False, 2) 84 85 logger.write_info_to_report("Discharging 10 mA") 86 bms_hardware.load.amps = 0.010 87 serial_watcher.assert_true("n_wakeup_gpio", True, 3) 88 bms_hardware.load.disable() 89 90 logger.write_info_to_report("Charging 100 mA") 91 bms_hardware.charger.set_profile(16.8, 0.100) 92 plateset.ce_switch = True 93 bms_hardware.charger.enable() 94 serial_watcher.assert_true("n_wakeup_gpio", False, 4) 95 bms_hardware.charger.disable() 96 plateset.ce_switch = False 97 98 serial_watcher.stop() 99 100 101def test_oc_oneshot(): 102 """ 103 OC Oneshot: This is an interrupt that will trigger if we sink a ton of current into the battery, this cannot be 104 tested with just software and the comparators used for this have not been tested for hardware yet. 105 106 The bms should have an interrupt be fired when the current reaches the max amount (I think ~63 amps). this will be 107 a test for Tyler to do on his own 108 109 NOTE: High current. Only run this on a modified board. 110 """ 111 112 113def test_overtemp_faults(): 114 """ 115 If our batteries get too hot, we must trigger a fault. This is common during high discharge or charging cycles. 116 There are 3 different environments where we would trigger an overtemp fault: charge, discharge and resting. While 117 charging, if we are above 45C we must trigger a fault If we are resting or discharging at 59 degrees we must 118 trigger a fault. Both of these faults should trigger a prefault condition in our flags. After we cycle again we 119 should then trigger a full fault. If the temperature ever goes above 93 degrees, the fault should never clear and 120 we should be in permanent fault and trigger the fets. (This should also be seen in the flags) 121 122 # Test Overview (reset inbetween tests) 123 - Default state at 15°C 124 - prefault_overtemp_discharge = 0 125 - fault_overtemp_discharge = 0 126 - prefault_overtemp_charge = 0 127 - fault_overtemp_charge = 0 128 - permanentdisable_overtemp = 0 129 - measure_output_fets_disabled = 0 130 131 - Resting at 59°C (at or above 59°C) 132 - prefault_overtemp_discharge = 1 133 - fault_overtemp_discharge = 1 134 135 - Discharging at 100 mA, 59°C (at or above 59°C) 136 - prefault_overtemp_discharge = 1 137 - fault_overtemp_discharge = 1 138 139 - Charging at 100 mA, 46°C (at or above 45°C) 140 - prefault_overtemp_charge = 1 141 - fault_overtemp_charge = 1permanentdisable_overtemp 142 143 - Charging at 100 mA, 94°C (at or above 93°C) 144 - permanentdisable_overtemp = 1 145 146 - Discharging at 100 mA, 94°C (at or above 93°C) 147 - permanentdisable_overtemp = 1 148 149 - Resting at 94°C (at or above 93°C) 150 - permanentdisable_overtemp = 1 151 """ 152 logger.write_info_to_report("Testing Overtemp") 153 test_reset_cell_sims() 154 serial_watcher.start() 155 156 # Test default state 157 logger.write_info_to_report(f"Resting at 0 mA, {CELL_VOLTAGE} V, 15°C") 158 plateset.thermistor1 = 15 159 serial_watcher.assert_true("flags.prefault_overtemp_discharge", False, 1) 160 serial_watcher.assert_true("flags.fault_overtemp_discharge", False, 1) 161 serial_watcher.assert_true("flags.prefault_overtemp_charge", False, 1) 162 serial_watcher.assert_true("flags.fault_overtemp_charge", False, 1) 163 serial_watcher.assert_true("flags.permanentdisable_overtemp", False, 1) 164 serial_watcher.assert_true("flags.measure_output_fets_disabled", False, 1) 165 166 # Test resting overtemp 167 logger.write_info_to_report("Resting at 59°C") 168 plateset.thermistor1 = 59 169 serial_watcher.assert_true("flags.prefault_overtemp_discharge", True, 2) 170 serial_watcher.assert_true("flags.fault_overtemp_discharge", True, 2) 171 logger.write_info_to_report("Resting at 15°C") 172 plateset.thermistor1 = 15 173 serial_watcher.assert_true("flags.prefault_overtemp_discharge", False, 3) 174 serial_watcher.assert_true("flags.fault_overtemp_discharge", False, 3) 175 176 # Test discharging overtemp 177 logger.write_info_to_report("Discharging at -100 mA, 59°C") 178 bms_hardware.load.mode_cc() 179 bms_hardware.load.amps = 0.100 180 bms_hardware.load.enable() 181 plateset.thermistor1 = 59 182 serial_watcher.assert_true("flags.prefault_overtemp_discharge", True, 4) 183 serial_watcher.assert_true("flags.fault_overtemp_discharge", True, 4) 184 logger.write_info_to_report("Discharging at -100 mA, 15°C") 185 plateset.thermistor1 = 15 186 serial_watcher.assert_true("flags.prefault_overtemp_discharge", False, 5) 187 serial_watcher.assert_true("flags.fault_overtemp_discharge", False, 5) 188 bms_hardware.load.disable() 189 190 # Test charging overtemp 191 logger.write_info_to_report("Charging at 100 mA, 65°C") 192 bms_hardware.charger.set_profile(16.8, 0.1) 193 plateset.ce_switch = True 194 bms_hardware.charger.enable() 195 plateset.thermistor1 = 65 196 serial_watcher.assert_true("flags.prefault_overtemp_charge", True, 2) 197 serial_watcher.assert_true("flags.fault_overtemp_charge", True, 2) 198 logger.write_info_to_report("Charging at 100 mA, 45°C") 199 plateset.thermistor1 = 45 200 serial_watcher.assert_true("flags.prefault_overtemp_charge", False, 3) 201 serial_watcher.assert_true("flags.fault_overtemp_charge", False, 3) 202 bms_hardware.charger.disable() 203 plateset.ce_switch = False 204 205 # Test charging permanent disable 206 logger.write_info_to_report("Charging at 100 mA, 94°C") 207 plateset.disengage_safety_protocols = True 208 plateset.thermistor1 = 94 209 plateset.disengage_safety_protocols = False 210 bms_hardware.charger.set_profile(16.8, 0.1) 211 plateset.ce_switch = True 212 bms_hardware.charger.enable() 213 214 serial_watcher.assert_true("flags.permanentdisable_overtemp", True, 2) 215 216 logger.write_info_to_report("Charging at 100 mA, 15°C") 217 plateset.thermistor1 = 15 218 serial_watcher.assert_false("flags.permanentdisable_overtemp", False, 3) 219 220 bms_hardware.charger.disable() 221 plateset.ce_switch = False 222 test_reset_cell_sims() 223 224 # Test discharging permanent disable 225 logger.write_info_to_report("Discharging at -100 mA, 94°C") 226 plateset.disengage_safety_protocols = True 227 plateset.thermistor1 = 94 228 plateset.disengage_safety_protocols = False 229 bms_hardware.load.mode_cc() 230 bms_hardware.load.amps = 0.100 231 bms_hardware.load.enable() 232 233 serial_watcher.assert_true("flags.permanentdisable_overtemp", True, 4) 234 235 logger.write_info_to_report("Discharging at -100 mA, 15°C") 236 plateset.thermistor1 = 15 237 serial_watcher.assert_false("flags.permanentdisable_overtemp", False, 5) 238 239 bms_hardware.load.disable() 240 test_reset_cell_sims() 241 242 # Test resting permanent disable 243 plateset.disengage_safety_protocols = True 244 logger.write_info_to_report("Resting at 94°C") 245 plateset.thermistor1 = 94 246 plateset.disengage_safety_protocols = False 247 serial_watcher.assert_true("flags.permanentdisable_overtemp", True, 6) 248 249 logger.write_info_to_report("Resting at 15°C") 250 plateset.thermistor1 = 15 251 serial_watcher.assert_false("flags.permanentdisable_overtemp", False, 7) 252 253 serial_watcher.stop() 254 255 256def test_undertemp_faults(): 257 """ 258 This occurs when we read more than 20 mamps from the battery, if any of the cells are under 0 degrees Celsius this 259 will trigger a fault. This will be cleared if we go above -2C. Regardless of current being measured, if we ever 260 read below -20C, this should trigger a fault. This fault should not be cleared until we are above -18C 261 262 *** THIS FAULT NEEDS TO BE INVESTIGATED 263 https://github.com/orgs/turnaroundfactor/projects/24?pane=issue&itemId=50150194 264 """ 265 logger.write_info_to_report("Testing Undertemp") 266 test_reset_cell_sims() 267 serial_watcher.start() 268 269 logger.write_info_to_report(f"Resting at 0 mA, {CELL_VOLTAGE} V, 15°C") 270 plateset.thermistor1 = 15 271 272 # Discharging flags 273 serial_watcher.assert_true("flags.prefault_undertemp_discharge", False, 1) 274 serial_watcher.assert_true("flags.fault_undertemp_discharge", False, 1) 275 276 # Charging flags 277 serial_watcher.assert_true("flags.prefault_undertemp_charge", False, 1) 278 serial_watcher.assert_true("flags.fault_undertemp_charge", False, 1) 279 280 logger.write_info_to_report("Resting at -21°C") 281 plateset.thermistor1 = -21 282 serial_watcher.assert_true("flags.prefault_undertemp_discharge", True, 2) 283 serial_watcher.assert_true("flags.fault_undertemp_discharge", True, 2) 284 logger.write_info_to_report("Resting at -17°C") 285 plateset.thermistor1 = -17 286 serial_watcher.assert_true("flags.prefault_undertemp_discharge", False, 3) 287 serial_watcher.assert_true("flags.fault_undertemp_discharge", False, 3) 288 289 logger.write_info_to_report("Discharging at -100 mA, -21°C") 290 bms_hardware.load.mode_cc() 291 bms_hardware.load.amps = 0.100 292 bms_hardware.load.enable() 293 plateset.thermistor1 = -21 294 serial_watcher.assert_true("flags.prefault_undertemp_discharge", True, 4) 295 serial_watcher.assert_true("flags.fault_undertemp_discharge", True, 4) 296 logger.write_info_to_report("Discharging at -100 mA, -17°C") 297 plateset.thermistor1 = -17 298 serial_watcher.assert_true("flags.prefault_undertemp_discharge", False, 5) 299 serial_watcher.assert_true("flags.fault_undertemp_discharge", False, 5) 300 bms_hardware.load.disable() 301 302 logger.write_info_to_report("Charging at 100 mA, -25°C") 303 bms_hardware.charger.set_profile(16.8, 0.1) 304 plateset.ce_switch = True 305 bms_hardware.charger.enable() 306 plateset.thermistor1 = -25 307 serial_watcher.assert_true("flags.prefault_undertemp_charge", True, 2) 308 serial_watcher.assert_true("flags.fault_undertemp_charge", True, 2) 309 logger.write_info_to_report("Charging at 100 mA, -15°C") 310 plateset.thermistor1 = -15 311 serial_watcher.assert_true("flags.prefault_undertemp_charge", False, 3) 312 serial_watcher.assert_true("flags.fault_undertemp_charge", False, 3) 313 bms_hardware.charger.disable() 314 plateset.ce_switch = False 315 316 serial_watcher.stop() 317 318 319def set_exact_volts(cell: Cell, voltage: float, compensation: float = 0.08): 320 """What the BMS reads won't exactly match the set voltage, thus we need slight adjustments.""" 321 cell.exact_volts = voltage + compensation 322 logger.write_debug_to_report(f"Cell is {cell.volts}V") 323 324 325def test_overvoltage_faults(): 326 """ 327 While charging, we need to monitor the voltage of our cells. Specifically, if a cell ever goes above 4.205 Volts, 328 we should trigger a prefault. If this prefault exsists for more than 3 seconds, we then should trigger a full 329 fault. If a cell ever gets to be above 4.250 volts, we should trigger a permanent fault. If we go under 4.201 we 330 should be able to clear the fault 331 """ 332 logger.write_info_to_report("Testing Overvoltage") 333 test_reset_cell_sims() 334 test_cell = bms_hardware.cells[1] 335 serial_watcher.start() 336 test_cell.disengage_safety_protocols = True 337 338 logger.write_info_to_report(f"Resting at 0 mA, {CELL_VOLTAGE} V") 339 serial_watcher.assert_true("flags.prefault_overvoltage_charge", False, 1) 340 serial_watcher.assert_true("flags.fault_overvoltage_charge", False, 1) 341 serial_watcher.assert_true("flags.permanentdisable_overvoltage", False, 1) 342 343 bms_hardware.charger.set_profile(16.8, 0.1) 344 plateset.ce_switch = True 345 bms_hardware.charger.enable() 346 logger.write_info_to_report("Charging at 100 mA, 4.21 V") 347 set_exact_volts(test_cell, 4.21) 348 serial_watcher.assert_true("flags.prefault_overvoltage_charge", True, 2) 349 serial_watcher.assert_true("flags.fault_overvoltage_charge", True, 2) 350 351 logger.write_info_to_report("Charging at 100 mA, 4.10 V") 352 set_exact_volts(test_cell, 4.10) 353 serial_watcher.assert_true("flags.prefault_overvoltage_charge", False, 3) 354 serial_watcher.assert_true("flags.fault_overvoltage_charge", False, 3) 355 356 logger.write_info_to_report("Charging at 100 mA, 4.26 V") 357 set_exact_volts(test_cell, 4.26) 358 serial_watcher.assert_true("flags.permanentdisable_overvoltage", True, 2) 359 360 logger.write_info_to_report("Charging at 100 mA, 4.10 V") 361 set_exact_volts(test_cell, 4.10) 362 serial_watcher.assert_false("flags.permanentdisable_overvoltage", False, 3) 363 364 bms_hardware.charger.disable() 365 plateset.ce_switch = False 366 367 serial_watcher.stop() 368 369 370def test_undervoltage_faults(): 371 """ 372 This has also been validated in software, meaning the logic should properly handle a situation with a cell 373 discharging too low, however this has not yet been tested in hardware with a cell sensor reading that low of a 374 voltage and triggering a fault. 375 376 If we are reading less than 20mamps from the cells, we should be able to trigger an under-voltage fault. If we 377 read less than 2.4 volts, we must trigger a fault. If this fault persists for over 1 second, we should then trigger 378 a full fault. We will not clear this fault unless we are able to read above 2.5 volts. 379 If we are reading over 400mamps and a cell reads less than 2.325 volts, we must trigger a cell voltage charge min 380 prefault, if this persists for another bms software cycle we will trigger a full fault. This fault will clear when 381 we read above this voltage. If the cell voltage ever goes under 2.3 while charging, we must trigger a permanent 382 fault. 383 """ 384 logger.write_info_to_report("Testing Undervoltage") 385 test_reset_cell_sims() 386 test_reset_cell_sims() 387 test_cell = bms_hardware.cells[1] 388 for cell in bms_hardware.cells.values(): 389 cell.disengage_safety_protocols = True 390 set_exact_volts(cell, 3.0, 0.05) # Must be low enough to not trigger cell imbalance [abs(high - low) > 1.5V] 391 serial_watcher.start() 392 393 logger.write_info_to_report("Resting at 0 mA, 3.0 V") 394 serial_watcher.assert_true("flags.prefault_undervoltage_discharge", False, 1) 395 serial_watcher.assert_true("flags.faultslumber_undervoltage_discharge", False, 1) 396 serial_watcher.assert_true("flags.prefault_undervoltage_charge", False, 1) 397 serial_watcher.assert_true("flags.fault_undervoltage_charge", False, 1) 398 serial_watcher.assert_true("flags.permanentdisable_undervoltage", False, 1) 399 400 logger.write_info_to_report("Resting at 0 mA, 2.3 V") 401 set_exact_volts(test_cell, 2.3, 0.05) 402 serial_watcher.assert_true("flags.prefault_undervoltage_discharge", True, 2, wait_time=600) 403 serial_watcher.assert_true("flags.faultslumber_undervoltage_discharge", True, 2, wait_time=600) 404 405 logger.write_info_to_report("Resting at 0 mA, 2.6 V") 406 set_exact_volts(test_cell, 2.6, 0.05) 407 serial_watcher.assert_true("flags.prefault_undervoltage_discharge", False, 3, wait_time=600) 408 serial_watcher.assert_true("flags.faultslumber_undervoltage_discharge", False, 3, wait_time=600) 409 410 bms_hardware.charger.set_profile(16.8, 0.500) 411 plateset.ce_switch = True 412 bms_hardware.charger.enable() 413 logger.write_info_to_report("Charging at 500 mA, 2.3 V") 414 set_exact_volts(test_cell, 2.3, 0.01) 415 serial_watcher.assert_true("flags.prefault_undervoltage_charge", True, 2, wait_time=600) 416 serial_watcher.assert_true("flags.fault_undervoltage_charge", True, 2, wait_time=600) 417 418 logger.write_info_to_report("Charging at 500 mA, 2.4 V") 419 set_exact_volts(test_cell, 2.4, 0.05) 420 serial_watcher.assert_true("flags.prefault_undervoltage_charge", False, 3) 421 serial_watcher.assert_true("flags.fault_undervoltage_charge", False, 3) 422 423 logger.write_info_to_report("Charging at 500 mA, 2.2 V") 424 set_exact_volts(test_cell, 2.2, 0.05) 425 serial_watcher.assert_true("flags.permanentdisable_undervoltage", True, 2) 426 427 logger.write_info_to_report("Charging at 500 mA, 2.6 V") 428 set_exact_volts(test_cell, 2.6, 0.05) 429 serial_watcher.assert_false("flags.permanentdisable_undervoltage", False, 3) 430 431 bms_hardware.charger.disable() 432 plateset.ce_switch = False 433 434 serial_watcher.stop() 435 436 437def test_cell_imbalance(): 438 """Occurs when the difference between the highest and lowest cell is 1.5V.""" 439 logger.write_info_to_report("Testing Cell Imbalance") 440 test_reset_cell_sims() 441 test_cell = bms_hardware.cells[1] 442 serial_watcher.start() 443 test_cell.disengage_safety_protocols = True 444 445 logger.write_info_to_report(f"Resting at 0 mA, {CELL_VOLTAGE} V") 446 serial_watcher.assert_true("flags.prefault_cellimbalance", False, 1) 447 serial_watcher.assert_true("flags.permanentdisable_cellimbalance", False, 1) 448 449 logger.write_info_to_report("Resting at 0 mA, 2.0 V") 450 set_exact_volts(test_cell, 2.0) 451 serial_watcher.assert_true("flags.prefault_cellimbalance", True, 2) 452 serial_watcher.assert_true("flags.permanentdisable_cellimbalance", True, 2) 453 454 logger.write_info_to_report(f"Resting at 0 mA, {CELL_VOLTAGE} V") 455 set_exact_volts(test_cell, CELL_VOLTAGE) 456 serial_watcher.assert_false("flags.permanentdisable_cellimbalance", False, 3) 457 458 serial_watcher.stop() 459 460 461def test_overvoltage_overtemp_faults(): 462 """A combo test where we have a high temperature and high voltage.""" 463 logger.write_info_to_report("Testing Overvoltage with Overtemp") 464 test_reset_cell_sims() 465 test_cell = bms_hardware.cells[1] 466 serial_watcher.start() 467 test_cell.disengage_safety_protocols = True 468 469 logger.write_info_to_report(f"Resting at 0 mA, {CELL_VOLTAGE} V, 15°C") 470 serial_watcher.assert_true("flags.prefault_overvoltage_charge", False, 1) 471 serial_watcher.assert_true("flags.fault_overvoltage_charge", False, 1) 472 serial_watcher.assert_true("flags.permanentdisable_overvoltage", False, 1) 473 474 serial_watcher.assert_true("flags.prefault_overtemp_charge", False, 1) 475 serial_watcher.assert_true("flags.fault_overtemp_charge", False, 1) 476 serial_watcher.assert_true("flags.permanentdisable_overtemp", False, 1) 477 478 bms_hardware.charger.set_profile(16.8, 0.1) 479 plateset.ce_switch = True 480 bms_hardware.charger.enable() 481 logger.write_info_to_report("Charging at 100 mA, 4.21 V, 65°C") 482 set_exact_volts(test_cell, 4.21) 483 plateset.thermistor1 = 65 484 serial_watcher.assert_true("flags.prefault_overvoltage_charge", True, 2) 485 serial_watcher.assert_true("flags.fault_overvoltage_charge", True, 2) 486 serial_watcher.assert_true("flags.prefault_overtemp_charge", True, 2) 487 serial_watcher.assert_true("flags.fault_overtemp_charge", True, 2) 488 489 logger.write_info_to_report("Charging at 100 mA, 4.10 V, 45°C") 490 set_exact_volts(test_cell, 4.10) 491 plateset.thermistor1 = 45 492 serial_watcher.assert_true("flags.prefault_overvoltage_charge", False, 3) 493 serial_watcher.assert_true("flags.fault_overvoltage_charge", False, 3) 494 serial_watcher.assert_true("flags.prefault_overtemp_charge", False, 3) 495 serial_watcher.assert_true("flags.fault_overtemp_charge", False, 3) 496 497 logger.write_info_to_report("Charging at 100 mA, 4.26 V, 94°C") 498 set_exact_volts(test_cell, 4.26) 499 plateset.disengage_safety_protocols = True 500 plateset.thermistor1 = 94 501 plateset.disengage_safety_protocols = False 502 serial_watcher.assert_true("flags.permanentdisable_overvoltage", True, 2) 503 serial_watcher.assert_true("flags.permanentdisable_overtemp", True, 2) 504 505 logger.write_info_to_report("Charging at 100 mA, 4.10 V, 15°C") 506 set_exact_volts(test_cell, 4.10) 507 plateset.thermistor1 = 15 508 serial_watcher.assert_false("flags.permanentdisable_overvoltage", False, 3) 509 serial_watcher.assert_false("flags.permanentdisable_overtemp", False, 3) 510 511 bms_hardware.charger.disable() 512 plateset.ce_switch = False 513 514 serial_watcher.stop() 515 516 517def test_undervoltage_undertemp_faults(): 518 """A combo test where we have a low temperature and low voltage.""" 519 logger.write_info_to_report("Testing Undervoltage with Undertemp") 520 test_reset_cell_sims() 521 test_cell = bms_hardware.cells[1] 522 for cell in bms_hardware.cells.values(): 523 cell.disengage_safety_protocols = True 524 set_exact_volts(cell, 3.0) # Must be low enough to not trigger cell imbalance [abs(high - low) > 1.5V] 525 serial_watcher.start() 526 527 logger.write_info_to_report("Resting at 0 mA, 3.0 V, 15°C") 528 plateset.thermistor1 = 15 529 serial_watcher.assert_true("flags.prefault_undervoltage_discharge", False, 1) 530 serial_watcher.assert_true("flags.faultslumber_undervoltage_discharge", False, 1) 531 serial_watcher.assert_true("flags.prefault_undervoltage_charge", False, 1) 532 serial_watcher.assert_true("flags.fault_undervoltage_charge", False, 1) 533 serial_watcher.assert_true("flags.permanentdisable_undervoltage", False, 1) 534 535 serial_watcher.assert_true("flags.prefault_undertemp_discharge", False, 1) 536 serial_watcher.assert_true("flags.fault_undertemp_discharge", False, 1) 537 serial_watcher.assert_true("flags.prefault_undertemp_charge", False, 1) 538 serial_watcher.assert_true("flags.fault_undertemp_charge", False, 1) 539 540 logger.write_info_to_report("Resting at 0 mA, 2.3 V, -21°C") 541 set_exact_volts(test_cell, 2.3) 542 plateset.thermistor1 = -21 543 serial_watcher.assert_true("flags.prefault_undervoltage_discharge", True, 2) 544 serial_watcher.assert_true("flags.faultslumber_undervoltage_discharge", True, 2) 545 serial_watcher.assert_true("flags.prefault_undertemp_discharge", True, 2) 546 serial_watcher.assert_true("flags.fault_undertemp_discharge", True, 2) 547 548 logger.write_info_to_report("Resting at 0 mA, 2.6 V, -17°C") 549 set_exact_volts(test_cell, 2.6) 550 plateset.thermistor1 = -17 551 serial_watcher.assert_true("flags.prefault_undervoltage_discharge", False, 3) 552 serial_watcher.assert_true("flags.faultslumber_undervoltage_discharge", False, 3) 553 serial_watcher.assert_true("flags.prefault_undertemp_discharge", False, 3) 554 serial_watcher.assert_true("flags.fault_undertemp_discharge", False, 3) 555 556 bms_hardware.charger.set_profile(16.8, 0.500) 557 plateset.ce_switch = True 558 bms_hardware.charger.enable() 559 logger.write_info_to_report("Charging at 500 mA, 2.3 V, -25°C") 560 set_exact_volts(test_cell, 2.3, 0.01) 561 plateset.thermistor1 = -25 562 serial_watcher.assert_true("flags.prefault_undervoltage_charge", True, 2) 563 serial_watcher.assert_true("flags.fault_undervoltage_charge", True, 2) 564 serial_watcher.assert_true("flags.prefault_undertemp_charge", True, 2) 565 serial_watcher.assert_true("flags.fault_undertemp_charge", True, 2) 566 567 logger.write_info_to_report("Charging at 500 mA, 2.4 V, -15°C") 568 set_exact_volts(test_cell, 2.4) 569 plateset.thermistor1 = -15 570 serial_watcher.assert_true("flags.prefault_undervoltage_charge", False, 3) 571 serial_watcher.assert_true("flags.fault_undervoltage_charge", False, 3) 572 serial_watcher.assert_true("flags.prefault_undertemp_charge", False, 3) 573 serial_watcher.assert_true("flags.fault_undertemp_charge", False, 3) 574 575 logger.write_info_to_report("Charging at 500 mA, 2.2 V, 15°C") 576 set_exact_volts(test_cell, 2.2) 577 plateset.thermistor1 = 15 578 serial_watcher.assert_true("flags.permanentdisable_undervoltage", True, 2) 579 580 logger.write_info_to_report("Charging at 500 mA, 2.6 V, 15°C") 581 set_exact_volts(test_cell, 2.6) 582 plateset.thermistor1 = 15 583 serial_watcher.assert_false("flags.permanentdisable_undervoltage", False, 3) 584 585 bms_hardware.charger.disable() 586 plateset.ce_switch = False 587 588 serial_watcher.stop() 589 590 591def test_cell_imbalance_charge(): 592 """Occurs when the difference between the highest and lowest cell is 1.5V.""" 593 logger.write_info_to_report("Testing Cell Imbalance Charge") 594 test_reset_cell_sims() 595 test_cell = bms_hardware.cells[1] 596 serial_watcher.start() 597 test_cell.disengage_safety_protocols = True 598 for cell in bms_hardware.cells.values(): 599 cell.exact_volts = 4.2 600 601 voltages = [f"{cell.measured_volts} V" for cell in bms_hardware.cells.values()] 602 logger.write_info_to_report(f"Resting at 0 mA, {', '.join(voltages)}") 603 serial_watcher.assert_true("flags.prefault_cellimbalance", False, 1) 604 serial_watcher.assert_true("flags.permanentdisable_cellimbalance", False, 1) 605 606 bms_hardware.charger.set_profile(16.8, 1) 607 plateset.ce_switch = True 608 bms_hardware.charger.enable() 609 set_exact_volts(test_cell, 2.5) 610 voltages = [f"{cell.measured_volts} V" for cell in bms_hardware.cells.values()] 611 logger.write_info_to_report(f"Charging at 1 A, {', '.join(voltages)}") 612 serial_watcher.assert_false("flags.prefault_cellimbalance", True) 613 serial_watcher.assert_false("flags.permanentdisable_cellimbalance", True) 614 615 test_cell.exact_volts = 4.2 616 voltages = [f"{cell.measured_volts} V" for cell in bms_hardware.cells.values()] 617 logger.write_info_to_report(f"Resting at 0 mA, {', '.join(voltages)}") 618 serial_watcher.assert_false("flags.prefault_cellimbalance", True) 619 serial_watcher.assert_false("flags.permanentdisable_cellimbalance", True) 620 621 bms_hardware.charger.disable() 622 plateset.ce_switch = False 623 serial_watcher.stop() 624 625 626def test_current_limit(): 627 """ 628 | Description | Confirm A fault is raised after 15A for 1 seconds | 629 | :------------------- | :----------------------------------------------------------------------------------- | 630 | GitHub Issue | turnaroundfactor/HITL#366 | 631 | Instructions | 1. Rest for 30 second </br>\ 632 2. Discharge at 2.25 amps for 1 second (the max limit will be 2amps) </br>\ 633 3. Verify a prefault (before 1 second) and fault (after 1 second) are raised. </br>\ 634 4. Rest until faults clear </br>\ 635 5. Discharge at 2.25 amps for less than 1 second </br>\ 636 6. Verify a prefault occurred but not a fault | 637 | Pass / Fail Criteria | Pass if faults are raised after 1 second, but not before | 638 | Estimated Duration | 5 minutes | 639 | Notes | We use 2A as a limit due to hardware limitations | 640 """ 641 # FIXME(JA): update duration after first test 642 643 logger.write_info_to_report("Testing Current Limit") 644 test_reset_cell_sims() 645 serial_watcher.start() 646 647 # Rest and make sure no faults are active 648 logger.write_info_to_report("Confirm no faults are active") 649 serial_watcher.assert_true("flags.prefault_sw_overcurrent_discharge", False, 1) 650 serial_watcher.assert_true("flags.fault_sw_overcurrent_discharge", False, 1) 651 652 # Discharge at 2.25 amps for more than 1 second and verify faults are raised 653 logger.write_info_to_report("Discharging 2.25 A for more than 1 second") 654 with bms_hardware.load(2.25): 655 start_time = time.perf_counter() 656 serial_watcher.assert_true("flags.prefault_sw_overcurrent_discharge", True, 2) 657 serial_watcher.assert_true("flags.fault_sw_overcurrent_discharge", True, 2) 658 assert serial_watcher.events["flags.fault_sw_overcurrent_discharge"][-1].time - start_time >= 1 659 660 # Rest until faults clear 661 logger.write_info_to_report("Resting") 662 serial_watcher.assert_true("flags.prefault_sw_overcurrent_discharge", False, 3) 663 serial_watcher.assert_true("flags.fault_sw_overcurrent_discharge", False, 3) 664 assert ( 665 serial_watcher.events["flags.fault_sw_overcurrent_discharge"][2].bms_time 666 - serial_watcher.events["flags.fault_sw_overcurrent_discharge"][1].bms_time 667 ).total_seconds() >= 20 668 669 # Discharge at 2.25 amps for less than 1 second, verify only prefault is raised 670 logger.write_info_to_report("Discharging 2.25 A for less than 1 second") 671 with bms_hardware.load(2.25): 672 time.sleep(0.5) 673 serial_watcher.assert_true("flags.prefault_sw_overcurrent_discharge", True, 4) 674 serial_watcher.assert_false("flags.fault_sw_overcurrent_discharge", True, 4) 675 676 # Rest until faults clear 677 logger.write_info_to_report("Resting") 678 serial_watcher.assert_true("flags.prefault_sw_overcurrent_discharge", False, 5) 679 680 serial_watcher.stop()
34def test_reset_cell_sims(): 35 """Activate cell sims and set appropriate temperatures.""" 36 logger.write_info_to_report("Powering down cell sims") 37 for cell in bms_hardware.cells.values(): 38 cell.disengage_safety_protocols = True 39 cell.volts = 0.0001 40 time.sleep(5) 41 logger.write_info_to_report("Powering up cell sims") 42 for cell in bms_hardware.cells.values(): 43 cell.volts = CELL_VOLTAGE 44 cell.disengage_safety_protocols = False 45 for cell in bms_hardware.cells.values(): 46 cell.exact_volts = CELL_VOLTAGE 47 logger.write_info_to_report("Setting temperature to 15°C") 48 plateset.thermistor1 = 15 49 plateset.thermistor2 = 15 50 logger.write_info_to_report("Enabling faults") 51 time.sleep(1) 52 smbus.write_register(SMBusReg.MANUFACTURING_ACCESS, smbus_types.BMSCommands.FAULT_ENABLE) # Enable faults
Activate cell sims and set appropriate temperatures.
55def test_wakeup_1(): 56 """ 57 The BMS should be in a state of slumber if the current measured is between -46mA and 19ma. This is done using 58 internal comparators on the board to a logical AND chip feeding into an interrupt pin. To test this, 3 different 59 currents should be set. One current below -46ma, another current between -46mA and 19ma, and another current above 60 19ma. If the current is within the allowable range, we should read logic 1 on the N_WAKEUP pin. If the current is 61 outside (above or below) we should read logic 0 on the pin. 62 63 # Test 64 - Default state at 0 mA (between -46mA and 19ma) 65 - n_wakeup_gpio = 1 66 - Discharging at 100 mA (below -46 mA) 67 - n_wakeup_gpio = 0 68 - Discharging at 10 mA (between -46mA and 19ma) 69 - n_wakeup_gpio = 1 70 - Charging at 100 mA (above 19 mA) 71 - n_wakeup_gpio = 0 72 """ 73 logger.write_info_to_report("Testing Wakeup") 74 test_reset_cell_sims() 75 serial_watcher.start() 76 77 logger.write_info_to_report("Default state") 78 serial_watcher.assert_true("n_wakeup_gpio", True, 1) 79 80 logger.write_info_to_report("Discharging 100 mA") 81 bms_hardware.load.mode_cc() 82 bms_hardware.load.amps = 0.100 83 bms_hardware.load.enable() 84 serial_watcher.assert_true("n_wakeup_gpio", False, 2) 85 86 logger.write_info_to_report("Discharging 10 mA") 87 bms_hardware.load.amps = 0.010 88 serial_watcher.assert_true("n_wakeup_gpio", True, 3) 89 bms_hardware.load.disable() 90 91 logger.write_info_to_report("Charging 100 mA") 92 bms_hardware.charger.set_profile(16.8, 0.100) 93 plateset.ce_switch = True 94 bms_hardware.charger.enable() 95 serial_watcher.assert_true("n_wakeup_gpio", False, 4) 96 bms_hardware.charger.disable() 97 plateset.ce_switch = False 98 99 serial_watcher.stop()
The BMS should be in a state of slumber if the current measured is between -46mA and 19ma. This is done using internal comparators on the board to a logical AND chip feeding into an interrupt pin. To test this, 3 different currents should be set. One current below -46ma, another current between -46mA and 19ma, and another current above 19ma. If the current is within the allowable range, we should read logic 1 on the N_WAKEUP pin. If the current is outside (above or below) we should read logic 0 on the pin.
Test
- Default state at 0 mA (between -46mA and 19ma)
- n_wakeup_gpio = 1
- Discharging at 100 mA (below -46 mA)
- n_wakeup_gpio = 0
- Discharging at 10 mA (between -46mA and 19ma)
- n_wakeup_gpio = 1
- Charging at 100 mA (above 19 mA)
- n_wakeup_gpio = 0
102def test_oc_oneshot(): 103 """ 104 OC Oneshot: This is an interrupt that will trigger if we sink a ton of current into the battery, this cannot be 105 tested with just software and the comparators used for this have not been tested for hardware yet. 106 107 The bms should have an interrupt be fired when the current reaches the max amount (I think ~63 amps). this will be 108 a test for Tyler to do on his own 109 110 NOTE: High current. Only run this on a modified board. 111 """
OC Oneshot: This is an interrupt that will trigger if we sink a ton of current into the battery, this cannot be tested with just software and the comparators used for this have not been tested for hardware yet.
The bms should have an interrupt be fired when the current reaches the max amount (I think ~63 amps). this will be a test for Tyler to do on his own
NOTE: High current. Only run this on a modified board.
114def test_overtemp_faults(): 115 """ 116 If our batteries get too hot, we must trigger a fault. This is common during high discharge or charging cycles. 117 There are 3 different environments where we would trigger an overtemp fault: charge, discharge and resting. While 118 charging, if we are above 45C we must trigger a fault If we are resting or discharging at 59 degrees we must 119 trigger a fault. Both of these faults should trigger a prefault condition in our flags. After we cycle again we 120 should then trigger a full fault. If the temperature ever goes above 93 degrees, the fault should never clear and 121 we should be in permanent fault and trigger the fets. (This should also be seen in the flags) 122 123 # Test Overview (reset inbetween tests) 124 - Default state at 15°C 125 - prefault_overtemp_discharge = 0 126 - fault_overtemp_discharge = 0 127 - prefault_overtemp_charge = 0 128 - fault_overtemp_charge = 0 129 - permanentdisable_overtemp = 0 130 - measure_output_fets_disabled = 0 131 132 - Resting at 59°C (at or above 59°C) 133 - prefault_overtemp_discharge = 1 134 - fault_overtemp_discharge = 1 135 136 - Discharging at 100 mA, 59°C (at or above 59°C) 137 - prefault_overtemp_discharge = 1 138 - fault_overtemp_discharge = 1 139 140 - Charging at 100 mA, 46°C (at or above 45°C) 141 - prefault_overtemp_charge = 1 142 - fault_overtemp_charge = 1permanentdisable_overtemp 143 144 - Charging at 100 mA, 94°C (at or above 93°C) 145 - permanentdisable_overtemp = 1 146 147 - Discharging at 100 mA, 94°C (at or above 93°C) 148 - permanentdisable_overtemp = 1 149 150 - Resting at 94°C (at or above 93°C) 151 - permanentdisable_overtemp = 1 152 """ 153 logger.write_info_to_report("Testing Overtemp") 154 test_reset_cell_sims() 155 serial_watcher.start() 156 157 # Test default state 158 logger.write_info_to_report(f"Resting at 0 mA, {CELL_VOLTAGE} V, 15°C") 159 plateset.thermistor1 = 15 160 serial_watcher.assert_true("flags.prefault_overtemp_discharge", False, 1) 161 serial_watcher.assert_true("flags.fault_overtemp_discharge", False, 1) 162 serial_watcher.assert_true("flags.prefault_overtemp_charge", False, 1) 163 serial_watcher.assert_true("flags.fault_overtemp_charge", False, 1) 164 serial_watcher.assert_true("flags.permanentdisable_overtemp", False, 1) 165 serial_watcher.assert_true("flags.measure_output_fets_disabled", False, 1) 166 167 # Test resting overtemp 168 logger.write_info_to_report("Resting at 59°C") 169 plateset.thermistor1 = 59 170 serial_watcher.assert_true("flags.prefault_overtemp_discharge", True, 2) 171 serial_watcher.assert_true("flags.fault_overtemp_discharge", True, 2) 172 logger.write_info_to_report("Resting at 15°C") 173 plateset.thermistor1 = 15 174 serial_watcher.assert_true("flags.prefault_overtemp_discharge", False, 3) 175 serial_watcher.assert_true("flags.fault_overtemp_discharge", False, 3) 176 177 # Test discharging overtemp 178 logger.write_info_to_report("Discharging at -100 mA, 59°C") 179 bms_hardware.load.mode_cc() 180 bms_hardware.load.amps = 0.100 181 bms_hardware.load.enable() 182 plateset.thermistor1 = 59 183 serial_watcher.assert_true("flags.prefault_overtemp_discharge", True, 4) 184 serial_watcher.assert_true("flags.fault_overtemp_discharge", True, 4) 185 logger.write_info_to_report("Discharging at -100 mA, 15°C") 186 plateset.thermistor1 = 15 187 serial_watcher.assert_true("flags.prefault_overtemp_discharge", False, 5) 188 serial_watcher.assert_true("flags.fault_overtemp_discharge", False, 5) 189 bms_hardware.load.disable() 190 191 # Test charging overtemp 192 logger.write_info_to_report("Charging at 100 mA, 65°C") 193 bms_hardware.charger.set_profile(16.8, 0.1) 194 plateset.ce_switch = True 195 bms_hardware.charger.enable() 196 plateset.thermistor1 = 65 197 serial_watcher.assert_true("flags.prefault_overtemp_charge", True, 2) 198 serial_watcher.assert_true("flags.fault_overtemp_charge", True, 2) 199 logger.write_info_to_report("Charging at 100 mA, 45°C") 200 plateset.thermistor1 = 45 201 serial_watcher.assert_true("flags.prefault_overtemp_charge", False, 3) 202 serial_watcher.assert_true("flags.fault_overtemp_charge", False, 3) 203 bms_hardware.charger.disable() 204 plateset.ce_switch = False 205 206 # Test charging permanent disable 207 logger.write_info_to_report("Charging at 100 mA, 94°C") 208 plateset.disengage_safety_protocols = True 209 plateset.thermistor1 = 94 210 plateset.disengage_safety_protocols = False 211 bms_hardware.charger.set_profile(16.8, 0.1) 212 plateset.ce_switch = True 213 bms_hardware.charger.enable() 214 215 serial_watcher.assert_true("flags.permanentdisable_overtemp", True, 2) 216 217 logger.write_info_to_report("Charging at 100 mA, 15°C") 218 plateset.thermistor1 = 15 219 serial_watcher.assert_false("flags.permanentdisable_overtemp", False, 3) 220 221 bms_hardware.charger.disable() 222 plateset.ce_switch = False 223 test_reset_cell_sims() 224 225 # Test discharging permanent disable 226 logger.write_info_to_report("Discharging at -100 mA, 94°C") 227 plateset.disengage_safety_protocols = True 228 plateset.thermistor1 = 94 229 plateset.disengage_safety_protocols = False 230 bms_hardware.load.mode_cc() 231 bms_hardware.load.amps = 0.100 232 bms_hardware.load.enable() 233 234 serial_watcher.assert_true("flags.permanentdisable_overtemp", True, 4) 235 236 logger.write_info_to_report("Discharging at -100 mA, 15°C") 237 plateset.thermistor1 = 15 238 serial_watcher.assert_false("flags.permanentdisable_overtemp", False, 5) 239 240 bms_hardware.load.disable() 241 test_reset_cell_sims() 242 243 # Test resting permanent disable 244 plateset.disengage_safety_protocols = True 245 logger.write_info_to_report("Resting at 94°C") 246 plateset.thermistor1 = 94 247 plateset.disengage_safety_protocols = False 248 serial_watcher.assert_true("flags.permanentdisable_overtemp", True, 6) 249 250 logger.write_info_to_report("Resting at 15°C") 251 plateset.thermistor1 = 15 252 serial_watcher.assert_false("flags.permanentdisable_overtemp", False, 7) 253 254 serial_watcher.stop()
If our batteries get too hot, we must trigger a fault. This is common during high discharge or charging cycles. There are 3 different environments where we would trigger an overtemp fault: charge, discharge and resting. While charging, if we are above 45C we must trigger a fault If we are resting or discharging at 59 degrees we must trigger a fault. Both of these faults should trigger a prefault condition in our flags. After we cycle again we should then trigger a full fault. If the temperature ever goes above 93 degrees, the fault should never clear and we should be in permanent fault and trigger the fets. (This should also be seen in the flags)
Test Overview (reset inbetween tests)
Default state at 15°C
- prefault_overtemp_discharge = 0
- fault_overtemp_discharge = 0
- prefault_overtemp_charge = 0
- fault_overtemp_charge = 0
- permanentdisable_overtemp = 0
- measure_output_fets_disabled = 0
Resting at 59°C (at or above 59°C)
- prefault_overtemp_discharge = 1
- fault_overtemp_discharge = 1
Discharging at 100 mA, 59°C (at or above 59°C)
- prefault_overtemp_discharge = 1
- fault_overtemp_discharge = 1
Charging at 100 mA, 46°C (at or above 45°C)
- prefault_overtemp_charge = 1
- fault_overtemp_charge = 1permanentdisable_overtemp
Charging at 100 mA, 94°C (at or above 93°C)
- permanentdisable_overtemp = 1
Discharging at 100 mA, 94°C (at or above 93°C)
- permanentdisable_overtemp = 1
Resting at 94°C (at or above 93°C)
- permanentdisable_overtemp = 1
257def test_undertemp_faults(): 258 """ 259 This occurs when we read more than 20 mamps from the battery, if any of the cells are under 0 degrees Celsius this 260 will trigger a fault. This will be cleared if we go above -2C. Regardless of current being measured, if we ever 261 read below -20C, this should trigger a fault. This fault should not be cleared until we are above -18C 262 263 *** THIS FAULT NEEDS TO BE INVESTIGATED 264 https://github.com/orgs/turnaroundfactor/projects/24?pane=issue&itemId=50150194 265 """ 266 logger.write_info_to_report("Testing Undertemp") 267 test_reset_cell_sims() 268 serial_watcher.start() 269 270 logger.write_info_to_report(f"Resting at 0 mA, {CELL_VOLTAGE} V, 15°C") 271 plateset.thermistor1 = 15 272 273 # Discharging flags 274 serial_watcher.assert_true("flags.prefault_undertemp_discharge", False, 1) 275 serial_watcher.assert_true("flags.fault_undertemp_discharge", False, 1) 276 277 # Charging flags 278 serial_watcher.assert_true("flags.prefault_undertemp_charge", False, 1) 279 serial_watcher.assert_true("flags.fault_undertemp_charge", False, 1) 280 281 logger.write_info_to_report("Resting at -21°C") 282 plateset.thermistor1 = -21 283 serial_watcher.assert_true("flags.prefault_undertemp_discharge", True, 2) 284 serial_watcher.assert_true("flags.fault_undertemp_discharge", True, 2) 285 logger.write_info_to_report("Resting at -17°C") 286 plateset.thermistor1 = -17 287 serial_watcher.assert_true("flags.prefault_undertemp_discharge", False, 3) 288 serial_watcher.assert_true("flags.fault_undertemp_discharge", False, 3) 289 290 logger.write_info_to_report("Discharging at -100 mA, -21°C") 291 bms_hardware.load.mode_cc() 292 bms_hardware.load.amps = 0.100 293 bms_hardware.load.enable() 294 plateset.thermistor1 = -21 295 serial_watcher.assert_true("flags.prefault_undertemp_discharge", True, 4) 296 serial_watcher.assert_true("flags.fault_undertemp_discharge", True, 4) 297 logger.write_info_to_report("Discharging at -100 mA, -17°C") 298 plateset.thermistor1 = -17 299 serial_watcher.assert_true("flags.prefault_undertemp_discharge", False, 5) 300 serial_watcher.assert_true("flags.fault_undertemp_discharge", False, 5) 301 bms_hardware.load.disable() 302 303 logger.write_info_to_report("Charging at 100 mA, -25°C") 304 bms_hardware.charger.set_profile(16.8, 0.1) 305 plateset.ce_switch = True 306 bms_hardware.charger.enable() 307 plateset.thermistor1 = -25 308 serial_watcher.assert_true("flags.prefault_undertemp_charge", True, 2) 309 serial_watcher.assert_true("flags.fault_undertemp_charge", True, 2) 310 logger.write_info_to_report("Charging at 100 mA, -15°C") 311 plateset.thermistor1 = -15 312 serial_watcher.assert_true("flags.prefault_undertemp_charge", False, 3) 313 serial_watcher.assert_true("flags.fault_undertemp_charge", False, 3) 314 bms_hardware.charger.disable() 315 plateset.ce_switch = False 316 317 serial_watcher.stop()
This occurs when we read more than 20 mamps from the battery, if any of the cells are under 0 degrees Celsius this will trigger a fault. This will be cleared if we go above -2C. Regardless of current being measured, if we ever read below -20C, this should trigger a fault. This fault should not be cleared until we are above -18C
* THIS FAULT NEEDS TO BE INVESTIGATED https://github.com/orgs/turnaroundfactor/projects/24?pane=issue&itemId=50150194
320def set_exact_volts(cell: Cell, voltage: float, compensation: float = 0.08): 321 """What the BMS reads won't exactly match the set voltage, thus we need slight adjustments.""" 322 cell.exact_volts = voltage + compensation 323 logger.write_debug_to_report(f"Cell is {cell.volts}V")
What the BMS reads won't exactly match the set voltage, thus we need slight adjustments.
326def test_overvoltage_faults(): 327 """ 328 While charging, we need to monitor the voltage of our cells. Specifically, if a cell ever goes above 4.205 Volts, 329 we should trigger a prefault. If this prefault exsists for more than 3 seconds, we then should trigger a full 330 fault. If a cell ever gets to be above 4.250 volts, we should trigger a permanent fault. If we go under 4.201 we 331 should be able to clear the fault 332 """ 333 logger.write_info_to_report("Testing Overvoltage") 334 test_reset_cell_sims() 335 test_cell = bms_hardware.cells[1] 336 serial_watcher.start() 337 test_cell.disengage_safety_protocols = True 338 339 logger.write_info_to_report(f"Resting at 0 mA, {CELL_VOLTAGE} V") 340 serial_watcher.assert_true("flags.prefault_overvoltage_charge", False, 1) 341 serial_watcher.assert_true("flags.fault_overvoltage_charge", False, 1) 342 serial_watcher.assert_true("flags.permanentdisable_overvoltage", False, 1) 343 344 bms_hardware.charger.set_profile(16.8, 0.1) 345 plateset.ce_switch = True 346 bms_hardware.charger.enable() 347 logger.write_info_to_report("Charging at 100 mA, 4.21 V") 348 set_exact_volts(test_cell, 4.21) 349 serial_watcher.assert_true("flags.prefault_overvoltage_charge", True, 2) 350 serial_watcher.assert_true("flags.fault_overvoltage_charge", True, 2) 351 352 logger.write_info_to_report("Charging at 100 mA, 4.10 V") 353 set_exact_volts(test_cell, 4.10) 354 serial_watcher.assert_true("flags.prefault_overvoltage_charge", False, 3) 355 serial_watcher.assert_true("flags.fault_overvoltage_charge", False, 3) 356 357 logger.write_info_to_report("Charging at 100 mA, 4.26 V") 358 set_exact_volts(test_cell, 4.26) 359 serial_watcher.assert_true("flags.permanentdisable_overvoltage", True, 2) 360 361 logger.write_info_to_report("Charging at 100 mA, 4.10 V") 362 set_exact_volts(test_cell, 4.10) 363 serial_watcher.assert_false("flags.permanentdisable_overvoltage", False, 3) 364 365 bms_hardware.charger.disable() 366 plateset.ce_switch = False 367 368 serial_watcher.stop()
While charging, we need to monitor the voltage of our cells. Specifically, if a cell ever goes above 4.205 Volts, we should trigger a prefault. If this prefault exsists for more than 3 seconds, we then should trigger a full fault. If a cell ever gets to be above 4.250 volts, we should trigger a permanent fault. If we go under 4.201 we should be able to clear the fault
371def test_undervoltage_faults(): 372 """ 373 This has also been validated in software, meaning the logic should properly handle a situation with a cell 374 discharging too low, however this has not yet been tested in hardware with a cell sensor reading that low of a 375 voltage and triggering a fault. 376 377 If we are reading less than 20mamps from the cells, we should be able to trigger an under-voltage fault. If we 378 read less than 2.4 volts, we must trigger a fault. If this fault persists for over 1 second, we should then trigger 379 a full fault. We will not clear this fault unless we are able to read above 2.5 volts. 380 If we are reading over 400mamps and a cell reads less than 2.325 volts, we must trigger a cell voltage charge min 381 prefault, if this persists for another bms software cycle we will trigger a full fault. This fault will clear when 382 we read above this voltage. If the cell voltage ever goes under 2.3 while charging, we must trigger a permanent 383 fault. 384 """ 385 logger.write_info_to_report("Testing Undervoltage") 386 test_reset_cell_sims() 387 test_reset_cell_sims() 388 test_cell = bms_hardware.cells[1] 389 for cell in bms_hardware.cells.values(): 390 cell.disengage_safety_protocols = True 391 set_exact_volts(cell, 3.0, 0.05) # Must be low enough to not trigger cell imbalance [abs(high - low) > 1.5V] 392 serial_watcher.start() 393 394 logger.write_info_to_report("Resting at 0 mA, 3.0 V") 395 serial_watcher.assert_true("flags.prefault_undervoltage_discharge", False, 1) 396 serial_watcher.assert_true("flags.faultslumber_undervoltage_discharge", False, 1) 397 serial_watcher.assert_true("flags.prefault_undervoltage_charge", False, 1) 398 serial_watcher.assert_true("flags.fault_undervoltage_charge", False, 1) 399 serial_watcher.assert_true("flags.permanentdisable_undervoltage", False, 1) 400 401 logger.write_info_to_report("Resting at 0 mA, 2.3 V") 402 set_exact_volts(test_cell, 2.3, 0.05) 403 serial_watcher.assert_true("flags.prefault_undervoltage_discharge", True, 2, wait_time=600) 404 serial_watcher.assert_true("flags.faultslumber_undervoltage_discharge", True, 2, wait_time=600) 405 406 logger.write_info_to_report("Resting at 0 mA, 2.6 V") 407 set_exact_volts(test_cell, 2.6, 0.05) 408 serial_watcher.assert_true("flags.prefault_undervoltage_discharge", False, 3, wait_time=600) 409 serial_watcher.assert_true("flags.faultslumber_undervoltage_discharge", False, 3, wait_time=600) 410 411 bms_hardware.charger.set_profile(16.8, 0.500) 412 plateset.ce_switch = True 413 bms_hardware.charger.enable() 414 logger.write_info_to_report("Charging at 500 mA, 2.3 V") 415 set_exact_volts(test_cell, 2.3, 0.01) 416 serial_watcher.assert_true("flags.prefault_undervoltage_charge", True, 2, wait_time=600) 417 serial_watcher.assert_true("flags.fault_undervoltage_charge", True, 2, wait_time=600) 418 419 logger.write_info_to_report("Charging at 500 mA, 2.4 V") 420 set_exact_volts(test_cell, 2.4, 0.05) 421 serial_watcher.assert_true("flags.prefault_undervoltage_charge", False, 3) 422 serial_watcher.assert_true("flags.fault_undervoltage_charge", False, 3) 423 424 logger.write_info_to_report("Charging at 500 mA, 2.2 V") 425 set_exact_volts(test_cell, 2.2, 0.05) 426 serial_watcher.assert_true("flags.permanentdisable_undervoltage", True, 2) 427 428 logger.write_info_to_report("Charging at 500 mA, 2.6 V") 429 set_exact_volts(test_cell, 2.6, 0.05) 430 serial_watcher.assert_false("flags.permanentdisable_undervoltage", False, 3) 431 432 bms_hardware.charger.disable() 433 plateset.ce_switch = False 434 435 serial_watcher.stop()
This has also been validated in software, meaning the logic should properly handle a situation with a cell discharging too low, however this has not yet been tested in hardware with a cell sensor reading that low of a voltage and triggering a fault.
If we are reading less than 20mamps from the cells, we should be able to trigger an under-voltage fault. If we read less than 2.4 volts, we must trigger a fault. If this fault persists for over 1 second, we should then trigger a full fault. We will not clear this fault unless we are able to read above 2.5 volts. If we are reading over 400mamps and a cell reads less than 2.325 volts, we must trigger a cell voltage charge min prefault, if this persists for another bms software cycle we will trigger a full fault. This fault will clear when we read above this voltage. If the cell voltage ever goes under 2.3 while charging, we must trigger a permanent fault.
438def test_cell_imbalance(): 439 """Occurs when the difference between the highest and lowest cell is 1.5V.""" 440 logger.write_info_to_report("Testing Cell Imbalance") 441 test_reset_cell_sims() 442 test_cell = bms_hardware.cells[1] 443 serial_watcher.start() 444 test_cell.disengage_safety_protocols = True 445 446 logger.write_info_to_report(f"Resting at 0 mA, {CELL_VOLTAGE} V") 447 serial_watcher.assert_true("flags.prefault_cellimbalance", False, 1) 448 serial_watcher.assert_true("flags.permanentdisable_cellimbalance", False, 1) 449 450 logger.write_info_to_report("Resting at 0 mA, 2.0 V") 451 set_exact_volts(test_cell, 2.0) 452 serial_watcher.assert_true("flags.prefault_cellimbalance", True, 2) 453 serial_watcher.assert_true("flags.permanentdisable_cellimbalance", True, 2) 454 455 logger.write_info_to_report(f"Resting at 0 mA, {CELL_VOLTAGE} V") 456 set_exact_volts(test_cell, CELL_VOLTAGE) 457 serial_watcher.assert_false("flags.permanentdisable_cellimbalance", False, 3) 458 459 serial_watcher.stop()
Occurs when the difference between the highest and lowest cell is 1.5V.
462def test_overvoltage_overtemp_faults(): 463 """A combo test where we have a high temperature and high voltage.""" 464 logger.write_info_to_report("Testing Overvoltage with Overtemp") 465 test_reset_cell_sims() 466 test_cell = bms_hardware.cells[1] 467 serial_watcher.start() 468 test_cell.disengage_safety_protocols = True 469 470 logger.write_info_to_report(f"Resting at 0 mA, {CELL_VOLTAGE} V, 15°C") 471 serial_watcher.assert_true("flags.prefault_overvoltage_charge", False, 1) 472 serial_watcher.assert_true("flags.fault_overvoltage_charge", False, 1) 473 serial_watcher.assert_true("flags.permanentdisable_overvoltage", False, 1) 474 475 serial_watcher.assert_true("flags.prefault_overtemp_charge", False, 1) 476 serial_watcher.assert_true("flags.fault_overtemp_charge", False, 1) 477 serial_watcher.assert_true("flags.permanentdisable_overtemp", False, 1) 478 479 bms_hardware.charger.set_profile(16.8, 0.1) 480 plateset.ce_switch = True 481 bms_hardware.charger.enable() 482 logger.write_info_to_report("Charging at 100 mA, 4.21 V, 65°C") 483 set_exact_volts(test_cell, 4.21) 484 plateset.thermistor1 = 65 485 serial_watcher.assert_true("flags.prefault_overvoltage_charge", True, 2) 486 serial_watcher.assert_true("flags.fault_overvoltage_charge", True, 2) 487 serial_watcher.assert_true("flags.prefault_overtemp_charge", True, 2) 488 serial_watcher.assert_true("flags.fault_overtemp_charge", True, 2) 489 490 logger.write_info_to_report("Charging at 100 mA, 4.10 V, 45°C") 491 set_exact_volts(test_cell, 4.10) 492 plateset.thermistor1 = 45 493 serial_watcher.assert_true("flags.prefault_overvoltage_charge", False, 3) 494 serial_watcher.assert_true("flags.fault_overvoltage_charge", False, 3) 495 serial_watcher.assert_true("flags.prefault_overtemp_charge", False, 3) 496 serial_watcher.assert_true("flags.fault_overtemp_charge", False, 3) 497 498 logger.write_info_to_report("Charging at 100 mA, 4.26 V, 94°C") 499 set_exact_volts(test_cell, 4.26) 500 plateset.disengage_safety_protocols = True 501 plateset.thermistor1 = 94 502 plateset.disengage_safety_protocols = False 503 serial_watcher.assert_true("flags.permanentdisable_overvoltage", True, 2) 504 serial_watcher.assert_true("flags.permanentdisable_overtemp", True, 2) 505 506 logger.write_info_to_report("Charging at 100 mA, 4.10 V, 15°C") 507 set_exact_volts(test_cell, 4.10) 508 plateset.thermistor1 = 15 509 serial_watcher.assert_false("flags.permanentdisable_overvoltage", False, 3) 510 serial_watcher.assert_false("flags.permanentdisable_overtemp", False, 3) 511 512 bms_hardware.charger.disable() 513 plateset.ce_switch = False 514 515 serial_watcher.stop()
A combo test where we have a high temperature and high voltage.
518def test_undervoltage_undertemp_faults(): 519 """A combo test where we have a low temperature and low voltage.""" 520 logger.write_info_to_report("Testing Undervoltage with Undertemp") 521 test_reset_cell_sims() 522 test_cell = bms_hardware.cells[1] 523 for cell in bms_hardware.cells.values(): 524 cell.disengage_safety_protocols = True 525 set_exact_volts(cell, 3.0) # Must be low enough to not trigger cell imbalance [abs(high - low) > 1.5V] 526 serial_watcher.start() 527 528 logger.write_info_to_report("Resting at 0 mA, 3.0 V, 15°C") 529 plateset.thermistor1 = 15 530 serial_watcher.assert_true("flags.prefault_undervoltage_discharge", False, 1) 531 serial_watcher.assert_true("flags.faultslumber_undervoltage_discharge", False, 1) 532 serial_watcher.assert_true("flags.prefault_undervoltage_charge", False, 1) 533 serial_watcher.assert_true("flags.fault_undervoltage_charge", False, 1) 534 serial_watcher.assert_true("flags.permanentdisable_undervoltage", False, 1) 535 536 serial_watcher.assert_true("flags.prefault_undertemp_discharge", False, 1) 537 serial_watcher.assert_true("flags.fault_undertemp_discharge", False, 1) 538 serial_watcher.assert_true("flags.prefault_undertemp_charge", False, 1) 539 serial_watcher.assert_true("flags.fault_undertemp_charge", False, 1) 540 541 logger.write_info_to_report("Resting at 0 mA, 2.3 V, -21°C") 542 set_exact_volts(test_cell, 2.3) 543 plateset.thermistor1 = -21 544 serial_watcher.assert_true("flags.prefault_undervoltage_discharge", True, 2) 545 serial_watcher.assert_true("flags.faultslumber_undervoltage_discharge", True, 2) 546 serial_watcher.assert_true("flags.prefault_undertemp_discharge", True, 2) 547 serial_watcher.assert_true("flags.fault_undertemp_discharge", True, 2) 548 549 logger.write_info_to_report("Resting at 0 mA, 2.6 V, -17°C") 550 set_exact_volts(test_cell, 2.6) 551 plateset.thermistor1 = -17 552 serial_watcher.assert_true("flags.prefault_undervoltage_discharge", False, 3) 553 serial_watcher.assert_true("flags.faultslumber_undervoltage_discharge", False, 3) 554 serial_watcher.assert_true("flags.prefault_undertemp_discharge", False, 3) 555 serial_watcher.assert_true("flags.fault_undertemp_discharge", False, 3) 556 557 bms_hardware.charger.set_profile(16.8, 0.500) 558 plateset.ce_switch = True 559 bms_hardware.charger.enable() 560 logger.write_info_to_report("Charging at 500 mA, 2.3 V, -25°C") 561 set_exact_volts(test_cell, 2.3, 0.01) 562 plateset.thermistor1 = -25 563 serial_watcher.assert_true("flags.prefault_undervoltage_charge", True, 2) 564 serial_watcher.assert_true("flags.fault_undervoltage_charge", True, 2) 565 serial_watcher.assert_true("flags.prefault_undertemp_charge", True, 2) 566 serial_watcher.assert_true("flags.fault_undertemp_charge", True, 2) 567 568 logger.write_info_to_report("Charging at 500 mA, 2.4 V, -15°C") 569 set_exact_volts(test_cell, 2.4) 570 plateset.thermistor1 = -15 571 serial_watcher.assert_true("flags.prefault_undervoltage_charge", False, 3) 572 serial_watcher.assert_true("flags.fault_undervoltage_charge", False, 3) 573 serial_watcher.assert_true("flags.prefault_undertemp_charge", False, 3) 574 serial_watcher.assert_true("flags.fault_undertemp_charge", False, 3) 575 576 logger.write_info_to_report("Charging at 500 mA, 2.2 V, 15°C") 577 set_exact_volts(test_cell, 2.2) 578 plateset.thermistor1 = 15 579 serial_watcher.assert_true("flags.permanentdisable_undervoltage", True, 2) 580 581 logger.write_info_to_report("Charging at 500 mA, 2.6 V, 15°C") 582 set_exact_volts(test_cell, 2.6) 583 plateset.thermistor1 = 15 584 serial_watcher.assert_false("flags.permanentdisable_undervoltage", False, 3) 585 586 bms_hardware.charger.disable() 587 plateset.ce_switch = False 588 589 serial_watcher.stop()
A combo test where we have a low temperature and low voltage.
592def test_cell_imbalance_charge(): 593 """Occurs when the difference between the highest and lowest cell is 1.5V.""" 594 logger.write_info_to_report("Testing Cell Imbalance Charge") 595 test_reset_cell_sims() 596 test_cell = bms_hardware.cells[1] 597 serial_watcher.start() 598 test_cell.disengage_safety_protocols = True 599 for cell in bms_hardware.cells.values(): 600 cell.exact_volts = 4.2 601 602 voltages = [f"{cell.measured_volts} V" for cell in bms_hardware.cells.values()] 603 logger.write_info_to_report(f"Resting at 0 mA, {', '.join(voltages)}") 604 serial_watcher.assert_true("flags.prefault_cellimbalance", False, 1) 605 serial_watcher.assert_true("flags.permanentdisable_cellimbalance", False, 1) 606 607 bms_hardware.charger.set_profile(16.8, 1) 608 plateset.ce_switch = True 609 bms_hardware.charger.enable() 610 set_exact_volts(test_cell, 2.5) 611 voltages = [f"{cell.measured_volts} V" for cell in bms_hardware.cells.values()] 612 logger.write_info_to_report(f"Charging at 1 A, {', '.join(voltages)}") 613 serial_watcher.assert_false("flags.prefault_cellimbalance", True) 614 serial_watcher.assert_false("flags.permanentdisable_cellimbalance", True) 615 616 test_cell.exact_volts = 4.2 617 voltages = [f"{cell.measured_volts} V" for cell in bms_hardware.cells.values()] 618 logger.write_info_to_report(f"Resting at 0 mA, {', '.join(voltages)}") 619 serial_watcher.assert_false("flags.prefault_cellimbalance", True) 620 serial_watcher.assert_false("flags.permanentdisable_cellimbalance", True) 621 622 bms_hardware.charger.disable() 623 plateset.ce_switch = False 624 serial_watcher.stop()
Occurs when the difference between the highest and lowest cell is 1.5V.
627def test_current_limit(): 628 """ 629 | Description | Confirm A fault is raised after 15A for 1 seconds | 630 | :------------------- | :----------------------------------------------------------------------------------- | 631 | GitHub Issue | turnaroundfactor/HITL#366 | 632 | Instructions | 1. Rest for 30 second </br>\ 633 2. Discharge at 2.25 amps for 1 second (the max limit will be 2amps) </br>\ 634 3. Verify a prefault (before 1 second) and fault (after 1 second) are raised. </br>\ 635 4. Rest until faults clear </br>\ 636 5. Discharge at 2.25 amps for less than 1 second </br>\ 637 6. Verify a prefault occurred but not a fault | 638 | Pass / Fail Criteria | Pass if faults are raised after 1 second, but not before | 639 | Estimated Duration | 5 minutes | 640 | Notes | We use 2A as a limit due to hardware limitations | 641 """ 642 # FIXME(JA): update duration after first test 643 644 logger.write_info_to_report("Testing Current Limit") 645 test_reset_cell_sims() 646 serial_watcher.start() 647 648 # Rest and make sure no faults are active 649 logger.write_info_to_report("Confirm no faults are active") 650 serial_watcher.assert_true("flags.prefault_sw_overcurrent_discharge", False, 1) 651 serial_watcher.assert_true("flags.fault_sw_overcurrent_discharge", False, 1) 652 653 # Discharge at 2.25 amps for more than 1 second and verify faults are raised 654 logger.write_info_to_report("Discharging 2.25 A for more than 1 second") 655 with bms_hardware.load(2.25): 656 start_time = time.perf_counter() 657 serial_watcher.assert_true("flags.prefault_sw_overcurrent_discharge", True, 2) 658 serial_watcher.assert_true("flags.fault_sw_overcurrent_discharge", True, 2) 659 assert serial_watcher.events["flags.fault_sw_overcurrent_discharge"][-1].time - start_time >= 1 660 661 # Rest until faults clear 662 logger.write_info_to_report("Resting") 663 serial_watcher.assert_true("flags.prefault_sw_overcurrent_discharge", False, 3) 664 serial_watcher.assert_true("flags.fault_sw_overcurrent_discharge", False, 3) 665 assert ( 666 serial_watcher.events["flags.fault_sw_overcurrent_discharge"][2].bms_time 667 - serial_watcher.events["flags.fault_sw_overcurrent_discharge"][1].bms_time 668 ).total_seconds() >= 20 669 670 # Discharge at 2.25 amps for less than 1 second, verify only prefault is raised 671 logger.write_info_to_report("Discharging 2.25 A for less than 1 second") 672 with bms_hardware.load(2.25): 673 time.sleep(0.5) 674 serial_watcher.assert_true("flags.prefault_sw_overcurrent_discharge", True, 4) 675 serial_watcher.assert_false("flags.fault_sw_overcurrent_discharge", True, 4) 676 677 # Rest until faults clear 678 logger.write_info_to_report("Resting") 679 serial_watcher.assert_true("flags.prefault_sw_overcurrent_discharge", False, 5) 680 681 serial_watcher.stop()
| Description | Confirm A fault is raised after 15A for 1 seconds |
|---|---|
| GitHub Issue | turnaroundfactor/HITL#366 |
| Instructions | 1. Rest for 30 second 2. Discharge at 2.25 amps for 1 second (the max limit will be 2amps) 3. Verify a prefault (before 1 second) and fault (after 1 second) are raised. 4. Rest until faults clear 5. Discharge at 2.25 amps for less than 1 second 6. Verify a prefault occurred but not a fault |
| Pass / Fail Criteria | Pass if faults are raised after 1 second, but not before |
| Estimated Duration | 5 minutes |
| Notes | We use 2A as a limit due to hardware limitations |