Path: blob/master/libraries/AP_BattMonitor/AP_BattMonitor.cpp
9386 views
#include "AP_BattMonitor_config.h"12#if AP_BATTERY_ENABLED34#include "AP_BattMonitor.h"56#include "AP_BattMonitor_Analog.h"7#include "AP_BattMonitor_SMBus.h"8#include "AP_BattMonitor_SMBus_Solo.h"9#include "AP_BattMonitor_SMBus_Generic.h"10#include "AP_BattMonitor_SMBus_Maxell.h"11#include "AP_BattMonitor_SMBus_Rotoye.h"12#include "AP_BattMonitor_Bebop.h"13#include "AP_BattMonitor_ESC.h"14#include "AP_BattMonitor_SMBus_SUI.h"15#include "AP_BattMonitor_SMBus_NeoDesign.h"16#include "AP_BattMonitor_Sum.h"17#include "AP_BattMonitor_FuelFlow.h"18#include "AP_BattMonitor_FuelLevel_PWM.h"19#include "AP_BattMonitor_Generator.h"20#include "AP_BattMonitor_EFI.h"21#include "AP_BattMonitor_INA2xx.h"22#include "AP_BattMonitor_INA239.h"23#include "AP_BattMonitor_INA3221.h"24#include "AP_BattMonitor_LTC2946.h"25#include "AP_BattMonitor_Torqeedo.h"26#include "AP_BattMonitor_FuelLevel_Analog.h"27#include "AP_BattMonitor_Synthetic_Current.h"28#include "AP_BattMonitor_AD7091R5.h"29#include "AP_BattMonitor_Scripting.h"30#include "AP_BattMonitor_TIBQ76952.h"3132#include <AP_HAL/AP_HAL.h>3334#if HAL_ENABLE_DRONECAN_DRIVERS35#include "AP_BattMonitor_DroneCAN.h"36#endif3738#include <AP_Vehicle/AP_Vehicle_Type.h>39#include <AP_Logger/AP_Logger.h>40#include <GCS_MAVLink/GCS.h>41#include <AP_Notify/AP_Notify.h>4243extern const AP_HAL::HAL& hal;4445AP_BattMonitor *AP_BattMonitor::_singleton;4647const AP_Param::GroupInfo AP_BattMonitor::var_info[] = {48// 0 - 18, 20- 22 used by old parameter indexes4950// Monitor 15152// @Group: _53// @Path: AP_BattMonitor_Params.cpp54AP_SUBGROUPINFO(_params[0], "_", 23, AP_BattMonitor, AP_BattMonitor_Params),5556// @Group: _57// @Path: AP_BattMonitor_Analog.cpp58// @Group: _59// @Path: AP_BattMonitor_SMBus.cpp60// @Group: _61// @Path: AP_BattMonitor_Sum.cpp62// @Group: _63// @Path: AP_BattMonitor_DroneCAN.cpp64// @Group: _65// @Path: AP_BattMonitor_FuelLevel_Analog.cpp66// @Group: _67// @Path: AP_BattMonitor_Synthetic_Current.cpp68// @Group: _69// @Path: AP_BattMonitor_INA2xx.cpp70// @Group: _71// @Path: AP_BattMonitor_ESC.cpp72// @Group: _73// @Path: AP_BattMonitor_INA239.cpp74// @Group: _75// @Path: AP_BattMonitor_INA3221.cpp76// @Group: _77// @Path: AP_BattMonitor_AD7091R5.cpp78AP_SUBGROUPVARPTR(drivers[0], "_", 41, AP_BattMonitor, backend_var_info[0]),7980#if AP_BATT_MONITOR_MAX_INSTANCES > 181// @Group: 2_82// @Path: AP_BattMonitor_Params.cpp83AP_SUBGROUPINFO(_params[1], "2_", 24, AP_BattMonitor, AP_BattMonitor_Params),8485// @Group: 2_86// @Path: AP_BattMonitor_Analog.cpp87// @Group: 2_88// @Path: AP_BattMonitor_SMBus.cpp89// @Group: 2_90// @Path: AP_BattMonitor_Sum.cpp91// @Group: 2_92// @Path: AP_BattMonitor_DroneCAN.cpp93// @Group: 2_94// @Path: AP_BattMonitor_FuelLevel_Analog.cpp95// @Group: 2_96// @Path: AP_BattMonitor_Synthetic_Current.cpp97// @Group: 2_98// @Path: AP_BattMonitor_INA2xx.cpp99// @Group: 2_100// @Path: AP_BattMonitor_ESC.cpp101// @Group: 2_102// @Path: AP_BattMonitor_INA239.cpp103// @Group: 2_104// @Path: AP_BattMonitor_INA3221.cpp105// @Group: 2_106// @Path: AP_BattMonitor_AD7091R5.cpp107AP_SUBGROUPVARPTR(drivers[1], "2_", 42, AP_BattMonitor, backend_var_info[1]),108#endif109110#if AP_BATT_MONITOR_MAX_INSTANCES > 2111// @Group: 3_112// @Path: AP_BattMonitor_Params.cpp113AP_SUBGROUPINFO(_params[2], "3_", 25, AP_BattMonitor, AP_BattMonitor_Params),114115// @Group: 3_116// @Path: AP_BattMonitor_Analog.cpp117// @Group: 3_118// @Path: AP_BattMonitor_SMBus.cpp119// @Group: 3_120// @Path: AP_BattMonitor_Sum.cpp121// @Group: 3_122// @Path: AP_BattMonitor_DroneCAN.cpp123// @Group: 3_124// @Path: AP_BattMonitor_FuelLevel_Analog.cpp125// @Group: 3_126// @Path: AP_BattMonitor_Synthetic_Current.cpp127// @Group: 3_128// @Path: AP_BattMonitor_INA2xx.cpp129// @Group: 3_130// @Path: AP_BattMonitor_ESC.cpp131// @Group: 3_132// @Path: AP_BattMonitor_INA239.cpp133// @Group: 3_134// @Path: AP_BattMonitor_INA3221.cpp135// @Group: 3_136// @Path: AP_BattMonitor_AD7091R5.cpp137AP_SUBGROUPVARPTR(drivers[2], "3_", 43, AP_BattMonitor, backend_var_info[2]),138#endif139140#if AP_BATT_MONITOR_MAX_INSTANCES > 3141// @Group: 4_142// @Path: AP_BattMonitor_Params.cpp143AP_SUBGROUPINFO(_params[3], "4_", 26, AP_BattMonitor, AP_BattMonitor_Params),144145// @Group: 4_146// @Path: AP_BattMonitor_Analog.cpp147// @Group: 4_148// @Path: AP_BattMonitor_SMBus.cpp149// @Group: 4_150// @Path: AP_BattMonitor_Sum.cpp151// @Group: 4_152// @Path: AP_BattMonitor_DroneCAN.cpp153// @Group: 4_154// @Path: AP_BattMonitor_FuelLevel_Analog.cpp155// @Group: 4_156// @Path: AP_BattMonitor_Synthetic_Current.cpp157// @Group: 4_158// @Path: AP_BattMonitor_INA2xx.cpp159// @Group: 4_160// @Path: AP_BattMonitor_ESC.cpp161// @Group: 4_162// @Path: AP_BattMonitor_INA239.cpp163// @Group: 4_164// @Path: AP_BattMonitor_INA3221.cpp165// @Group: 4_166// @Path: AP_BattMonitor_AD7091R5.cpp167AP_SUBGROUPVARPTR(drivers[3], "4_", 44, AP_BattMonitor, backend_var_info[3]),168#endif169170#if AP_BATT_MONITOR_MAX_INSTANCES > 4171// @Group: 5_172// @Path: AP_BattMonitor_Params.cpp173AP_SUBGROUPINFO(_params[4], "5_", 27, AP_BattMonitor, AP_BattMonitor_Params),174175// @Group: 5_176// @Path: AP_BattMonitor_Analog.cpp177// @Group: 5_178// @Path: AP_BattMonitor_SMBus.cpp179// @Group: 5_180// @Path: AP_BattMonitor_Sum.cpp181// @Group: 5_182// @Path: AP_BattMonitor_DroneCAN.cpp183// @Group: 5_184// @Path: AP_BattMonitor_FuelLevel_Analog.cpp185// @Group: 5_186// @Path: AP_BattMonitor_Synthetic_Current.cpp187// @Group: 5_188// @Path: AP_BattMonitor_INA2xx.cpp189// @Group: 5_190// @Path: AP_BattMonitor_ESC.cpp191// @Group: 5_192// @Path: AP_BattMonitor_INA239.cpp193// @Group: 5_194// @Path: AP_BattMonitor_INA3221.cpp195// @Group: 5_196// @Path: AP_BattMonitor_AD7091R5.cpp197AP_SUBGROUPVARPTR(drivers[4], "5_", 45, AP_BattMonitor, backend_var_info[4]),198#endif199200#if AP_BATT_MONITOR_MAX_INSTANCES > 5201// @Group: 6_202// @Path: AP_BattMonitor_Params.cpp203AP_SUBGROUPINFO(_params[5], "6_", 28, AP_BattMonitor, AP_BattMonitor_Params),204205// @Group: 6_206// @Path: AP_BattMonitor_Analog.cpp207// @Group: 6_208// @Path: AP_BattMonitor_SMBus.cpp209// @Group: 6_210// @Path: AP_BattMonitor_Sum.cpp211// @Group: 6_212// @Path: AP_BattMonitor_DroneCAN.cpp213// @Group: 6_214// @Path: AP_BattMonitor_FuelLevel_Analog.cpp215// @Group: 6_216// @Path: AP_BattMonitor_Synthetic_Current.cpp217// @Group: 6_218// @Path: AP_BattMonitor_INA2xx.cpp219// @Group: 6_220// @Path: AP_BattMonitor_ESC.cpp221// @Group: 6_222// @Path: AP_BattMonitor_INA239.cpp223// @Group: 6_224// @Path: AP_BattMonitor_INA3221.cpp225// @Group: 6_226// @Path: AP_BattMonitor_AD7091R5.cpp227AP_SUBGROUPVARPTR(drivers[5], "6_", 46, AP_BattMonitor, backend_var_info[5]),228#endif229230#if AP_BATT_MONITOR_MAX_INSTANCES > 6231// @Group: 7_232// @Path: AP_BattMonitor_Params.cpp233AP_SUBGROUPINFO(_params[6], "7_", 29, AP_BattMonitor, AP_BattMonitor_Params),234235// @Group: 7_236// @Path: AP_BattMonitor_Analog.cpp237// @Group: 7_238// @Path: AP_BattMonitor_SMBus.cpp239// @Group: 7_240// @Path: AP_BattMonitor_Sum.cpp241// @Group: 7_242// @Path: AP_BattMonitor_DroneCAN.cpp243// @Group: 7_244// @Path: AP_BattMonitor_FuelLevel_Analog.cpp245// @Group: 7_246// @Path: AP_BattMonitor_Synthetic_Current.cpp247// @Group: 7_248// @Path: AP_BattMonitor_INA2xx.cpp249// @Group: 7_250// @Path: AP_BattMonitor_ESC.cpp251// @Group: 7_252// @Path: AP_BattMonitor_INA239.cpp253// @Group: 7_254// @Path: AP_BattMonitor_INA3221.cpp255// @Group: 7_256// @Path: AP_BattMonitor_AD7091R5.cpp257AP_SUBGROUPVARPTR(drivers[6], "7_", 47, AP_BattMonitor, backend_var_info[6]),258#endif259260#if AP_BATT_MONITOR_MAX_INSTANCES > 7261// @Group: 8_262// @Path: AP_BattMonitor_Params.cpp263AP_SUBGROUPINFO(_params[7], "8_", 30, AP_BattMonitor, AP_BattMonitor_Params),264265// @Group: 8_266// @Path: AP_BattMonitor_Analog.cpp267// @Group: 8_268// @Path: AP_BattMonitor_SMBus.cpp269// @Group: 8_270// @Path: AP_BattMonitor_Sum.cpp271// @Group: 8_272// @Path: AP_BattMonitor_DroneCAN.cpp273// @Group: 8_274// @Path: AP_BattMonitor_FuelLevel_Analog.cpp275// @Group: 8_276// @Path: AP_BattMonitor_Synthetic_Current.cpp277// @Group: 8_278// @Path: AP_BattMonitor_INA2xx.cpp279// @Group: 8_280// @Path: AP_BattMonitor_ESC.cpp281// @Group: 8_282// @Path: AP_BattMonitor_INA239.cpp283// @Group: 8_284// @Path: AP_BattMonitor_INA3221.cpp285// @Group: 8_286// @Path: AP_BattMonitor_AD7091R5.cpp287AP_SUBGROUPVARPTR(drivers[7], "8_", 48, AP_BattMonitor, backend_var_info[7]),288#endif289290#if AP_BATT_MONITOR_MAX_INSTANCES > 8291// @Group: 9_292// @Path: AP_BattMonitor_Params.cpp293AP_SUBGROUPINFO(_params[8], "9_", 31, AP_BattMonitor, AP_BattMonitor_Params),294295// @Group: 9_296// @Path: AP_BattMonitor_Analog.cpp297// @Group: 9_298// @Path: AP_BattMonitor_SMBus.cpp299// @Group: 9_300// @Path: AP_BattMonitor_Sum.cpp301// @Group: 9_302// @Path: AP_BattMonitor_DroneCAN.cpp303// @Group: 9_304// @Path: AP_BattMonitor_FuelLevel_Analog.cpp305// @Group: 9_306// @Path: AP_BattMonitor_Synthetic_Current.cpp307// @Group: 9_308// @Path: AP_BattMonitor_INA2xx.cpp309// @Group: 9_310// @Path: AP_BattMonitor_ESC.cpp311// @Group: 9_312// @Path: AP_BattMonitor_INA239.cpp313// @Group: 9_314// @Path: AP_BattMonitor_INA3221.cpp315// @Group: 9_316// @Path: AP_BattMonitor_AD7091R5.cpp317AP_SUBGROUPVARPTR(drivers[8], "9_", 49, AP_BattMonitor, backend_var_info[8]),318#endif319320#if AP_BATT_MONITOR_MAX_INSTANCES > 9321// @Group: A_322// @Path: AP_BattMonitor_Params.cpp323AP_SUBGROUPINFO(_params[9], "A_", 32, AP_BattMonitor, AP_BattMonitor_Params),324325// @Group: A_326// @Path: AP_BattMonitor_Analog.cpp327// @Group: A_328// @Path: AP_BattMonitor_SMBus.cpp329// @Group: A_330// @Path: AP_BattMonitor_Sum.cpp331// @Group: A_332// @Path: AP_BattMonitor_DroneCAN.cpp333// @Group: A_334// @Path: AP_BattMonitor_FuelLevel_Analog.cpp335// @Group: A_336// @Path: AP_BattMonitor_Synthetic_Current.cpp337// @Group: A_338// @Path: AP_BattMonitor_INA2xx.cpp339// @Group: A_340// @Path: AP_BattMonitor_ESC.cpp341// @Group: A_342// @Path: AP_BattMonitor_INA239.cpp343// @Group: A_344// @Path: AP_BattMonitor_INA3221.cpp345// @Group: A_346// @Path: AP_BattMonitor_AD7091R5.cpp347AP_SUBGROUPVARPTR(drivers[9], "A_", 50, AP_BattMonitor, backend_var_info[9]),348#endif349350#if AP_BATT_MONITOR_MAX_INSTANCES > 10351// @Group: B_352// @Path: AP_BattMonitor_Params.cpp353AP_SUBGROUPINFO(_params[10], "B_", 33, AP_BattMonitor, AP_BattMonitor_Params),354355// @Group: B_356// @Path: AP_BattMonitor_Analog.cpp357// @Group: B_358// @Path: AP_BattMonitor_SMBus.cpp359// @Group: B_360// @Path: AP_BattMonitor_Sum.cpp361// @Group: B_362// @Path: AP_BattMonitor_DroneCAN.cpp363// @Group: B_364// @Path: AP_BattMonitor_FuelLevel_Analog.cpp365// @Group: B_366// @Path: AP_BattMonitor_Synthetic_Current.cpp367// @Group: B_368// @Path: AP_BattMonitor_INA2xx.cpp369// @Group: B_370// @Path: AP_BattMonitor_ESC.cpp371// @Group: B_372// @Path: AP_BattMonitor_INA239.cpp373// @Group: B_374// @Path: AP_BattMonitor_INA3221.cpp375// @Group: B_376// @Path: AP_BattMonitor_AD7091R5.cpp377AP_SUBGROUPVARPTR(drivers[10], "B_", 51, AP_BattMonitor, backend_var_info[10]),378#endif379380#if AP_BATT_MONITOR_MAX_INSTANCES > 11381// @Group: C_382// @Path: AP_BattMonitor_Params.cpp383AP_SUBGROUPINFO(_params[11], "C_", 34, AP_BattMonitor, AP_BattMonitor_Params),384385// @Group: C_386// @Path: AP_BattMonitor_Analog.cpp387// @Group: C_388// @Path: AP_BattMonitor_SMBus.cpp389// @Group: C_390// @Path: AP_BattMonitor_Sum.cpp391// @Group: C_392// @Path: AP_BattMonitor_DroneCAN.cpp393// @Group: C_394// @Path: AP_BattMonitor_FuelLevel_Analog.cpp395// @Group: C_396// @Path: AP_BattMonitor_Synthetic_Current.cpp397// @Group: C_398// @Path: AP_BattMonitor_INA2xx.cpp399// @Group: C_400// @Path: AP_BattMonitor_ESC.cpp401// @Group: C_402// @Path: AP_BattMonitor_INA239.cpp403// @Group: C_404// @Path: AP_BattMonitor_INA3221.cpp405// @Group: C_406// @Path: AP_BattMonitor_AD7091R5.cpp407AP_SUBGROUPVARPTR(drivers[11], "C_", 52, AP_BattMonitor, backend_var_info[11]),408#endif409410#if AP_BATT_MONITOR_MAX_INSTANCES > 12411// @Group: D_412// @Path: AP_BattMonitor_Params.cpp413AP_SUBGROUPINFO(_params[12], "D_", 35, AP_BattMonitor, AP_BattMonitor_Params),414415// @Group: D_416// @Path: AP_BattMonitor_Analog.cpp417// @Group: D_418// @Path: AP_BattMonitor_SMBus.cpp419// @Group: D_420// @Path: AP_BattMonitor_Sum.cpp421// @Group: D_422// @Path: AP_BattMonitor_DroneCAN.cpp423// @Group: D_424// @Path: AP_BattMonitor_FuelLevel_Analog.cpp425// @Group: D_426// @Path: AP_BattMonitor_Synthetic_Current.cpp427// @Group: D_428// @Path: AP_BattMonitor_INA2xx.cpp429// @Group: D_430// @Path: AP_BattMonitor_ESC.cpp431// @Group: D_432// @Path: AP_BattMonitor_INA239.cpp433// @Group: D_434// @Path: AP_BattMonitor_INA3221.cpp435// @Group: D_436// @Path: AP_BattMonitor_AD7091R5.cpp437AP_SUBGROUPVARPTR(drivers[12], "D_", 53, AP_BattMonitor, backend_var_info[12]),438#endif439440#if AP_BATT_MONITOR_MAX_INSTANCES > 13441// @Group: E_442// @Path: AP_BattMonitor_Params.cpp443AP_SUBGROUPINFO(_params[13], "E_", 36, AP_BattMonitor, AP_BattMonitor_Params),444445// @Group: E_446// @Path: AP_BattMonitor_Analog.cpp447// @Group: E_448// @Path: AP_BattMonitor_SMBus.cpp449// @Group: E_450// @Path: AP_BattMonitor_Sum.cpp451// @Group: E_452// @Path: AP_BattMonitor_DroneCAN.cpp453// @Group: E_454// @Path: AP_BattMonitor_FuelLevel_Analog.cpp455// @Group: E_456// @Path: AP_BattMonitor_Synthetic_Current.cpp457// @Group: E_458// @Path: AP_BattMonitor_INA2xx.cpp459// @Group: E_460// @Path: AP_BattMonitor_ESC.cpp461// @Group: E_462// @Path: AP_BattMonitor_INA239.cpp463// @Group: E_464// @Path: AP_BattMonitor_INA3221.cpp465// @Group: E_466// @Path: AP_BattMonitor_AD7091R5.cpp467AP_SUBGROUPVARPTR(drivers[13], "E_", 54, AP_BattMonitor, backend_var_info[13]),468#endif469470#if AP_BATT_MONITOR_MAX_INSTANCES > 14471// @Group: F_472// @Path: AP_BattMonitor_Params.cpp473AP_SUBGROUPINFO(_params[14], "F_", 37, AP_BattMonitor, AP_BattMonitor_Params),474475// @Group: F_476// @Path: AP_BattMonitor_Analog.cpp477// @Group: F_478// @Path: AP_BattMonitor_SMBus.cpp479// @Group: F_480// @Path: AP_BattMonitor_Sum.cpp481// @Group: F_482// @Path: AP_BattMonitor_DroneCAN.cpp483// @Group: F_484// @Path: AP_BattMonitor_FuelLevel_Analog.cpp485// @Group: F_486// @Path: AP_BattMonitor_Synthetic_Current.cpp487// @Group: F_488// @Path: AP_BattMonitor_INA2xx.cpp489// @Group: F_490// @Path: AP_BattMonitor_ESC.cpp491// @Group: F_492// @Path: AP_BattMonitor_INA239.cpp493// @Group: F_494// @Path: AP_BattMonitor_INA3221.cpp495// @Group: F_496// @Path: AP_BattMonitor_AD7091R5.cpp497AP_SUBGROUPVARPTR(drivers[14], "F_", 55, AP_BattMonitor, backend_var_info[14]),498#endif499500#if AP_BATT_MONITOR_MAX_INSTANCES > 15501// @Group: G_502// @Path: AP_BattMonitor_Params.cpp503AP_SUBGROUPINFO(_params[15], "G_", 38, AP_BattMonitor, AP_BattMonitor_Params),504505// @Group: G_506// @Path: AP_BattMonitor_Analog.cpp507// @Group: G_508// @Path: AP_BattMonitor_SMBus.cpp509// @Group: G_510// @Path: AP_BattMonitor_Sum.cpp511// @Group: G_512// @Path: AP_BattMonitor_DroneCAN.cpp513// @Group: G_514// @Path: AP_BattMonitor_FuelLevel_Analog.cpp515// @Group: G_516// @Path: AP_BattMonitor_Synthetic_Current.cpp517// @Group: G_518// @Path: AP_BattMonitor_INA2xx.cpp519// @Group: G_520// @Path: AP_BattMonitor_ESC.cpp521// @Group: G_522// @Path: AP_BattMonitor_INA239.cpp523// @Group: G_524// @Path: AP_BattMonitor_INA3221.cpp525// @Group: G_526// @Path: AP_BattMonitor_AD7091R5.cpp527AP_SUBGROUPVARPTR(drivers[15], "G_", 56, AP_BattMonitor, backend_var_info[15]),528#endif529530#if AP_BATT_MONITOR_MAX_INSTANCES > 16531#error "AP_BATT_MONITOR_MAX_INSTANCES too large, reset_remaining_mask() will cause an assert above 16"532#endif533534AP_GROUPEND535};536537const AP_Param::GroupInfo *AP_BattMonitor::backend_var_info[AP_BATT_MONITOR_MAX_INSTANCES];538539// Default constructor.540// Note that the Vector/Matrix constructors already implicitly zero541// their values.542//543AP_BattMonitor::AP_BattMonitor(uint32_t log_battery_bit, battery_failsafe_handler_fn_t battery_failsafe_handler_fn, const int8_t *failsafe_priorities) :544_log_battery_bit(log_battery_bit),545_battery_failsafe_handler_fn(battery_failsafe_handler_fn),546_failsafe_priorities(failsafe_priorities)547{548AP_Param::setup_object_defaults(this, var_info);549550if (_singleton != nullptr) {551AP_HAL::panic("AP_BattMonitor must be singleton");552}553_singleton = this;554}555556// init - instantiate the battery monitors557void558AP_BattMonitor::init()559{560// check init has not been called before561if (_num_instances != 0) {562return;563}564565_highest_failsafe_priority = INT8_MAX;566567#ifdef HAL_BATT_MONITOR_DEFAULT568_params[0]._type.set_default(int8_t(HAL_BATT_MONITOR_DEFAULT));569#endif570#ifdef HAL_BATT2_MONITOR_DEFAULT571_params[1]._type.set_default(int8_t(HAL_BATT2_MONITOR_DEFAULT));572#endif573574// create each instance575for (uint8_t instance=0; instance<AP_BATT_MONITOR_MAX_INSTANCES; instance++) {576// clear out the cell voltages577memset(&state[instance].cell_voltages, 0xFF, sizeof(cells));578state[instance].instance = instance;579580const auto allocation_type = configured_type(instance);581switch (allocation_type) {582#if AP_BATTERY_ANALOG_ENABLED583case Type::ANALOG_VOLTAGE_ONLY:584case Type::ANALOG_VOLTAGE_AND_CURRENT:585case Type::ANALOG_CURRENT_ONLY:586drivers[instance] = NEW_NOTHROW AP_BattMonitor_Analog(*this, state[instance], _params[instance]);587break;588#endif589#if AP_BATTERY_SMBUS_SOLO_ENABLED590case Type::SOLO:591drivers[instance] = NEW_NOTHROW AP_BattMonitor_SMBus_Solo(*this, state[instance], _params[instance]);592break;593#endif594#if AP_BATTERY_SMBUS_GENERIC_ENABLED595case Type::SMBus_Generic:596drivers[instance] = NEW_NOTHROW AP_BattMonitor_SMBus_Generic(*this, state[instance], _params[instance]);597break;598#endif599#if AP_BATTERY_SMBUS_SUI_ENABLED600case Type::SUI3:601drivers[instance] = NEW_NOTHROW AP_BattMonitor_SMBus_SUI(*this, state[instance], _params[instance], 3);602break;603case Type::SUI6:604drivers[instance] = NEW_NOTHROW AP_BattMonitor_SMBus_SUI(*this, state[instance], _params[instance], 6);605break;606#endif607#if AP_BATTERY_SMBUS_MAXELL_ENABLED608case Type::MAXELL:609drivers[instance] = NEW_NOTHROW AP_BattMonitor_SMBus_Maxell(*this, state[instance], _params[instance]);610break;611#endif612#if AP_BATTERY_SMBUS_ROTOYE_ENABLED613case Type::Rotoye:614drivers[instance] = NEW_NOTHROW AP_BattMonitor_SMBus_Rotoye(*this, state[instance], _params[instance]);615break;616#endif617#if AP_BATTERY_SMBUS_NEODESIGN_ENABLED618case Type::NeoDesign:619drivers[instance] = NEW_NOTHROW AP_BattMonitor_SMBus_NeoDesign(*this, state[instance], _params[instance]);620break;621#endif622#if AP_BATTERY_BEBOP_ENABLED623case Type::BEBOP:624drivers[instance] = NEW_NOTHROW AP_BattMonitor_Bebop(*this, state[instance], _params[instance]);625break;626#endif627#if AP_BATTERY_UAVCAN_BATTERYINFO_ENABLED628case Type::UAVCAN_BatteryInfo:629drivers[instance] = NEW_NOTHROW AP_BattMonitor_DroneCAN(*this, state[instance], AP_BattMonitor_DroneCAN::UAVCAN_BATTERY_INFO, _params[instance]);630break;631#endif632#if AP_BATTERY_ESC_ENABLED633case Type::BLHeliESC:634drivers[instance] = NEW_NOTHROW AP_BattMonitor_ESC(*this, state[instance], _params[instance]);635break;636#endif637#if AP_BATTERY_SUM_ENABLED638case Type::Sum:639drivers[instance] = NEW_NOTHROW AP_BattMonitor_Sum(*this, state[instance], _params[instance], instance);640break;641#endif642#if AP_BATTERY_FUELFLOW_ENABLED643case Type::FuelFlow:644drivers[instance] = NEW_NOTHROW AP_BattMonitor_FuelFlow(*this, state[instance], _params[instance]);645break;646#endif // AP_BATTERY_FUELFLOW_ENABLED647#if AP_BATTERY_FUELLEVEL_PWM_ENABLED648case Type::FuelLevel_PWM:649drivers[instance] = NEW_NOTHROW AP_BattMonitor_FuelLevel_PWM(*this, state[instance], _params[instance]);650break;651#endif // AP_BATTERY_FUELLEVEL_PWM_ENABLED652#if AP_BATTERY_FUELLEVEL_ANALOG_ENABLED653case Type::FuelLevel_Analog:654drivers[instance] = NEW_NOTHROW AP_BattMonitor_FuelLevel_Analog(*this, state[instance], _params[instance]);655break;656#endif // AP_BATTERY_FUELLEVEL_ANALOG_ENABLED657#if HAL_GENERATOR_ENABLED658case Type::GENERATOR_ELEC:659drivers[instance] = NEW_NOTHROW AP_BattMonitor_Generator_Elec(*this, state[instance], _params[instance]);660break;661case Type::GENERATOR_FUEL:662drivers[instance] = NEW_NOTHROW AP_BattMonitor_Generator_FuelLevel(*this, state[instance], _params[instance]);663break;664#endif // HAL_GENERATOR_ENABLED665#if AP_BATTERY_INA2XX_ENABLED666case Type::INA2XX:667drivers[instance] = NEW_NOTHROW AP_BattMonitor_INA2XX(*this, state[instance], _params[instance]);668break;669#endif670#if AP_BATTERY_LTC2946_ENABLED671case Type::LTC2946:672drivers[instance] = NEW_NOTHROW AP_BattMonitor_LTC2946(*this, state[instance], _params[instance]);673break;674#endif675#if HAL_TORQEEDO_ENABLED676case Type::Torqeedo:677drivers[instance] = NEW_NOTHROW AP_BattMonitor_Torqeedo(*this, state[instance], _params[instance]);678break;679#endif680#if AP_BATTERY_SYNTHETIC_CURRENT_ENABLED681case Type::Analog_Volt_Synthetic_Current:682drivers[instance] = NEW_NOTHROW AP_BattMonitor_Synthetic_Current(*this, state[instance], _params[instance]);683break;684#endif685#if AP_BATTERY_INA239_ENABLED686case Type::INA239_SPI:687drivers[instance] = NEW_NOTHROW AP_BattMonitor_INA239(*this, state[instance], _params[instance]);688break;689#endif690#if AP_BATTERY_EFI_ENABLED691case Type::EFI:692drivers[instance] = NEW_NOTHROW AP_BattMonitor_EFI(*this, state[instance], _params[instance]);693break;694#endif // AP_BATTERY_EFI_ENABLED695#if AP_BATTERY_AD7091R5_ENABLED696case Type::AD7091R5:697drivers[instance] = NEW_NOTHROW AP_BattMonitor_AD7091R5(*this, state[instance], _params[instance]);698break;699#endif// AP_BATTERY_AD7091R5_ENABLED700#if AP_BATTERY_SCRIPTING_ENABLED701case Type::Scripting:702drivers[instance] = NEW_NOTHROW AP_BattMonitor_Scripting(*this, state[instance], _params[instance]);703break;704#endif // AP_BATTERY_SCRIPTING_ENABLED705#if AP_BATTERY_INA3221_ENABLED706case Type::INA3221:707drivers[instance] = NEW_NOTHROW AP_BattMonitor_INA3221(*this, state[instance], _params[instance]);708break;709#endif // AP_BATTERY_INA3221_ENABLED710#if AP_BATTERY_TIBQ76952_ENABLED711case Type::TIBQ76952_I2C:712drivers[instance] = NEW_NOTHROW AP_BattMonitor_TIBQ76952(*this, state[instance], _params[instance]);713break;714#endif // AP_BATTERY_TIBQ76952_ENABLED715case Type::NONE:716default:717break;718}719if (drivers[instance] != nullptr) {720state[instance].type = allocation_type;721}722// if the backend has some local parameters then make those available in the tree723if (drivers[instance] && state[instance].var_info) {724backend_var_info[instance] = state[instance].var_info;725AP_Param::load_object_from_eeprom(drivers[instance], backend_var_info[instance]);726727// param count could have changed728AP_Param::invalidate_count();729}730731// call init function for each backend732if (drivers[instance] != nullptr) {733drivers[instance]->init();734// _num_instances is actually the index for looping over instances735// the user may have BATT_MONITOR=0 and BATT2_MONITOR=7, in which case736// there will be a gap, but as we always check for drivers[instances] being nullptr737// this is safe738_num_instances = instance + 1;739}740}741}742743// read - For all active instances read voltage & current; log BAT, BCL, POWR, MCU744void AP_BattMonitor::read()745{746#if HAL_LOGGING_ENABLED747AP_Logger *logger = AP_Logger::get_singleton();748if (logger != nullptr && logger->should_log(_log_battery_bit)) {749logger->Write_Power();750}751#endif752753const uint32_t now_ms = AP_HAL::millis();754for (uint8_t i=0; i<_num_instances; i++) {755if (drivers[i] == nullptr) {756continue;757}758if (allocated_type(i) != configured_type(i)) {759continue;760}761// allow run-time disabling; this is technically redundant762if (configured_type(i) == Type::NONE) {763continue;764}765drivers[i]->read();766drivers[i]->update_resistance_estimate();767768#if AP_BATTERY_ESC_TELEM_OUTBOUND_ENABLED769drivers[i]->update_esc_telem_outbound();770#endif771772// Update last heathy timestamp773if (state[i].healthy) {774state[i].last_healthy_ms = now_ms;775}776777#if HAL_LOGGING_ENABLED778if (logger != nullptr && logger->should_log(_log_battery_bit)) {779const uint64_t time_us = AP_HAL::micros64();780drivers[i]->Log_Write_BAT(i, time_us);781drivers[i]->Log_Write_BCL(i, time_us);782}783#endif784}785786check_failsafes();787788checkPoweringOff();789}790791// healthy - returns true if monitor is functioning792bool AP_BattMonitor::healthy(uint8_t instance) const {793return instance < _num_instances && state[instance].healthy;794}795796/// voltage - returns battery voltage in volts797float AP_BattMonitor::voltage(uint8_t instance) const798{799if (instance < _num_instances) {800return state[instance].voltage;801} else {802return 0.0f;803}804}805806/// get voltage with sag removed (based on battery current draw and resistance)807/// this will always be greater than or equal to the raw voltage808float AP_BattMonitor::voltage_resting_estimate(uint8_t instance) const809{810if (instance < _num_instances && drivers[instance] != nullptr) {811return drivers[instance]->voltage_resting_estimate();812} else {813return 0.0f;814}815}816817/// voltage - returns battery voltage in volts for GCS, may be resting voltage if option enabled818float AP_BattMonitor::gcs_voltage(uint8_t instance) const819{820if (instance >= _num_instances || drivers[instance] == nullptr) {821return 0.0f;822}823if (drivers[instance]->option_is_set(AP_BattMonitor_Params::Options::GCS_Resting_Voltage)) {824return voltage_resting_estimate(instance);825}826return state[instance].voltage;827}828829bool AP_BattMonitor::option_is_set(uint8_t instance, AP_BattMonitor_Params::Options option) const830{831if (instance >= _num_instances || drivers[instance] == nullptr) {832return false;833}834return drivers[instance]->option_is_set(option);835}836837/// current_amps - returns the instantaneous current draw in amperes838bool AP_BattMonitor::current_amps(float ¤t, uint8_t instance) const {839if ((instance < _num_instances) && (drivers[instance] != nullptr) && drivers[instance]->has_current()) {840current = state[instance].current_amps;841return true;842} else {843return false;844}845}846847/// consumed_mah - returns total current drawn since start-up in milliampere.hours848bool AP_BattMonitor::consumed_mah(float &mah, const uint8_t instance) const {849if ((instance < _num_instances) && (drivers[instance] != nullptr) && drivers[instance]->has_current()) {850const float consumed_mah = state[instance].consumed_mah;851if (isnan(consumed_mah)) {852return false;853}854mah = consumed_mah;855return true;856} else {857return false;858}859}860861/// consumed_wh - returns energy consumed since start-up in Watt.hours862bool AP_BattMonitor::consumed_wh(float &wh, const uint8_t instance) const {863if (instance < _num_instances && drivers[instance] != nullptr && drivers[instance]->has_consumed_energy()) {864wh = state[instance].consumed_wh;865return true;866} else {867return false;868}869}870871/// capacity_remaining_pct - returns true if the percentage is valid and writes to percentage argument872bool AP_BattMonitor::capacity_remaining_pct(uint8_t &percentage, uint8_t instance) const873{874if (instance < _num_instances && drivers[instance] != nullptr) {875return drivers[instance]->capacity_remaining_pct(percentage);876}877return false;878}879880/// time_remaining - returns remaining battery time881bool AP_BattMonitor::time_remaining(uint32_t &seconds, uint8_t instance) const882{883if (instance < _num_instances && drivers[instance] != nullptr && state[instance].has_time_remaining) {884seconds = state[instance].time_remaining;885return true;886}887return false;888}889890/// pack_capacity_mah - returns the capacity of the battery pack in mAh when the pack is full891int32_t AP_BattMonitor::pack_capacity_mah(uint8_t instance) const892{893if (instance < AP_BATT_MONITOR_MAX_INSTANCES) {894return _params[instance]._pack_capacity;895} else {896return 0;897}898}899900void AP_BattMonitor::check_failsafes(void)901{902if (hal.util->get_soft_armed()) {903for (uint8_t i = 0; i < _num_instances; i++) {904if (drivers[i] == nullptr) {905continue;906}907908const Failsafe type = drivers[i]->update_failsafes();909if (type <= state[i].failsafe) {910continue;911}912913int8_t action = 0;914const char *type_str = nullptr;915switch (type) {916case Failsafe::None:917continue; // should not have been called in this case918case Failsafe::Unhealthy:919// Report only for unhealthy, could add action param in the future920action = 0;921type_str = "missing, last:";922break;923case Failsafe::Low:924action = _params[i]._failsafe_low_action;925type_str = "low";926break;927case Failsafe::Critical:928action = _params[i]._failsafe_critical_action;929type_str = "critical";930break;931}932933GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Battery %d is %s %.2fV used %.0f mAh", i + 1, type_str,934(double)voltage(i), (double)state[i].consumed_mah);935_has_triggered_failsafe = true;936#ifndef HAL_BUILD_AP_PERIPH937AP_Notify::flags.failsafe_battery = true;938#endif939state[i].failsafe = type;940941// map the desired failsafe action to a priority level942int8_t priority = 0;943if (_failsafe_priorities != nullptr) {944while (_failsafe_priorities[priority] != -1) {945if (_failsafe_priorities[priority] == action) {946break;947}948priority++;949}950951}952953// trigger failsafe if the action was equal or higher priority954// It's valid to retrigger the same action if a different battery provoked the event955if (priority <= _highest_failsafe_priority) {956_battery_failsafe_handler_fn(type_str, action);957_highest_failsafe_priority = priority;958}959}960}961}962963// return true if any battery is pushing too much power964bool AP_BattMonitor::overpower_detected() const965{966#if AP_BATTERY_WATT_MAX_ENABLED && APM_BUILD_TYPE(APM_BUILD_ArduPlane)967for (uint8_t instance = 0; instance < _num_instances; instance++) {968if (overpower_detected(instance)) {969return true;970}971}972#endif973return false;974}975976bool AP_BattMonitor::overpower_detected(uint8_t instance) const977{978#if AP_BATTERY_WATT_MAX_ENABLED && APM_BUILD_TYPE(APM_BUILD_ArduPlane)979if (instance < _num_instances && _params[instance]._watt_max > 0) {980const float power = state[instance].current_amps * state[instance].voltage;981return state[instance].healthy && (power > _params[instance]._watt_max);982}983#endif984return false;985}986987bool AP_BattMonitor::has_cell_voltages(const uint8_t instance) const988{989if (instance < _num_instances && drivers[instance] != nullptr) {990return drivers[instance]->has_cell_voltages();991}992993return false;994}995996// return the current cell voltages, returns the first monitor instances cells if the instance is out of range997const AP_BattMonitor::cells & AP_BattMonitor::get_cell_voltages(const uint8_t instance) const998{999if (instance >= AP_BATT_MONITOR_MAX_INSTANCES) {1000return state[AP_BATT_PRIMARY_INSTANCE].cell_voltages;1001} else {1002return state[instance].cell_voltages;1003}1004}10051006// get once cell voltage (for scripting)1007bool AP_BattMonitor::get_cell_voltage(uint8_t instance, uint8_t cell, float &voltage) const1008{1009if (!has_cell_voltages(instance) ||1010cell >= AP_BATT_MONITOR_CELLS_MAX) {1011return false;1012}1013const auto &cell_voltages = get_cell_voltages(instance);1014const uint16_t voltage_mv = cell_voltages.cells[cell];1015if (voltage_mv == 0 || voltage_mv == UINT16_MAX) {1016// UINT16_MAX is used as invalid indicator1017return false;1018}1019voltage = voltage_mv*0.001;1020return true;1021}10221023// returns true if there is a temperature reading1024bool AP_BattMonitor::get_temperature(float &temperature, const uint8_t instance) const1025{1026if (instance >= _num_instances || drivers[instance] == nullptr) {1027return false;1028}10291030return drivers[instance]->get_temperature(temperature);1031}10321033#if AP_TEMPERATURE_SENSOR_ENABLED1034// return true when successfully setting a battery temperature from an external source by instance1035bool AP_BattMonitor::set_temperature(const float temperature, const uint8_t instance)1036{1037if (instance >= _num_instances || drivers[instance] == nullptr) {1038return false;1039}1040state[instance].temperature_external = temperature;1041state[instance].temperature_external_use = true;1042return true;1043}10441045// return true when successfully setting a battery temperature from an external source by serial_number1046bool AP_BattMonitor::set_temperature_by_serial_number(const float temperature, const int32_t serial_number)1047{1048bool success = false;1049for (uint8_t i = 0; i < _num_instances; i++) {1050if (drivers[i] != nullptr && get_serial_number(i) == serial_number) {1051success |= set_temperature(temperature, i);1052}1053}1054return success;1055}1056#endif // AP_TEMPERATURE_SENSOR_ENABLED10571058// return true if cycle count can be provided and fills in cycles argument1059bool AP_BattMonitor::get_cycle_count(uint8_t instance, uint16_t &cycles) const1060{1061if (instance >= _num_instances || (drivers[instance] == nullptr)) {1062return false;1063}1064return drivers[instance]->get_cycle_count(cycles);1065}10661067bool AP_BattMonitor::arming_checks(size_t buflen, char *buffer) const1068{1069char temp_buffer[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1] {};10701071for (uint8_t i = 0; i < AP_BATT_MONITOR_MAX_INSTANCES; i++) {1072const auto expected_type = configured_type(i);10731074if (drivers[i] == nullptr && expected_type == Type::NONE) {1075continue;1076}10771078#if !AP_BATTERY_SUM_ENABLED1079// CONVERSION - Added Sep 2024 for ArduPilot 4.6 as we are1080// removing the SUM backend on 1MB boards. Give a1081// more-specific error for the sum backend:1082if (expected_type == Type::Sum) {1083hal.util->snprintf(buffer, buflen, "Battery %d %s", i + 1, "feature BATTERY_SUM not available");1084return false;1085}1086#endif10871088if (drivers[i] == nullptr || allocated_type(i) != expected_type) {1089hal.util->snprintf(buffer, buflen, "Battery %d %s", i + 1, "unhealthy");1090return false;1091}10921093if (!drivers[i]->arming_checks(temp_buffer, sizeof(temp_buffer))) {1094hal.util->snprintf(buffer, buflen, "Battery %d %s", i + 1, temp_buffer);1095return false;1096}1097}10981099return true;1100}11011102// Check's each smart battery instance for its powering off state and broadcasts notifications1103void AP_BattMonitor::checkPoweringOff(void)1104{1105for (uint8_t i = 0; i < _num_instances; i++) {1106if (state[i].is_powering_off && !state[i].powerOffNotified) {1107#ifndef HAL_BUILD_AP_PERIPH1108// Set the AP_Notify flag, which plays the power off tones1109AP_Notify::flags.powering_off = true;1110#endif11111112// Send a Mavlink broadcast announcing the shutdown1113#if HAL_GCS_ENABLED1114mavlink_command_long_t cmd_msg{};1115cmd_msg.command = MAV_CMD_POWER_OFF_INITIATED;1116cmd_msg.param1 = i+1;1117GCS_MAVLINK::send_to_components(MAVLINK_MSG_ID_COMMAND_LONG, (char*)&cmd_msg, sizeof(cmd_msg));1118GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Vehicle %d battery %d is powering off", mavlink_system.sysid, i+1);1119#endif11201121// only send this once1122state[i].powerOffNotified = true;1123}1124}1125}11261127/*1128reset battery remaining percentage for batteries that integrate to1129calculate percentage remaining1130*/1131bool AP_BattMonitor::reset_remaining_mask(uint16_t battery_mask, float percentage)1132{1133static_assert(AP_BATT_MONITOR_MAX_INSTANCES <= 16, "More batteries are enabled then can be reset");1134bool ret = true;1135Failsafe highest_failsafe = Failsafe::None;1136for (uint8_t i = 0; i < _num_instances; i++) {1137if ((1U<<i) & battery_mask) {1138if (drivers[i] != nullptr) {1139ret &= drivers[i]->reset_remaining(percentage);1140} else {1141ret = false;1142}1143}1144if (state[i].failsafe > highest_failsafe) {1145highest_failsafe = state[i].failsafe;1146}1147}11481149// If all backends are not in failsafe then set overall failsafe state1150if (highest_failsafe == Failsafe::None) {1151_highest_failsafe_priority = INT8_MAX;1152_has_triggered_failsafe = false;1153// and reset notify flag1154AP_Notify::flags.failsafe_battery = false;1155}1156return ret;1157}11581159// Returns the mavlink charge state. The following mavlink charge states are not used1160// MAV_BATTERY_CHARGE_STATE_EMERGENCY , MAV_BATTERY_CHARGE_STATE_FAILED1161// MAV_BATTERY_CHARGE_STATE_UNHEALTHY, MAV_BATTERY_CHARGE_STATE_CHARGING1162MAV_BATTERY_CHARGE_STATE AP_BattMonitor::get_mavlink_charge_state(const uint8_t instance) const1163{1164if (instance >= _num_instances) {1165return MAV_BATTERY_CHARGE_STATE_UNDEFINED;1166}11671168switch (state[instance].failsafe) {11691170case Failsafe::None:1171case Failsafe::Unhealthy:1172if (get_mavlink_fault_bitmask(instance) != 0 || !healthy()) {1173return MAV_BATTERY_CHARGE_STATE_UNHEALTHY;1174}1175return MAV_BATTERY_CHARGE_STATE_OK;11761177case Failsafe::Low:1178return MAV_BATTERY_CHARGE_STATE_LOW;11791180case Failsafe::Critical:1181return MAV_BATTERY_CHARGE_STATE_CRITICAL;1182}11831184// Should not reach this1185return MAV_BATTERY_CHARGE_STATE_UNDEFINED;1186}11871188// Returns mavlink fault state1189uint32_t AP_BattMonitor::get_mavlink_fault_bitmask(const uint8_t instance) const1190{1191if (instance >= _num_instances || drivers[instance] == nullptr) {1192return 0;1193}1194return drivers[instance]->get_mavlink_fault_bitmask();1195}11961197// return true if state of health (as a percentage) can be provided and fills in soh_pct argument1198bool AP_BattMonitor::get_state_of_health_pct(uint8_t instance, uint8_t &soh_pct) const1199{1200if (instance >= _num_instances || drivers[instance] == nullptr) {1201return false;1202}1203return drivers[instance]->get_state_of_health_pct(soh_pct);1204}12051206// Enable/Disable (Turn on/off) power to all backends who are MPPTs or BMSs1207void AP_BattMonitor::set_powered_state_to_all(const bool power_on)1208{1209for (uint8_t i=0; i < _num_instances; i++) {1210set_powered_state(i, power_on);1211}1212}12131214// Enable/Disable (Turn on/off) power. When disabled, the MPPT or BMS does not1215// supply energy to the system regardless if it's capable to or not. When enabled1216// it will supply energy if available.1217void AP_BattMonitor::set_powered_state(const uint8_t instance, const bool power_on)1218{1219if (instance < _num_instances && drivers[instance] != nullptr) {1220drivers[instance]->set_powered_state(power_on);1221}1222}12231224/*1225check that all configured battery monitors are healthy1226*/1227bool AP_BattMonitor::healthy() const1228{1229for (uint8_t i=0; i< _num_instances; i++) {1230if (allocated_type(i) != configured_type(i)) {1231return false;1232}1233// allow run-time disabling; this is technically redundant1234if (configured_type(i) == Type::NONE) {1235continue;1236}1237if (!healthy(i)) {1238return false;1239}1240}1241return true;1242}12431244#if AP_BATTERY_SCRIPTING_ENABLED1245/*1246handle state update from a lua script1247*/1248bool AP_BattMonitor::handle_scripting(uint8_t idx, const BattMonitorScript_State &_state)1249{1250if (idx >= _num_instances) {1251return false;1252}1253if (drivers[idx] == nullptr) {1254return false;1255}1256return drivers[idx]->handle_scripting(_state);1257}1258#endif12591260namespace AP {12611262AP_BattMonitor &battery()1263{1264return *AP_BattMonitor::get_singleton();1265}12661267};12681269#endif // AP_BATTERY_ENABLED127012711272