hitl_tester.test_cases.bms.battery_tester

A duplicate of the older battery tester program.

For example, to charge 17 cells at 700mA for 14 hours, rest for 10 seconds, constant resistance discharge at 17V, 17Ω

  • ./hitl_tester.py battery_tester -c DEBUG -DCHARGE_TYPE=custom -DCHARGE_RATE=700 -DCHARGE_TIME=50400 -DCELL_COUNT=17 -DDISCHARGE_VOLTAGE=17 -DDISCHARGE_RESISTANCE=17 -DRESTING_TIME=10

Used in these test plans:

  • battery_tester_pack ⠀⠀⠀(bms/battery_tester_pack.plan)
  • battery_tester ⠀⠀⠀(bms/battery_tester.plan)

Example Command (warning: test plan may run other test cases):

  • ./hitl_tester.py battery_tester_pack -DSAMPLE_RATE=10 -DCELL_COUNT=None -DCAPACITY=None -DCHARGE_TYPE=UNDEFINED -DCHARGE_RATE=None -DCHARGE_TIME=None -DDISCHARGE_CURRENT=None -DDISCHARGE_RESISTANCE=None -DDISCHARGE_CURRENT_LIMIT=10 -DDISCHARGE_VOLTAGE=None -DRESTING_TIME=None
  1"""
  2A duplicate of the older battery tester program.
  3
  4For example, to charge 17 cells at 700mA for 14 hours, rest for 10 seconds, constant resistance discharge at 17V, 17Ω
  5   - `./hitl_tester.py battery_tester -c DEBUG -DCHARGE_TYPE=custom -DCHARGE_RATE=700 -DCHARGE_TIME=50400
  6-DCELL_COUNT=17 -DDISCHARGE_VOLTAGE=17 -DDISCHARGE_RESISTANCE=17 -DRESTING_TIME=10`
  7"""
  8
  9from __future__ import annotations
 10
 11import pytest
 12from colorama import Fore
 13
 14from hitl_tester.modules.bms.bms_hw import BMSHardware
 15from hitl_tester.modules.bms_types import NiCdChargeCycle, DischargeType
 16from hitl_tester.modules.file_lock import FileLock
 17from hitl_tester.modules.logger import logger
 18from hitl_tester.modules.bms.plateset import Plateset
 19
 20SAMPLE_RATE = 10
 21"""How often to log data in seconds."""
 22CELL_COUNT: int | None = None
 23"""How many cells are attached."""
 24CAPACITY: float | None = None
 25"""Battery Capacity in mAh."""
 26
 27CHARGE_TYPE: NiCdChargeCycle | str = "UNDEFINED"
 28"""
 29The charge profile
 30- Standard: 0.1C for 16 hours
 31- DV_DT: 0.5C for 2.5 hours
 32- Custom: Current and time are defined separately by the user with CHARGE_RATE and CHARGE_TIME
 33"""
 34CHARGE_RATE: float | None = None
 35"""Charge current in mAh. **Optional:** only needed for custom charge type."""
 36CHARGE_TIME: float | None = None
 37"""How long to charge in seconds. **Optional:** only needed for custom charge type."""
 38
 39DISCHARGE_CURRENT: float | None = None
 40"""Current for discharge. **Optional:** only needed for constant current discharge."""
 41DISCHARGE_RESISTANCE: float | None = None
 42"""Resistance for discharge. **Optional:** only needed for constant resistance discharge."""
 43DISCHARGE_CURRENT_LIMIT: float = 10
 44"""Current limit for discharge. **Optional:** only needed for constant resistance discharge."""
 45DISCHARGE_VOLTAGE: float | None = None
 46"""The discharge voltage in volts."""
 47
 48RESTING_TIME: float | None = None
 49"""How long to rest before discharging in seconds."""
 50
 51bms_hardware = BMSHardware(pytest.flags)  # type: ignore[arg-type]
 52"""@private"""
 53bms_hardware.init()
 54plateset = Plateset()
 55"""@private"""
 56test_status_lock = FileLock("test_alive")
 57"""Tell other processes if this test is running or not"""
 58
 59
 60def test_nicd():
 61    """
 62    This test is ported from the older battery tester program.
 63    It performs a charge, rest, and discharge, then rests for 100 years.
 64    """
 65    global CHARGE_TYPE, CHARGE_RATE, CHARGE_TIME, DISCHARGE_CURRENT
 66
 67    test_status_lock.acquire()
 68
 69    if isinstance(CHARGE_TYPE, str):
 70        CHARGE_TYPE = NiCdChargeCycle[CHARGE_TYPE.upper()]
 71    if CHARGE_TYPE is NiCdChargeCycle.CUSTOM and (CHARGE_RATE is None or CHARGE_TIME is None):
 72        raise RuntimeError('CHARGE_TYPE "Custom" requires CHARGE_RATE and CHARGE_TIME to be defined.')
 73
 74    if CHARGE_TYPE is NiCdChargeCycle.STANDARD:
 75        CHARGE_RATE = CAPACITY / 10000  # 0.1C
 76        CHARGE_TIME = 57600  # 16 hours
 77    elif CHARGE_TYPE is NiCdChargeCycle.CUSTOM:
 78        CHARGE_RATE = CHARGE_RATE / 1000
 79    elif CHARGE_TYPE is NiCdChargeCycle.DV_DT:
 80        CHARGE_RATE = CAPACITY / 1000 / 2  # C/2 charge rate
 81        CHARGE_TIME = 3600 * 2.5  # 2.5 hour charging timeout
 82    elif CHARGE_TYPE is NiCdChargeCycle.UNDEFINED:
 83        raise RuntimeError("Charge type was undefined.")
 84
 85    if DISCHARGE_CURRENT is not None:
 86        DISCHARGE_CURRENT = DISCHARGE_CURRENT / 1000
 87        bms_hardware.discharge_type = DischargeType.CONSTANT_CURRENT
 88    elif DISCHARGE_RESISTANCE is not None:
 89        bms_hardware.discharge_type = DischargeType.CONSTANT_RESISTANCE
 90    else:
 91        raise RuntimeError("No discharge current/resistance provided.")
 92
 93    logger.write_info_to_report(f"Mode set to: {bms_hardware.discharge_type.name}")
 94    logger.write_info_to_report("")
 95    logger.write_info_to_report(f"{Fore.GREEN}Starting NiCd Charge Cycle...")
 96    logger.write_info_to_report("")
 97    bms_hardware.nicd_cell_count = CELL_COUNT
 98    bms_hardware.current = CHARGE_RATE
 99    bms_hardware.max_time = CHARGE_TIME
