/************************************************************************** * * 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 Battery.c * * @author (last) Suresh Dharnala * @date (last) 15-May-2026 * * @author (original) Suresh Dharnala * @date (original) 13-May-2026 * ***************************************************************************/ #include "Battery.h" #include "BatteryDriver.h" #include "Messaging.h" #include "PersistentAlarm.h" #include "Timers.h" /** * @addtogroup Battery * @{ */ // ********** private definitions ********** #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. #define BATTERY_PACK_ERROR_BITS 0x0F ///< Error codes are in the first byte. // Battery logging #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 ); ///< Persist time in ms for battery pack status error condition. static U32 lastBatteryMonitorTime; ///< Previous battery monitor time. static OVERRIDE_U32_T batteryStatus; ///< Battery status static OVERRIDE_U32_T batteryChargerStatus; ///< Battery charger status static OVERRIDE_U32_T batteryRemCapacity_mAh; ///< Battery pack remaining capacity (in mAh). static BATTERY_MANAGEMENT_ENUM_T current_BM_value; ///< Index for which battery data to read now. static BATTERY_DATA_PAYLOAD_T BatteryData; ///< Record with latest battery data. // ********** private function prototypes ********** static U32 getBatteryRemainingCapacity_mAh( void ); static U32 getBatteryStatus( void ); static U32 getBatteryChargerStatus( void ); static void getBatteryManagementData( void ); static void publishBatteryDataPayload( void ); /*********************************************************************//** * @brief * The initBattery function initializes the Battery monitor module. * @details \b Inputs: none * @details \b Outputs: Battery monitor module initialized. * @return none *************************************************************************/ void initBattery( void ) { initsetupI2CDriver(); current_BM_value = BEGINNING_OF_LIST; lastBatteryMonitorTime = 0; memset( &batteryStatus, 0, sizeof(OVERRIDE_U32_T)); memset( &batteryChargerStatus, 0, sizeof(OVERRIDE_U32_T)); memset( &batteryRemCapacity_mAh, 0, sizeof(OVERRIDE_U32_T)); memset( &BatteryData, 0, sizeof( BATTERY_DATA_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 ); } /*********************************************************************//** * @brief * The execBatteryMonitor function monitors the battery status by reading * battery data in a round robin fashion and publishing the results. * @details \b Inputs: lastBatteryMonitorTime * @details \b 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 \b Inputs: batteryChargerStatus * @details \b Outputs: none * @return latest battery charger status value. *************************************************************************/ 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 pack status. * @details \b Inputs: batteryStatus * @details \b Outputs: none * @return latest battery pack status value. *************************************************************************/ 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 \b Inputs: batteryRemCapacity_mAh * @details \b Outputs: none * @return latest battery remaining capacity in mAh. *************************************************************************/ static U32 getBatteryRemainingCapacity_mAh( void ) { U32 result = batteryRemCapacity_mAh.data; if ( OVERRIDE_KEY == batteryRemCapacity_mAh.override ) { result = batteryRemCapacity_mAh.ovData; } return result; } /*********************************************************************//** * @brief * The getBatteryManagementData function reads one battery data item per * call in a round-robin fashion and publishes the collected battery data * when the full list has been read. * @details \b Alarm: ALARM_ID_TD_BATTERY_PACK_ERROR_DETECTED if battery * pack status error bits are set. * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if an invalid battery * management data state is detected. * @details \b Inputs: current_BM_value, BatteryData * @details \b Outputs: current_BM_value, batteryRemCapacity_mAh, * batteryStatus, batteryChargerStatus, BatteryData * @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_CAPACITY: getBatteryData (BATTERY_PACK_ADDRESS, BATTERY_PACK_REMAINING_CAPACITY, &BatteryData.RemainingCapacity ); batteryRemCapacity_mAh.data = BatteryData.RemainingCapacity; break; case BATTERY_PACK_BATTERY_STATUS: getBatteryData ( BATTERY_PACK_ADDRESS, BATTERY_PACK_BATTERY_STATUS, &BatteryData.BatteryStatus ); batteryStatus.data = BatteryData.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_PACK_RELATIVE_STATE_OF_CHARGE: getBatteryData ( BATTERY_PACK_ADDRESS, BATTERY_PACK_RELATIVE_STATE_OF_CHARGE, &BatteryData.RelativeStateOfCharge ); break; case BATTERY_PACK_FULL_CHARGE_CAPACITY: getBatteryData ( BATTERY_PACK_ADDRESS, BATTERY_PACK_FULL_CHARGE_CAPACITY, &BatteryData.FullChargeCapacity ); break; case BATTERY_CHARGER_STATUS: if ( TRUE == getBatteryData( BATTERY_CHARGER_ADDRESS, BATTERY_CHARGER_STATUS, &BatteryData.BatteryChargerStatus ) ) { batteryChargerStatus.data = BatteryData.BatteryChargerStatus; } break; case BATTERY_CHARGER_TS: getBatteryData ( BATTERY_PACK_ADDRESS, BATTERY_CHARGER_TS, &BatteryData.TS ); BatteryData.TS = (BatteryData.TS * 1024.0f) / 100.0F; break; case BATTERY_CHARGER_VBAT: getBatteryData( BATTERY_PACK_ADDRESS, BATTERY_CHARGER_VBAT, &BatteryData.VBAT ); BatteryData.VBAT = BatteryData.VBAT * 2U; break; case BATTERY_CHARGER_VSYS: getBatteryData( BATTERY_CHARGER_ADDRESS, BATTERY_CHARGER_VSYS, &BatteryData.VSYS ); BatteryData.VSYS = BatteryData.VSYS * 2U; break; case BATTERY_CHARGER_FAULT: getBatteryData( BATTERY_CHARGER_ADDRESS, BATTERY_CHARGER_FAULT, &BatteryData.FaultStatus ); break; case BATTERY_CHARGER_CURRENT: getBatteryData( BATTERY_PACK_ADDRESS, BATTERY_CHARGER_CURRENT, &BatteryData.IBAT ); BatteryData.IBAT = BatteryData.IBAT * 2U; break; case END_OF_LIST: current_BM_value = BEGINNING_OF_LIST; publishBatteryDataPayload(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_BATTERY_MANAGEMENT_DATA_STATE, current_BM_value ) break; } } /*********************************************************************//** * @brief * The isBatteryCharged function checks if the battery has sufficient * charge to allow starting a treatment. * @details \b Inputs: batteryRemCapacity_mAh * @details \b Outputs: none * @return TRUE if battery has sufficient charge, otherwise FALSE *************************************************************************/ BOOL isBatteryCharged( void ) { return ( getBatteryRemainingCapacity_mAh() > BATTERY_MIN_CAPACITY_MAH ? TRUE : FALSE ); } /*********************************************************************//** * @brief * The publishBatteryDataPayload function updates and publishes the battery * data payload over CAN. * @details \b Message \b Sent: MSG_ID_TD_BATTERY_DATA * @details \b Inputs: BatteryData, batteryRemCapacity_mAh, batteryStatus, * batteryChargerStatus, batteryI2CStatusRegister * @details \b Outputs: BatteryData * @return none *************************************************************************/ static void publishBatteryDataPayload( void ) { BatteryData.RemainingCapacity = getBatteryRemainingCapacity_mAh(); BatteryData.BatteryStatus = getBatteryStatus(); BatteryData.BatteryChargerStatus = getBatteryChargerStatus(); BatteryData.BatteryI2CStatus = getI2CStatusRegister( FALSE ); broadcastData( MSG_ID_TD_BATTERY_DATA, COMM_BUFFER_OUT_CAN_TD_BROADCAST, (U08*)&BatteryData, sizeof( BATTERY_DATA_PAYLOAD_T ) ); } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testBatteryRemainingCapacityOverride function overrides the battery * remaining capacity value. * @details \b Inputs: none * @details \b Outputs: batteryRemCapacity_mAh * @param message override message from Diamond which includes the * remaining capacity value to override to. * @return TRUE if override request is successful, FALSE if not *************************************************************************/ BOOL testBatteryRemainingCapacityOverride( MESSAGE_T *message ) { BOOL result = f32Override( message, (OVERRIDE_F32_T*) &batteryRemCapacity_mAh ); return result; } /*********************************************************************//** * @brief * The testBatteryStatusOverride function overrides the battery * pack status value. * @details \b Inputs: none * @details \b Outputs: batteryStatus * @param message override message from Diamond which includes the * battery status value to override to. * @return TRUE if override request is successful, FALSE if not *************************************************************************/ BOOL testBatteryStatusOverride( MESSAGE_T *message ) { BOOL result = f32Override( message, (OVERRIDE_F32_T*) &batteryStatus ); return result; } /*********************************************************************//** * @brief * The testBatteryChargerStatusOverride function overrides the battery * charger status value. * @details \b Inputs: none * @details \b Outputs: batteryChargerStatus * @param message override message from Diamond which includes the * charger status value to override to. * @return TRUE if override request is successful, FALSE if not *************************************************************************/ BOOL testBatteryChargerStatusOverride( MESSAGE_T *message ) { BOOL result = f32Override( message, (OVERRIDE_F32_T*)&batteryChargerStatus ); return result; }