/************************************************************************** * * Copyright (c) 2026 Diality Inc. - All Rights Reserved. * * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * @file BatteryMonitor.c * * @author (last) Suresh Dharnala * @date (last) 15-May-2026 * * @author (original) Suresh Dharnala * @date (original) 13-May-2026 * ***************************************************************************/ #include "BatteryMonitor.h" #include "Timers.h" #include "BatteryDriver.h" #include "PersistentAlarm.h" #include "Messaging.h" // ********** private definitions ********** #define BATTERY_CHARGER_SLAVE_ADDRESS 0x6B ///< Battery charger controller device address. #define BATTERY_PACK_SLAVE_ADDRESS 0x0B ///< Battery pack device address. #define BATTERY_CHARGER_STATUS1_CMD 0x21 ///< Battery charger status register 1 #define BATTERY_CHARGER_STATUS2_CMD 0x22 ///< Battery charger status register 2. #define BATTERY_CHARGER_FAULT_CMD 0x24 ///< Command to get battery charger fault. #define BATTERY_PACK_ERROR_BITS 0x0F ///< Error codes are in the first byte. #define BATTERY_MONITOR_INTERVAL_MS 247 ///< Battery monitor interval in ms. #define BATTERY_COMM_FAULT_COUNT 5 ///< Battery communication fault persistent count before alarming. #define BATTERY_COMM_FAULT_TIMER ( 10 * SEC_PER_MIN * MS_PER_SECOND ) ///< Battery communication fault persistence timer. // Battery logging #define BATTERY_CHARGER_ADDR_TS 0x37 ///< Battery thermistor ADC register. #define BATTERY_CHARGER_ADDR_VBAT 0x33 ///< Battery voltage ADC register. #define BATTERY_CHARGER_ADDR_VSYS 0x35 ///< Battery system voltage ADC register. #define BATTERY_CHARGER_ADDR_CURRENT 0x2F ///< Battery current ADC register. #define BATTERY_PACK_ADDR_RELATIVE_STATE_OF_CHARGE 0x0d ///< Battery pack address relative state of charge. #define BATTERY_PACK_ADDR_REMAINING_CAPACITY 0x0f ///< Battery pack address remaining capacity. #define BATTERY_PACK_ADDR_FULL_CHARGE_CAPACITY 0x10 ///< Battery pack address full charge capacity. #define BATTERY_PACK_ADDR_BATTERY_STATUS 0x16 ///< Battery pack address battery status. #define BATTERY_MIN_CAPACITY_MAH 1950.0F ///< Minimum battery capacity for starting a treatment. // ********** private data ********** /// Persist time (in ms) for battery pack status error condition. static const U32 BATT_STATUS_ERROR_PERSIST_MS = ( 5 * MS_PER_SECOND ); static U32 lastBatteryMonitorTime = 0; ///< Previous battery monitor time. static OVERRIDE_U32_T batteryStatus = { 0, 0, 0, 0 }; ///< Battery status static OVERRIDE_U32_T batteryChargerStatus = { 0, 0, 0, 0 }; ///< Battery charger status static OVERRIDE_U32_T batteryI2CStatusRegister = { 0, 0, 0, 0 }; ///< Battery I2C Interrupt Status register static OVERRIDE_F32_T batteryRemCapacity_mAh = { 0.0, 0.0, 0.0, 0 }; ///< Battery pack remaining capacity (in mAh). static BATTERY_MANAGEMENT_ENUM_T current_BM_value = BEGINNING_OF_LIST; ///< Index for which battery data to read now. static BATTERY_MANAGER_PAYLOAD_T BatteryManagerData; ///< Record with latest battery pack data updated at the slower frequency. static BATTERY_STATUS_PAYLOAD_T BatteryStatusData; ///< Record with latest battery pack/charger status data updated at the faster frequency. // ********** private function prototypes ********** static F32 getBatteryRemainingCapacity_mAh( void ); static U32 getBatteryStatus( void ); static U32 getBatteryChargerStatus( void ); // For logging static void getBatteryManagementData( void ); static void publishBatteryManagementData( void ); static void publishBatteryStatusData( void ); /*********************************************************************//** * @brief * The initBattery function initializes the Battery module. * @details Inputs: none * @details Outputs: Battery module is initialized. * @return none *************************************************************************/ void initBattery( void ) { memset( &BatteryStatusData, 0, sizeof( BATTERY_STATUS_PAYLOAD_T ) ); memset( &BatteryManagerData, 0, sizeof( BATTERY_MANAGER_PAYLOAD_T ) ); // Initialize persistent alarm for battery pack status error initPersistentAlarm( ALARM_ID_TD_BATTERY_PACK_ERROR_DETECTED, 0, BATT_STATUS_ERROR_PERSIST_MS ); // Initialize bad message CRC time windowed count initTimeWindowedCount( TIME_WINDOWED_COUNT_BATT_COMM_ERROR, BATTERY_COMM_FAULT_COUNT, BATTERY_COMM_FAULT_TIMER ); initsetupI2CDriver(); } /*********************************************************************//** * @brief * The execBatteryMonitor function monitors the battery status. * @details Inputs: lastBatteryMonitorTime * @details Outputs: lastBatteryMonitorTime * @return none *************************************************************************/ void execBatteryMonitor( void ) { if ( TRUE == didTimeout( lastBatteryMonitorTime, BATTERY_MONITOR_INTERVAL_MS ) ) { lastBatteryMonitorTime = getMSTimerCount(); getBatteryManagementData(); } } /*********************************************************************//** * @brief * The getBatteryChargerStatus function returns the latest battery * charger status. * @details Inputs: batteryChargerStatus * @details Outputs: none * @return battery charger status. *************************************************************************/ static U32 getBatteryChargerStatus( void ) { U32 result = batteryChargerStatus.data; if ( OVERRIDE_KEY == batteryChargerStatus.override ) { result = batteryChargerStatus.ovData; } return result; } /*********************************************************************//** * @brief * The getBatteryStatus function returns the latest battery status * @details Inputs: batteryStatus * @details Outputs: none * @return battery status. *************************************************************************/ static U32 getBatteryStatus( void ) { U32 result = batteryStatus.data; if ( OVERRIDE_KEY == batteryStatus.override ) { result = batteryStatus.ovData; } return result; } /*********************************************************************//** * @brief * The getBatteryRemainingCapacity_mAh function returns the latest battery * remaining capacity (in mAh). * @details Inputs: batteryRemCapacity_mAh * @details Outputs: none * @return battery remaining capacity (in mAh). *************************************************************************/ static F32 getBatteryRemainingCapacity_mAh( void ) { F32 result = batteryRemCapacity_mAh.data; if ( OVERRIDE_KEY == batteryRemCapacity_mAh.override ) { result = batteryRemCapacity_mAh.ovData; } return result; } /*********************************************************************//** * @brief * The getBatteryManagementData function accumulates the battery management * and status data and publishes the two sets of values when complete. Also, * the battery remaining capacity is stored, the battery status alarm is set * if necessary, and the loss of AC power alarm is set if necessary. * @details Inputs: current_BM_value, BatteryStatusData * @details Outputs: batteryRemCapacity_mAh, BatteryManagerData * @return none *************************************************************************/ static void getBatteryManagementData(void) { // Increment the position in the enum. Starting value is BEGINNING_OF_LIST (0), so on the first // cycle through this function it will be set to the first valid position (1) current_BM_value += 1; switch( current_BM_value ) { case BATTERY_PACK_REMAINING_CAPACITY1: case BATTERY_PACK_REMAINING_CAPACITY2: case BATTERY_PACK_REMAINING_CAPACITY3: case BATTERY_PACK_REMAINING_CAPACITY4: case BATTERY_PACK_REMAINING_CAPACITY5: getBatteryData ( BATTERY_PACK_SLAVE_ADDRESS, BATTERY_PACK_ADDR_REMAINING_CAPACITY, &BatteryStatusData.RemainingCapacity ); batteryRemCapacity_mAh.data = (F32)BatteryStatusData.RemainingCapacity; break; case BATTERY_PACK_BATTERY_STATUS1: case BATTERY_PACK_BATTERY_STATUS2: case BATTERY_PACK_BATTERY_STATUS3: case BATTERY_PACK_BATTERY_STATUS4: case BATTERY_PACK_BATTERY_STATUS5: getBatteryData ( BATTERY_PACK_SLAVE_ADDRESS, BATTERY_PACK_ADDR_BATTERY_STATUS, &BatteryStatusData.BatteryStatus ); batteryStatus.data = BatteryStatusData.BatteryStatus; if ( ( TRUE == isPersistentAlarmTriggered( ALARM_ID_TD_BATTERY_PACK_ERROR_DETECTED, ( getBatteryStatus() & BATTERY_PACK_ERROR_BITS ) != 0 ) ) ) { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_BATTERY_PACK_ERROR_DETECTED, getBatteryStatus(), BATTERY_PACK_ERROR_BITS ); } break; case BATTERY_CHARGER_TS: getBatteryData ( BATTERY_CHARGER_SLAVE_ADDRESS, BATTERY_CHARGER_ADDR_TS, &BatteryManagerData.TS ); BatteryManagerData.TS = (BatteryManagerData.TS * 1024.0f) / 100.0F; break; case BATTERY_CHARGER_VBAT: getBatteryData( BATTERY_CHARGER_SLAVE_ADDRESS, BATTERY_CHARGER_ADDR_VBAT, &BatteryManagerData.VBAT ); BatteryManagerData.VBAT = BatteryManagerData.VBAT * 2U; break; case BATTERY_CHARGER_VSYS: getBatteryData( BATTERY_CHARGER_SLAVE_ADDRESS, BATTERY_CHARGER_ADDR_VSYS, &BatteryManagerData.VSYS ); BatteryManagerData.VSYS = BatteryManagerData.VSYS * 2U; break; case BATTERY_CHARGER_FAULT: getBatteryData( BATTERY_CHARGER_SLAVE_ADDRESS, BATTERY_CHARGER_FAULT_CMD, &BatteryManagerData.FaultStatus ); break; case BATTERY_CHARGER_CURRENT: getBatteryData( BATTERY_CHARGER_SLAVE_ADDRESS, BATTERY_CHARGER_ADDR_CURRENT, &BatteryManagerData.IBAT ); BatteryManagerData.IBAT = BatteryManagerData.IBAT * 2U; break; case BATTERY_PACK_RELATIVE_STATE_OF_CHARGE: getBatteryData ( BATTERY_PACK_SLAVE_ADDRESS, BATTERY_PACK_ADDR_RELATIVE_STATE_OF_CHARGE, &BatteryManagerData.RelativeStateOfCharge ); break; case BATTERY_PACK_FULL_CHARGE_CAPACITY: getBatteryData ( BATTERY_PACK_SLAVE_ADDRESS, BATTERY_PACK_ADDR_FULL_CHARGE_CAPACITY, &BatteryManagerData.FullChargeCapacity ); break; case BATTERY_CHARGER_STATUS1: case BATTERY_CHARGER_STATUS2: case BATTERY_CHARGER_STATUS3: case BATTERY_CHARGER_STATUS4: case BATTERY_CHARGER_STATUS5: if ( TRUE == getBatteryData( BATTERY_CHARGER_SLAVE_ADDRESS, BATTERY_CHARGER_STATUS1_CMD, &BatteryStatusData.BatteryChargerStatus ) ) { batteryChargerStatus.data = BatteryStatusData.BatteryChargerStatus; } // Publish battery status data after reading battery charger status (last status read) publishBatteryStatusData(); break; case END_OF_LIST: current_BM_value = BEGINNING_OF_LIST; publishBatteryManagementData(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_BATTERY_INVALID_MANAGEMENT_DATA_STATE, current_BM_value ) break; } } /*********************************************************************//** * @brief * The isBatteryCharged function checks if the battery is charged. * @details Inputs: batteryRelStateOfCharge_pct * @details Outputs: none * @return TRUE if battery is charged, otherwise FALSE *************************************************************************/ BOOL isBatteryCharged( void ) { return ( getBatteryRemainingCapacity_mAh() > BATTERY_MIN_CAPACITY_MAH ? TRUE : FALSE ); } /*********************************************************************//** * @brief * The publishBatteryManagementData function publishes the battery management data * @details Inputs: BatteryManagerData * @details Outputs: send battery management data * @return none *************************************************************************/ static void publishBatteryManagementData( void ) { broadcastData( MSG_ID_TD_BATTERY_MANAGEMENT_DATA, COMM_BUFFER_OUT_CAN_TD_BROADCAST, (U08*)&BatteryManagerData, sizeof( BATTERY_MANAGER_PAYLOAD_T ) ); } /*********************************************************************//** * @brief * The publishBatteryStatusData function publishes the battery status data * @details Inputs: BatteryStatusData * @details Outputs: none * @return none *************************************************************************/ static void publishBatteryStatusData( void ) { BATTERY_STATUS_PAYLOAD_T batteryStatusData; batteryStatusData.RemainingCapacity = (U32)getBatteryRemainingCapacity_mAh(); batteryStatusData.BatteryStatus = getBatteryStatus(); batteryStatusData.BatteryChargerStatus = getBatteryChargerStatus(); batteryStatusData.BatteryI2CStatus = getI2CStatusRegister( FALSE ); broadcastData( MSG_ID_TD_BATTERY_STATUS_DATA, COMM_BUFFER_OUT_CAN_TD_BROADCAST, (U08*)&batteryStatusData, sizeof( BATTERY_STATUS_PAYLOAD_T ) ); } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetBatteryRemainingPercentOverride function overrides the battery * remaining percent value. * @details Inputs: none * @details Outputs: batteryRemCapacity_mAh * @param value override battery remaining percent * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetBatteryRemainingCapacityOverride( F32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; batteryRemCapacity_mAh.ovData = value; batteryRemCapacity_mAh.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetBatteryRemainingPercentOverride function resets the * override of the battery remaining percent value. * @details Inputs: none * @details Outputs: batteryRemCapacity_mAh * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetBatteryRemainingCapacityOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; batteryRemCapacity_mAh.override = OVERRIDE_RESET; batteryRemCapacity_mAh.ovData = batteryRemCapacity_mAh.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetBatteryStatusOverride function overrides the battery * status value. * @details Inputs: none * @details Outputs: batteryStatus * @param value override battery status * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetBatteryStatusOverride( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; batteryStatus.ovData = value; batteryStatus.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetBatteryStatusOverride function resets the * override of the battery status value. * @details Inputs: none * @details Outputs: batteryStatus * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetBatteryStatusOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; batteryStatus.override = OVERRIDE_RESET; batteryStatus.ovData = batteryStatus.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetBatteryChargerStatusOverride function overrides the battery * charger status value. * @details Inputs: none * @details Outputs: batteryChargerStatus * @param value override battery charge status * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetBatteryChargerStatusOverride( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; batteryChargerStatus.ovData = value; batteryChargerStatus.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetBatteryChargerStatusOverride function resets the * override of the battery charger status. * @details Inputs: none * @details Outputs: batteryChargerStatus * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetBatteryChargerStatusOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; batteryChargerStatus.override = OVERRIDE_RESET; batteryChargerStatus.ovData = batteryChargerStatus.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetBatteryI2CStatusOverride function overrides the battery * i2c status register value. * @details Inputs: none * @details Outputs: batteryI2CStatusRegister * @param value override battery charge status * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetBatteryI2CStatusOverride( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; batteryI2CStatusRegister.ovData = value; batteryI2CStatusRegister.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetBatteryI2CStatusOverride function resets the * override of the battery i2c status register value. * @details Inputs: none * @details Outputs: batteryI2CStatusRegister * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetBatteryI2CStatusOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; batteryI2CStatusRegister.override = OVERRIDE_RESET; batteryI2CStatusRegister.ovData = batteryI2CStatusRegister.ovInitData; } return result; }