100    bms_hardware.sample_interval = SAMPLE_RATE
101    bms_hardware.nicd_charge_type = CHARGE_TYPE
102    charge_ah = bms_hardware.run_nicd_charge_cycle()
103    logger.write_info_to_report(f"{Fore.GREEN}Charge Added: {charge_ah} Ah")
104    logger.write_info_to_report("")
105    logger.write_info_to_report(f"{Fore.GREEN}Resting before discharge cycle...")
106    logger.write_info_to_report("")
107    bms_hardware.max_time = RESTING_TIME
108    bms_hardware.sample_interval = SAMPLE_RATE
109    bms_hardware.run_resting_cycle()
110    logger.write_info_to_report("")
111    logger.write_info_to_report(f"{Fore.GREEN}Starting Discharge Cycle...")
112    logger.write_info_to_report("")
113
114    bms_hardware.max_time = 86400  # 24 hours
115    bms_hardware.voltage = DISCHARGE_VOLTAGE
116    bms_hardware.current = DISCHARGE_CURRENT
117    bms_hardware.current_limit = DISCHARGE_CURRENT_LIMIT
118    bms_hardware.resistance = DISCHARGE_RESISTANCE
119    bms_hardware.uv_protection = max(bms_hardware.voltage - 1, 0.001)
120    bms_hardware.sample_interval = SAMPLE_RATE
121    discharge_ah = -1 * bms_hardware.run_discharge_cycle()
122    logger.write_info_to_report(f"{Fore.GREEN}Charge Removed: {discharge_ah} Ah")
123    logger.write_info_to_report("")
124    logger.write_info_to_report("-------------------------------------------------------------")
125    logger.write_info_to_report("")
126    logger.write_info_to_report(f"{Fore.GREEN}Test Completed!")
127    logger.write_info_to_report("")
128    logger.write_info_to_report(f"Charge Added to Battery: {charge_ah} Ah")
129    logger.write_info_to_report(f"Charge Removed from Battery: {discharge_ah} Ah")
130    logger.write_info_to_report("")
131    logger.write_info_to_report("Running infinite rest cycle!")
132    bms_hardware.max_time = 3156000000  # Rest for 100 years
133    bms_hardware.sample_interval = SAMPLE_RATE
134    bms_hardware.run_resting_cycle()
SAMPLE_RATE = 10

How often to log data in seconds.

CELL_COUNT: int | None = None

How many cells are attached.

CAPACITY: float | None = None

Battery Capacity in mAh.

CHARGE_TYPE: hitl_tester.modules.bms_types.NiCdChargeCycle | str = 'UNDEFINED'

The charge profile

  • Standard: 0.1C for 16 hours
  • DV_DT: 0.5C for 2.5 hours
  • Custom: Current and time are defined separately by the user with CHARGE_RATE and CHARGE_TIME
CHARGE_RATE: float | None = None

Charge current in mAh. Optional: only needed for custom charge type.

CHARGE_TIME: float | None = None

How long to charge in seconds. Optional: only needed for custom charge type.

DISCHARGE_CURRENT: float | None = None

Current for discharge. Optional: only needed for constant current discharge.

DISCHARGE_RESISTANCE: float | None = None

Resistance for discharge. Optional: only needed for constant resistance discharge.

DISCHARGE_CURRENT_LIMIT: float = 10

Current limit for discharge. Optional: only needed for constant resistance discharge.

DISCHARGE_VOLTAGE: float | None = None

The discharge voltage in volts.

RESTING_TIME: float | None = None

