furi_hal_power.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. #include <furi_hal_power.h>
  2. #include <furi_hal_clock.h>
  3. #include <furi_hal_delay.h>
  4. #include <furi_hal_bt.h>
  5. #include <furi_hal_resources.h>
  6. #include <furi_hal_uart.h>
  7. #include <stm32wbxx_ll_rcc.h>
  8. #include <stm32wbxx_ll_pwr.h>
  9. #include <stm32wbxx_ll_hsem.h>
  10. #include <stm32wbxx_ll_cortex.h>
  11. #include <stm32wbxx_ll_gpio.h>
  12. #include <hw_conf.h>
  13. #include <bq27220.h>
  14. #include <bq25896.h>
  15. #include <furi.h>
  16. #define TAG "FuriHalPower"
  17. #ifdef FURI_HAL_POWER_DEEP_SLEEP_ENABLED
  18. #define FURI_HAL_POWER_DEEP_INSOMNIA 0
  19. #else
  20. #define FURI_HAL_POWER_DEEP_INSOMNIA 1
  21. #endif
  22. typedef struct {
  23. volatile uint8_t insomnia;
  24. volatile uint8_t deep_insomnia;
  25. volatile uint8_t suppress_charge;
  26. uint8_t gauge_initialized;
  27. uint8_t charger_initialized;
  28. } FuriHalPower;
  29. static volatile FuriHalPower furi_hal_power = {
  30. .insomnia = 0,
  31. .deep_insomnia = FURI_HAL_POWER_DEEP_INSOMNIA,
  32. .suppress_charge = 0,
  33. };
  34. const ParamCEDV cedv = {
  35. .cedv_conf.gauge_conf =
  36. {
  37. .CCT = 1,
  38. .CSYNC = 0,
  39. .EDV_CMP = 0,
  40. .SC = 1,
  41. .FIXED_EDV0 = 1,
  42. .FCC_LIM = 1,
  43. .FC_FOR_VDQ = 1,
  44. .IGNORE_SD = 1,
  45. .SME0 = 0,
  46. },
  47. .full_charge_cap = 2101,
  48. .design_cap = 2101,
  49. .EDV0 = 3300,
  50. .EDV1 = 3321,
  51. .EDV2 = 3355,
  52. .EMF = 3679,
  53. .C0 = 430,
  54. .C1 = 0,
  55. .R1 = 408,
  56. .R0 = 334,
  57. .T0 = 4626,
  58. .TC = 11,
  59. .DOD0 = 4044,
  60. .DOD10 = 3905,
  61. .DOD20 = 3807,
  62. .DOD30 = 3718,
  63. .DOD40 = 3642,
  64. .DOD50 = 3585,
  65. .DOD60 = 3546,
  66. .DOD70 = 3514,
  67. .DOD80 = 3477,
  68. .DOD90 = 3411,
  69. .DOD100 = 3299,
  70. };
  71. void furi_hal_power_init() {
  72. LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
  73. LL_PWR_SMPS_SetMode(LL_PWR_SMPS_STEP_DOWN);
  74. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  75. bq27220_init(&furi_hal_i2c_handle_power, &cedv);
  76. bq25896_init(&furi_hal_i2c_handle_power);
  77. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  78. #ifdef FURI_HAL_OS_DEBUG
  79. furi_hal_gpio_init_simple(&gpio_ext_pb2, GpioModeOutputPushPull);
  80. furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeOutputPushPull);
  81. #endif
  82. FURI_LOG_I(TAG, "Init OK");
  83. }
  84. bool furi_hal_power_gauge_is_ok() {
  85. bool ret = true;
  86. BatteryStatus battery_status;
  87. OperationStatus operation_status;
  88. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  89. if(bq27220_get_battery_status(&furi_hal_i2c_handle_power, &battery_status) == BQ27220_ERROR ||
  90. bq27220_get_operation_status(&furi_hal_i2c_handle_power, &operation_status) ==
  91. BQ27220_ERROR) {
  92. ret = false;
  93. } else {
  94. ret &= battery_status.BATTPRES;
  95. ret &= operation_status.INITCOMP;
  96. ret &= (cedv.design_cap == bq27220_get_design_capacity(&furi_hal_i2c_handle_power));
  97. }
  98. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  99. return ret;
  100. }
  101. uint16_t furi_hal_power_insomnia_level() {
  102. return furi_hal_power.insomnia;
  103. }
  104. void furi_hal_power_insomnia_enter() {
  105. FURI_CRITICAL_ENTER();
  106. furi_assert(furi_hal_power.insomnia < UINT8_MAX);
  107. furi_hal_power.insomnia++;
  108. FURI_CRITICAL_EXIT();
  109. }
  110. void furi_hal_power_insomnia_exit() {
  111. FURI_CRITICAL_ENTER();
  112. furi_assert(furi_hal_power.insomnia > 0);
  113. furi_hal_power.insomnia--;
  114. FURI_CRITICAL_EXIT();
  115. }
  116. bool furi_hal_power_sleep_available() {
  117. return furi_hal_power.insomnia == 0;
  118. }
  119. bool furi_hal_power_deep_sleep_available() {
  120. return furi_hal_bt_is_alive() && furi_hal_power.deep_insomnia == 0;
  121. }
  122. void furi_hal_power_light_sleep() {
  123. __WFI();
  124. }
  125. static inline void furi_hal_power_suspend_aux_periphs() {
  126. // Disable USART
  127. furi_hal_uart_suspend(FuriHalUartIdUSART1);
  128. furi_hal_uart_suspend(FuriHalUartIdLPUART1);
  129. // TODO: Disable USB
  130. }
  131. static inline void furi_hal_power_resume_aux_periphs() {
  132. // Re-enable USART
  133. furi_hal_uart_resume(FuriHalUartIdUSART1);
  134. furi_hal_uart_resume(FuriHalUartIdLPUART1);
  135. // TODO: Re-enable USB
  136. }
  137. void furi_hal_power_deep_sleep() {
  138. furi_hal_power_suspend_aux_periphs();
  139. while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID))
  140. ;
  141. if(!LL_HSEM_1StepLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID)) {
  142. if(LL_PWR_IsActiveFlag_C2DS() || LL_PWR_IsActiveFlag_C2SB()) {
  143. // Release ENTRY_STOP_MODE semaphore
  144. LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0);
  145. // The switch on HSI before entering Stop Mode is required
  146. furi_hal_clock_switch_to_hsi();
  147. }
  148. } else {
  149. /**
  150. * The switch on HSI before entering Stop Mode is required
  151. */
  152. furi_hal_clock_switch_to_hsi();
  153. }
  154. /* Release RCC semaphore */
  155. LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0);
  156. // Prepare deep sleep
  157. LL_PWR_SetPowerMode(LL_PWR_MODE_STOP2);
  158. LL_C2_PWR_SetPowerMode(LL_PWR_MODE_STOP2);
  159. LL_LPM_EnableDeepSleep();
  160. #if defined(__CC_ARM)
  161. // Force store operations
  162. __force_stores();
  163. #endif
  164. __WFI();
  165. LL_LPM_EnableSleep();
  166. // Make sure that values differ to prevent disaster on wfi
  167. LL_PWR_SetPowerMode(LL_PWR_MODE_STOP0);
  168. LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
  169. LL_PWR_ClearFlag_C1STOP_C1STB();
  170. LL_PWR_ClearFlag_C2STOP_C2STB();
  171. /* Release ENTRY_STOP_MODE semaphore */
  172. LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0);
  173. while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID))
  174. ;
  175. if(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) {
  176. furi_hal_clock_switch_to_pll();
  177. }
  178. LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0);
  179. furi_hal_power_resume_aux_periphs();
  180. }
  181. void furi_hal_power_sleep() {
  182. if(furi_hal_power_deep_sleep_available()) {
  183. #ifdef FURI_HAL_OS_DEBUG
  184. furi_hal_gpio_write(&gpio_ext_pc3, 1);
  185. #endif
  186. furi_hal_power_deep_sleep();
  187. #ifdef FURI_HAL_OS_DEBUG
  188. furi_hal_gpio_write(&gpio_ext_pc3, 0);
  189. #endif
  190. } else {
  191. #ifdef FURI_HAL_OS_DEBUG
  192. furi_hal_gpio_write(&gpio_ext_pb2, 1);
  193. #endif
  194. furi_hal_power_light_sleep();
  195. #ifdef FURI_HAL_OS_DEBUG
  196. furi_hal_gpio_write(&gpio_ext_pb2, 0);
  197. #endif
  198. }
  199. }
  200. uint8_t furi_hal_power_get_pct() {
  201. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  202. uint8_t ret = bq27220_get_state_of_charge(&furi_hal_i2c_handle_power);
  203. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  204. return ret;
  205. }
  206. uint8_t furi_hal_power_get_bat_health_pct() {
  207. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  208. uint8_t ret = bq27220_get_state_of_health(&furi_hal_i2c_handle_power);
  209. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  210. return ret;
  211. }
  212. bool furi_hal_power_is_charging() {
  213. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  214. bool ret = bq25896_is_charging(&furi_hal_i2c_handle_power);
  215. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  216. return ret;
  217. }
  218. void furi_hal_power_shutdown() {
  219. furi_hal_power_insomnia_enter();
  220. furi_hal_bt_reinit();
  221. while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID))
  222. ;
  223. if(!LL_HSEM_1StepLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID)) {
  224. if(LL_PWR_IsActiveFlag_C2DS() || LL_PWR_IsActiveFlag_C2SB()) {
  225. // Release ENTRY_STOP_MODE semaphore
  226. LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0);
  227. }
  228. }
  229. // Prepare Wakeup pin
  230. LL_PWR_SetWakeUpPinPolarityLow(LL_PWR_WAKEUP_PIN2);
  231. LL_PWR_EnableWakeUpPin(LL_PWR_WAKEUP_PIN2);
  232. LL_C2_PWR_EnableWakeUpPin(LL_PWR_WAKEUP_PIN2);
  233. /* Release RCC semaphore */
  234. LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0);
  235. LL_PWR_DisableBootC2();
  236. LL_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
  237. LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
  238. LL_LPM_EnableDeepSleep();
  239. __WFI();
  240. furi_crash("Insomniac core2");
  241. }
  242. void furi_hal_power_off() {
  243. // Crutch: shutting down with ext 3V3 off is causing LSE to stop
  244. furi_hal_power_enable_external_3_3v();
  245. furi_hal_delay_us(1000);
  246. // Send poweroff to charger
  247. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  248. bq25896_poweroff(&furi_hal_i2c_handle_power);
  249. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  250. }
  251. void furi_hal_power_reset() {
  252. NVIC_SystemReset();
  253. }
  254. void furi_hal_power_enable_otg() {
  255. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  256. bq25896_enable_otg(&furi_hal_i2c_handle_power);
  257. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  258. }
  259. void furi_hal_power_disable_otg() {
  260. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  261. bq25896_disable_otg(&furi_hal_i2c_handle_power);
  262. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  263. }
  264. bool furi_hal_power_is_otg_enabled() {
  265. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  266. bool ret = bq25896_is_otg_enabled(&furi_hal_i2c_handle_power);
  267. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  268. return ret;
  269. }
  270. void furi_hal_power_check_otg_status() {
  271. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  272. if(bq25896_check_otg_fault(&furi_hal_i2c_handle_power))
  273. bq25896_disable_otg(&furi_hal_i2c_handle_power);
  274. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  275. }
  276. uint32_t furi_hal_power_get_battery_remaining_capacity() {
  277. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  278. uint32_t ret = bq27220_get_remaining_capacity(&furi_hal_i2c_handle_power);
  279. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  280. return ret;
  281. }
  282. uint32_t furi_hal_power_get_battery_full_capacity() {
  283. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  284. uint32_t ret = bq27220_get_full_charge_capacity(&furi_hal_i2c_handle_power);
  285. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  286. return ret;
  287. }
  288. uint32_t furi_hal_power_get_battery_design_capacity() {
  289. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  290. uint32_t ret = bq27220_get_design_capacity(&furi_hal_i2c_handle_power);
  291. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  292. return ret;
  293. }
  294. float furi_hal_power_get_battery_voltage(FuriHalPowerIC ic) {
  295. float ret = 0.0f;
  296. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  297. if(ic == FuriHalPowerICCharger) {
  298. ret = (float)bq25896_get_vbat_voltage(&furi_hal_i2c_handle_power) / 1000.0f;
  299. } else if(ic == FuriHalPowerICFuelGauge) {
  300. ret = (float)bq27220_get_voltage(&furi_hal_i2c_handle_power) / 1000.0f;
  301. }
  302. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  303. return ret;
  304. }
  305. float furi_hal_power_get_battery_current(FuriHalPowerIC ic) {
  306. float ret = 0.0f;
  307. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  308. if(ic == FuriHalPowerICCharger) {
  309. ret = (float)bq25896_get_vbat_current(&furi_hal_i2c_handle_power) / 1000.0f;
  310. } else if(ic == FuriHalPowerICFuelGauge) {
  311. ret = (float)bq27220_get_current(&furi_hal_i2c_handle_power) / 1000.0f;
  312. }
  313. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  314. return ret;
  315. }
  316. static float furi_hal_power_get_battery_temperature_internal(FuriHalPowerIC ic) {
  317. float ret = 0.0f;
  318. if(ic == FuriHalPowerICCharger) {
  319. // Linear approximation, +/- 5 C
  320. ret = (71.0f - (float)bq25896_get_ntc_mpct(&furi_hal_i2c_handle_power) / 1000) / 0.6f;
  321. } else if(ic == FuriHalPowerICFuelGauge) {
  322. ret = ((float)bq27220_get_temperature(&furi_hal_i2c_handle_power) - 2731.0f) / 10.0f;
  323. }
  324. return ret;
  325. }
  326. float furi_hal_power_get_battery_temperature(FuriHalPowerIC ic) {
  327. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  328. float ret = furi_hal_power_get_battery_temperature_internal(ic);
  329. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  330. return ret;
  331. }
  332. float furi_hal_power_get_usb_voltage() {
  333. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  334. float ret = (float)bq25896_get_vbus_voltage(&furi_hal_i2c_handle_power) / 1000.0f;
  335. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  336. return ret;
  337. }
  338. void furi_hal_power_dump_state() {
  339. BatteryStatus battery_status;
  340. OperationStatus operation_status;
  341. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  342. if(bq27220_get_battery_status(&furi_hal_i2c_handle_power, &battery_status) == BQ27220_ERROR ||
  343. bq27220_get_operation_status(&furi_hal_i2c_handle_power, &operation_status) ==
  344. BQ27220_ERROR) {
  345. printf("Failed to get bq27220 status. Communication error.\r\n");
  346. } else {
  347. // Operation status register
  348. printf(
  349. "bq27220: CALMD: %d, SEC: %d, EDV2: %d, VDQ: %d, INITCOMP: %d, SMTH: %d, BTPINT: %d, CFGUPDATE: %d\r\n",
  350. operation_status.CALMD,
  351. operation_status.SEC,
  352. operation_status.EDV2,
  353. operation_status.VDQ,
  354. operation_status.INITCOMP,
  355. operation_status.SMTH,
  356. operation_status.BTPINT,
  357. operation_status.CFGUPDATE);
  358. // Battery status register, part 1
  359. printf(
  360. "bq27220: CHGINH: %d, FC: %d, OTD: %d, OTC: %d, SLEEP: %d, OCVFAIL: %d, OCVCOMP: %d, FD: %d\r\n",
  361. battery_status.CHGINH,
  362. battery_status.FC,
  363. battery_status.OTD,
  364. battery_status.OTC,
  365. battery_status.SLEEP,
  366. battery_status.OCVFAIL,
  367. battery_status.OCVCOMP,
  368. battery_status.FD);
  369. // Battery status register, part 2
  370. printf(
  371. "bq27220: DSG: %d, SYSDWN: %d, TDA: %d, BATTPRES: %d, AUTH_GD: %d, OCVGD: %d, TCA: %d, RSVD: %d\r\n",
  372. battery_status.DSG,
  373. battery_status.SYSDWN,
  374. battery_status.TDA,
  375. battery_status.BATTPRES,
  376. battery_status.AUTH_GD,
  377. battery_status.OCVGD,
  378. battery_status.TCA,
  379. battery_status.RSVD);
  380. // Voltage and current info
  381. printf(
  382. "bq27220: Full capacity: %dmAh, Design capacity: %dmAh, Remaining capacity: %dmAh, State of Charge: %d%%, State of health: %d%%\r\n",
  383. bq27220_get_full_charge_capacity(&furi_hal_i2c_handle_power),
  384. bq27220_get_design_capacity(&furi_hal_i2c_handle_power),
  385. bq27220_get_remaining_capacity(&furi_hal_i2c_handle_power),
  386. bq27220_get_state_of_charge(&furi_hal_i2c_handle_power),
  387. bq27220_get_state_of_health(&furi_hal_i2c_handle_power));
  388. printf(
  389. "bq27220: Voltage: %dmV, Current: %dmA, Temperature: %dC\r\n",
  390. bq27220_get_voltage(&furi_hal_i2c_handle_power),
  391. bq27220_get_current(&furi_hal_i2c_handle_power),
  392. (int)furi_hal_power_get_battery_temperature_internal(FuriHalPowerICFuelGauge));
  393. }
  394. printf(
  395. "bq25896: VBUS: %d, VSYS: %d, VBAT: %d, Current: %d, NTC: %ldm%%\r\n",
  396. bq25896_get_vbus_voltage(&furi_hal_i2c_handle_power),
  397. bq25896_get_vsys_voltage(&furi_hal_i2c_handle_power),
  398. bq25896_get_vbat_voltage(&furi_hal_i2c_handle_power),
  399. bq25896_get_vbat_current(&furi_hal_i2c_handle_power),
  400. bq25896_get_ntc_mpct(&furi_hal_i2c_handle_power));
  401. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  402. }
  403. void furi_hal_power_enable_external_3_3v() {
  404. furi_hal_gpio_write(&periph_power, 1);
  405. }
  406. void furi_hal_power_disable_external_3_3v() {
  407. furi_hal_gpio_write(&periph_power, 0);
  408. }
  409. void furi_hal_power_suppress_charge_enter() {
  410. vTaskSuspendAll();
  411. bool disable_charging = furi_hal_power.suppress_charge == 0;
  412. furi_hal_power.suppress_charge++;
  413. xTaskResumeAll();
  414. if(disable_charging) {
  415. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  416. bq25896_disable_charging(&furi_hal_i2c_handle_power);
  417. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  418. }
  419. }
  420. void furi_hal_power_suppress_charge_exit() {
  421. vTaskSuspendAll();
  422. furi_hal_power.suppress_charge--;
  423. bool enable_charging = furi_hal_power.suppress_charge == 0;
  424. xTaskResumeAll();
  425. if(enable_charging) {
  426. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  427. bq25896_enable_charging(&furi_hal_i2c_handle_power);
  428. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  429. }
  430. }
  431. void furi_hal_power_info_get(FuriHalPowerInfoCallback out, void* context) {
  432. furi_assert(out);
  433. string_t value;
  434. string_init(value);
  435. // Power Info version
  436. out("power_info_major", "1", false, context);
  437. out("power_info_minor", "0", false, context);
  438. uint8_t charge = furi_hal_power_get_pct();
  439. string_printf(value, "%u", charge);
  440. out("charge_level", string_get_cstr(value), false, context);
  441. if(furi_hal_power_is_charging()) {
  442. if(charge < 100) {
  443. string_printf(value, "charging");
  444. } else {
  445. string_printf(value, "charged");
  446. }
  447. } else {
  448. string_printf(value, "discharging");
  449. }
  450. out("charge_state", string_get_cstr(value), false, context);
  451. uint16_t voltage =
  452. (uint16_t)(furi_hal_power_get_battery_voltage(FuriHalPowerICFuelGauge) * 1000.f);
  453. string_printf(value, "%u", voltage);
  454. out("battery_voltage", string_get_cstr(value), false, context);
  455. int16_t current =
  456. (int16_t)(furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge) * 1000.f);
  457. string_printf(value, "%d", current);
  458. out("battery_current", string_get_cstr(value), false, context);
  459. int16_t temperature = (int16_t)furi_hal_power_get_battery_temperature(FuriHalPowerICFuelGauge);
  460. string_printf(value, "%d", temperature);
  461. out("gauge_temp", string_get_cstr(value), false, context);
  462. string_printf(value, "%u", furi_hal_power_get_bat_health_pct());
  463. out("battery_health", string_get_cstr(value), false, context);
  464. string_printf(value, "%u", furi_hal_power_get_battery_remaining_capacity());
  465. out("capacity_remain", string_get_cstr(value), false, context);
  466. string_printf(value, "%u", furi_hal_power_get_battery_full_capacity());
  467. out("capacity_full", string_get_cstr(value), false, context);
  468. string_printf(value, "%u", furi_hal_power_get_battery_design_capacity());
  469. out("capacity_design", string_get_cstr(value), true, context);
  470. string_clear(value);
  471. }