/* Original work Copyright 2023 Dual Tachyon * https://github.com/DualTachyon * * Modified work Copyright 2024 kamilsss655 * https://github.com/kamilsss655 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "battery.h" #include "driver/backlight.h" #include "driver/st7565.h" #include "functions.h" #include "misc.h" #include "settings.h" #include "ui/battery.h" #include "ui/menu.h" #include "ui/ui.h" uint16_t gBatteryCalibration[6]; uint16_t gBatteryCurrentVoltage; uint16_t gBatteryVoltages[4]; uint16_t gBatteryVoltageAverage; uint8_t gBatteryDisplayLevel; bool gLowBatteryBlink; bool gLowBattery; bool gLowBatteryConfirmed; uint16_t gBatteryCheckCounter; typedef enum { BATTERY_LOW_INACTIVE, BATTERY_LOW_ACTIVE, BATTERY_LOW_CONFIRMED } BatteryLow_t; uint16_t lowBatteryCountdown; const uint16_t lowBatteryPeriod = 30; volatile uint16_t gPowerSave_10ms; unsigned int BATTERY_VoltsToPercent(const unsigned int voltage_10mV) { const uint16_t crv1600[][2] = { {828, 100}, {814, 97 }, {760, 25 }, {729, 6 }, {630, 0 }, {0, 0 } }; const uint16_t crv2200[][2] = { {832, 100}, {813, 95 }, {740, 60 }, {707, 21 }, {682, 5 }, {630, 0 }, {0, 0 } }; const BATTERY_Type_t type = gEeprom.BATTERY_TYPE; const uint16_t(*crv)[2]; uint8_t size; if (type == BATTERY_TYPE_2200_MAH) { crv = crv2200; size = ARRAY_SIZE(crv2200); } else { crv = crv1600; size = ARRAY_SIZE(crv1600); } const int mulipl = 1000; for (int i = 1; i < size; i++) { if (voltage_10mV > crv[i][0]) { int a = (crv[i - 1][1] - crv[i][1]) * mulipl / (crv[i - 1][0] - crv[i][0]); int b = crv[i][1] - a * crv[i][0] / mulipl; int p = a * voltage_10mV / mulipl + b; return MIN(p, 100); } } return 0; } void BATTERY_GetReadings(const bool bDisplayBatteryLevel) { const uint8_t PreviousBatteryLevel = gBatteryDisplayLevel; const uint16_t Voltage = (gBatteryVoltages[0] + gBatteryVoltages[1] + gBatteryVoltages[2] + gBatteryVoltages[3]) / 4; gBatteryVoltageAverage = (Voltage * 760) / gBatteryCalibration[3]; if(gBatteryVoltageAverage > 890) gBatteryDisplayLevel = 7; // battery overvoltage else if(gBatteryVoltageAverage < 630) gBatteryDisplayLevel = 0; // battery critical else { gBatteryDisplayLevel = 1; const uint8_t levels[] = {5,17,41,65,88}; uint8_t perc = BATTERY_VoltsToPercent(gBatteryVoltageAverage); for(uint8_t i = 6; i >= 1; i--){ if (perc > levels[i-2]) { gBatteryDisplayLevel = i; break; } } } if ((gScreenToDisplay == DISPLAY_MENU) && UI_MENU_GetCurrentMenuId() == MENU_VOL) gUpdateDisplay = true; if (PreviousBatteryLevel != gBatteryDisplayLevel) { if(gBatteryDisplayLevel > 2) gLowBatteryConfirmed = false; else if (gBatteryDisplayLevel < 2) { gLowBattery = true; } else { gLowBattery = false; if (bDisplayBatteryLevel) UI_DisplayBattery(gBatteryDisplayLevel, gLowBatteryBlink); } if(!gLowBatteryConfirmed) gUpdateDisplay = true; lowBatteryCountdown = 0; } } void BATTERY_TimeSlice500ms(void) { if (gLowBattery) { gLowBatteryBlink = ++lowBatteryCountdown & 1; UI_DisplayBattery(0, gLowBatteryBlink); if (gCurrentFunction != FUNCTION_TRANSMIT) { // not transmitting if (lowBatteryCountdown < lowBatteryPeriod) { if (lowBatteryCountdown == lowBatteryPeriod-1 && !gLowBatteryConfirmed) AUDIO_PlayBeep(BEEP_500HZ_60MS_DOUBLE_BEEP); } else { lowBatteryCountdown = 0; if(!gLowBatteryConfirmed) { AUDIO_PlayBeep(BEEP_500HZ_60MS_DOUBLE_BEEP); #ifdef ENABLE_VOICE AUDIO_SetVoiceID(0, VOICE_ID_LOW_VOLTAGE); #endif } if (gBatteryDisplayLevel == 0) { #ifdef ENABLE_VOICE AUDIO_PlaySingleVoice(true); #endif gReducedService = true; //if (gCurrentFunction != FUNCTION_POWER_SAVE) FUNCTION_Select(FUNCTION_POWER_SAVE); ST7565_HardwareReset(); if (gEeprom.BACKLIGHT_TIME < (ARRAY_SIZE(gSubMenu_BACKLIGHT) - 1)) BACKLIGHT_TurnOff(); // turn the backlight off } #ifdef ENABLE_VOICE else AUDIO_PlaySingleVoice(false); #endif } } } }