How long to rest before discharging in seconds.

test_status_lock = <hitl_tester.modules.file_lock.FileLock object>

Tell other processes if this test is running or not

def test_nicd():
 61def test_nicd():
 62    """
 63    This test is ported from the older battery tester program.
 64    It performs a charge, rest, and discharge, then rests for 100 years.
 65    """
 66    global CHARGE_TYPE, CHARGE_RATE, CHARGE_TIME, DISCHARGE_CURRENT
 67
 68    test_status_lock.acquire()
 69
 70    if isinstance(CHARGE_TYPE, str):
 71        CHARGE_TYPE = NiCdChargeCycle[CHARGE_TYPE.upper()]
 72    if CHARGE_TYPE is NiCdChargeCycle.CUSTOM and (CHARGE_RATE is None or CHARGE_TIME is None):
 73        raise RuntimeError('CHARGE_TYPE "Custom" requires CHARGE_RATE and CHARGE_TIME to be defined.')
 74
 75    if CHARGE_TYPE is NiCdChargeCycle.STANDARD:
 76        CHARGE_RATE = CAPACITY / 10000  # 0.1C
 77        CHARGE_TIME = 57600  # 16 hours
 78    elif CHARGE_TYPE is NiCdChargeCycle.CUSTOM:
 79        CHARGE_RATE = CHARGE_RATE / 1000
 80    elif CHARGE_TYPE is NiCdChargeCycle.DV_DT:
 81        CHARGE_RATE = CAPACITY / 1000 / 2  # C/2 charge rate
 82        CHARGE_TIME = 3600 * 2.5  # 2.5 hour charging timeout
 83    elif CHARGE_TYPE is NiCdChargeCycle.UNDEFINED:
 84        raise RuntimeError("Charge type was undefined.")
 85
 86    if DISCHARGE_CURRENT is not None:
 87        DISCHARGE_CURRENT = DISCHARGE_CURRENT / 1000
 88        bms_hardware.discharge_type = DischargeType.CONSTANT_CURRENT
 89    elif DISCHARGE_RESISTANCE is not None:
 90        bms_hardware.discharge_type = DischargeType.CONSTANT_RESISTANCE
 91    else:
 92        raise RuntimeError("No discharge current/resistance provided.")
 93
 94    logger.write_info_to_report(f"Mode set to: {bms_hardware.discharge_type.name}")
 95    logger.write_info_to_report("")
 96    logger.write_info_to_report(f"{Fore.GREEN}Starting NiCd Charge Cycle...")
 97    logger.write_info_to_report("")
 98    bms_hardware.nicd_cell_count = CELL_COUNT
 99    bms_hardware.current = CHARGE_RATE
100    bms_hardware.max_time = CHARGE_TIME
101    bms_hardware.sample_interval = SAMPLE_RATE
102    bms_hardware.nicd_charge_type = CHARGE_TYPE
103    charge_ah = bms_hardware.run_nicd_charge_cycle()
104    logger.write_info_to_report(f"{Fore.GREEN}Charge Added: {charge_ah} Ah")
105    logger.write_info_to_report("")
106    logger.write_info_to_report(f"{Fore.GREEN}Resting before discharge cycle...")
107    logger.write_info_to_report("")
108    bms_hardware.max_time = RESTING_TIME
109    bms_hardware.sample_interval = SAMPLE_RATE
110    bms_hardware.run_resting_cycle()
111    logger.write_info_to_report("")
112    logger.write_info_to_report(f"{Fore.GREEN}Starting Discharge Cycle...")
113    logger.write_info_to_report("")
114
115    bms_hardware.max_time = 86400  # 24 hours
116    bms_hardware.voltage = DISCHARGE_VOLTAGE
117    bms_hardware.current = DISCHARGE_CURRENT
118    bms_hardware.current_limit = DISCHARGE_CURRENT_LIMIT
119    bms_hardware.resistance = DISCHARGE_RESISTANCE
120    bms_hardware.uv_protection = max(bms_hardware.voltage - 1, 0.001)
121    bms_hardware.sample_interval = SAMPLE_RATE
122    discharge_ah = -1 * bms_hardware.run_discharge_cycle()
123    logger.write_info_to_report(f"{Fore.GREEN}Charge Removed: {discharge_ah} Ah")
124    logger.write_info_to_report("")
125    logger.write_info_to_report("-------------------------------------------------------------")
126    logger.write_info_to_report("")
127    logger.write_info_to_report(f"{Fore.GREEN}Test Completed!")
128    logger.write_info_to_report("")
129    logger.write_info_to_report(f"Charge Added to Battery: {charge_ah} Ah")
130    logger.write_info_to_report(f"Charge Removed from Battery: {discharge_ah} Ah")
131    logger.write_info_to_report("")
132    logger.write_info_to_report("Running infinite rest cycle!")
133    bms_hardware.max_time = 3156000000  # Rest for 100 years
134    bms_hardware.sample_interval = SAMPLE_RATE
135    bms_hardware.run_resting_cycle()

This test is ported from the older battery tester program. It performs a charge, rest, and discharge, then rests for 100 years.