diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/UITask.cpp index a7f03a26..6d7eb58d 100644 --- a/examples/companion_radio/UITask.cpp +++ b/examples/companion_radio/UITask.cpp @@ -143,11 +143,7 @@ void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, i } } -void UITask::renderBatteryIndicator(uint16_t batteryMilliVolts) { - // Convert millivolts to percentage - const int minMilliVolts = 3000; // Minimum voltage (e.g., 3.0V) - const int maxMilliVolts = 4200; // Maximum voltage (e.g., 4.2V) - int batteryPercentage = ((batteryMilliVolts - minMilliVolts) * 100) / (maxMilliVolts - minMilliVolts); +void UITask::renderBatteryIndicator(uint16_t batteryPercentage) { if (batteryPercentage < 0) batteryPercentage = 0; // Clamp to 0% if (batteryPercentage > 100) batteryPercentage = 100; // Clamp to 100% @@ -222,7 +218,7 @@ void UITask::renderCurrScreen() { _display->print(_node_prefs->node_name); // battery voltage - renderBatteryIndicator(_board->getBattMilliVolts()); + renderBatteryIndicator(_board->getBattPercent()); // freq / sf _display->setCursor(0, 20); diff --git a/examples/companion_radio/UITask.h b/examples/companion_radio/UITask.h index 77ef875f..a0ff6aa5 100644 --- a/examples/companion_radio/UITask.h +++ b/examples/companion_radio/UITask.h @@ -51,7 +51,7 @@ class UITask { void renderCurrScreen(); void userLedHandler(); - void renderBatteryIndicator(uint16_t batteryMilliVolts); + void renderBatteryIndicator(uint16_t batteryPercentage); // Button action handlers void handleButtonAnyPress(); diff --git a/src/MeshCore.h b/src/MeshCore.h index d8886136..839bab8b 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -1,6 +1,7 @@ #pragma once #include +#include "helpers/Battery.h" #define MAX_HASH_SIZE 8 #define PUB_KEY_SIZE 32 @@ -36,6 +37,11 @@ namespace mesh { class MainBoard { public: virtual uint16_t getBattMilliVolts() = 0; + uint16_t getBattPercent() { + uint16_t mv = getBattMilliVolts(); + return batt_percent(getBattType(), mv); + } + uint8_t getBattType() { return BATT_LIPO; }; virtual const char* getManufacturerName() const = 0; virtual void onBeforeTransmit() { } virtual void onAfterTransmit() { } diff --git a/src/helpers/Battery.cpp b/src/helpers/Battery.cpp new file mode 100644 index 00000000..3deb5db6 --- /dev/null +++ b/src/helpers/Battery.cpp @@ -0,0 +1,18 @@ +#include +#include "Battery.h" + +uint16_t batt_percent(uint8_t batt, uint16_t mv) { + uint16_t last = mv; // set last to mv so it can never start greater than itself + uint16_t p = 0; + // 101 elements including 0 percentile + for (int i = 0; i < 101; i++) { + // if we match the current value or are between the current and last value + // we return the lower of the the two, giving accuracy +/- 1% soc + if (mv <= last && mv >= batt_curve[i][batt]) { + p = batt_curve[i][batt_p_col]; + break; + } + last = batt_curve[i][batt]; + } + return p; +}; diff --git a/src/helpers/Battery.h b/src/helpers/Battery.h new file mode 100644 index 00000000..888f3017 --- /dev/null +++ b/src/helpers/Battery.h @@ -0,0 +1,122 @@ +/* +helpers/Battery.h defines battery chemistry types and their discharge curves +*/ + +// BATT_TYPE is a unique ID for battery chemistry type +// NOTE: enum for curve must match index in table curve +enum BATT_TYPE { BATT_LIPO, BATT_LIFEPO4 }; + +uint16_t batt_percent(uint8_t batt, uint16_t mv); + +// batt_p_col is the column index of the battery percentage in the below table +const int batt_p_col = 2; + +// batt_curve maps the battery voltage in mV to +// the approximate SoC for a typical cell +// Each column represents the mV for a given Soc foe that chemistry +// COL1 = LIPO, COL2 = LIFEPO4, COL3 = percent charge +// reference used https://blog.ampow.com/lipo-voltage-chart/ +// reference used https://cleversolarpower.com/lifepo4-voltage-chart/ +const uint16_t batt_curve[101][3] = { + { 4200, 3400, 100 }, + { 4190, 3395, 99 }, + { 4180, 3390, 98 }, + { 4170, 3385, 97 }, + { 4160, 3380, 96 }, + { 4150, 3375, 95 }, + { 4142, 3370, 94 }, + { 4134, 3365, 93 }, + { 4126, 3360, 92 }, + { 4118, 3355, 91 }, + { 4110, 3350, 90 }, + { 4104, 3347, 89 }, + { 4098, 3344, 88 }, + { 4092, 3341, 87 }, + { 4086, 3338, 86 }, + { 4080, 3335, 85 }, + { 4068, 3332, 84 }, + { 4056, 3329, 83 }, + { 4044, 3326, 82 }, + { 4032, 3323, 81 }, + { 4020, 3320, 80 }, + { 4012, 3318, 79 }, + { 4004, 3316, 78 }, + { 3996, 3314, 77 }, + { 3988, 3312, 76 }, + { 3980, 3310, 75 }, + { 3974, 3308, 74 }, + { 3968, 3306, 73 }, + { 3962, 3304, 72 }, + { 3956, 3302, 71 }, + { 3950, 3300, 70 }, + { 3942, 3297, 69 }, + { 3934, 3294, 68 }, + { 3926, 3291, 67 }, + { 3918, 3288, 66 }, + { 3910, 3285, 65 }, + { 3902, 3282, 64 }, + { 3894, 3279, 63 }, + { 3886, 3276, 62 }, + { 3878, 3273, 61 }, + { 3870, 3270, 60 }, + { 3866, 3269, 59 }, + { 3862, 3268, 58 }, + { 3858, 3267, 57 }, + { 3854, 3266, 56 }, + { 3850, 3265, 55 }, + { 3848, 3264, 54 }, + { 3846, 3263, 53 }, + { 3844, 3262, 52 }, + { 3842, 3261, 51 }, + { 3840, 3260, 50 }, + { 3836, 3259, 49 }, + { 3832, 3258, 48 }, + { 3828, 3257, 47 }, + { 3824, 3256, 46 }, + { 3820, 3255, 45 }, + { 3816, 3254, 44 }, + { 3812, 3253, 43 }, + { 3808, 3252, 42 }, + { 3804, 3251, 41 }, + { 3800, 3250, 40 }, + { 3798, 3247, 39 }, + { 3796, 3244, 38 }, + { 3794, 3241, 37 }, + { 3792, 3238, 36 }, + { 3790, 3235, 35 }, + { 3786, 3232, 34 }, + { 3782, 3229, 33 }, + { 3778, 3226, 32 }, + { 3774, 3223, 31 }, + { 3770, 3220, 30 }, + { 3766, 3218, 29 }, + { 3762, 3216, 28 }, + { 3758, 3214, 27 }, + { 3754, 3212, 26 }, + { 3750, 3210, 25 }, + { 3746, 3208, 24 }, + { 3742, 3206, 23 }, + { 3738, 3204, 22 }, + { 3724, 3202, 21 }, + { 3730, 3200, 20 }, + { 3726, 3180, 19 }, + { 3722, 3160, 18 }, + { 3718, 3140, 17 }, + { 3714, 3120, 16 }, + { 3710, 3100, 15 }, + { 3706, 3080, 14 }, + { 3702, 3060, 13 }, + { 3698, 3040, 12 }, + { 3694, 3020, 11 }, + { 3690, 3000, 10 }, + { 3674, 2950, 9 }, + { 3658, 2900, 8 }, + { 3642, 2850, 7 }, + { 3626, 2800, 6 }, + { 3610, 2750, 5 }, + { 3542, 2700, 4 }, + { 3474, 2650, 3 }, + { 3406, 2600, 2 }, + { 3338, 2550, 1 }, + { 3270, 2500, 0 } +};