diff --git a/include/power.h b/include/power.h
index 12e8c032..9fd1a73c 100644
--- a/include/power.h
+++ b/include/power.h
@@ -7,6 +7,7 @@
#include "i2c.h"
#include "reset.h"
+#include "lorawan.h"
#define DEFAULT_VREF 1100 // tbd: use adc2_vref_to_gpio() for better estimate
#define NO_OF_SAMPLES 64 // we do some multisampling to get better values
@@ -15,11 +16,12 @@
#define BAT_MAX_VOLTAGE 4200 // millivolts
#endif
#ifndef BAT_MIN_VOLTAGE
-#define BAT_MIN_VOLTAGE 2800 // millivolts
+#define BAT_MIN_VOLTAGE 3100 // millivolts
#endif
+typedef uint8_t (*mapFn_t)(uint16_t, uint16_t, uint16_t);
+
uint16_t read_voltage(void);
-uint8_t read_battlevel(void);
void calibrate_voltage(void);
bool batt_sufficient(void);
@@ -34,4 +36,67 @@ void AXP192_showstatus(void);
#endif // HAS_PMU
+// The following map functions were taken from
+
+/*
+ Battery.h - Battery library
+ Copyright (c) 2014 Roberto Lo Giacco.
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+ */
+
+//
+// Plots of the functions below available at
+// https://www.desmos.com/calculator/x0esk5bsrk
+//
+
+/**
+ * Symmetric sigmoidal approximation
+ * https://www.desmos.com/calculator/7m9lu26vpy
+ *
+ * c - c / (1 + k*x/v)^3
+ */
+static inline uint8_t sigmoidal(uint16_t voltage, uint16_t minVoltage, uint16_t maxVoltage) {
+ // slow
+ // uint8_t result = 110 - (110 / (1 + pow(1.468 * (voltage - minVoltage)/(maxVoltage - minVoltage), 6)));
+
+ // steep
+ // uint8_t result = 102 - (102 / (1 + pow(1.621 * (voltage - minVoltage)/(maxVoltage - minVoltage), 8.1)));
+
+ // normal
+ uint8_t result = 105 - (105 / (1 + pow(1.724 * (voltage - minVoltage)/(maxVoltage - minVoltage), 5.5)));
+ return result >= 100 ? 100 : result;
+}
+
+/**
+ * Asymmetric sigmoidal approximation
+ * https://www.desmos.com/calculator/oyhpsu8jnw
+ *
+ * c - c / [1 + (k*x/v)^4.5]^3
+ */
+static inline uint8_t asigmoidal(uint16_t voltage, uint16_t minVoltage, uint16_t maxVoltage) {
+ uint8_t result = 101 - (101 / pow(1 + pow(1.33 * (voltage - minVoltage)/(maxVoltage - minVoltage) ,4.5), 3));
+ return result >= 100 ? 100 : result;
+}
+
+/**
+ * Linear mapping
+ * https://www.desmos.com/calculator/sowyhttjta
+ *
+ * x * 100 / v
+ */
+static inline uint8_t linear(uint16_t voltage, uint16_t minVoltage, uint16_t maxVoltage) {
+ return (unsigned long)(voltage - minVoltage) * 100 / (maxVoltage - minVoltage);
+}
+
+uint8_t read_battlevel(mapFn_t mapFunction = &sigmoidal);
+
#endif
\ No newline at end of file
diff --git a/src/cyclic.cpp b/src/cyclic.cpp
index 92d98acf..3bc4ccdb 100644
--- a/src/cyclic.cpp
+++ b/src/cyclic.cpp
@@ -68,10 +68,6 @@ void doHousekeeping() {
#if (defined BAT_MEASURE_ADC || defined HAS_PMU)
batt_level = read_battlevel();
ESP_LOGI(TAG, "Battery: %d%%", batt_level);
-#if (HAS_LORA)
- // to come with future LMIC version
- // lora_setBattLevel(batt_level);
-#endif
#ifdef HAS_PMU
AXP192_showstatus();
#endif
diff --git a/src/lorawan.cpp b/src/lorawan.cpp
index 6a033580..4e3d7469 100644
--- a/src/lorawan.cpp
+++ b/src/lorawan.cpp
@@ -485,12 +485,10 @@ void lora_setBattLevel(uint8_t batt_percent) {
#endif // HAS_PMU
else
- lmic_batt_level = static_cast(
- (float)batt_percent /
- (float)(MCMD_DEVS_BATT_MAX - MCMD_DEVS_BATT_MIN + 1) * 100.0f);
+ lmic_batt_level =
+ batt_percent / 100.0 * (MCMD_DEVS_BATT_MAX - MCMD_DEVS_BATT_MIN + 1);
LMIC_setBattLevel(lmic_batt_level);
- ESP_LOGD(TAG, "lmic_batt_level = %d", lmic_batt_level);
}
// event EV_RXCOMPLETE message handler
diff --git a/src/power.cpp b/src/power.cpp
index 47e4fb0a..c5da9a13 100644
--- a/src/power.cpp
+++ b/src/power.cpp
@@ -210,19 +210,29 @@ uint16_t read_voltage(void) {
return voltage;
}
-uint8_t read_battlevel() {
+uint8_t read_battlevel(mapFn_t mapFunction) {
- // return the battery level in values 0 ... 255 [percent],
- // values > 100 probably mean external power, depending on hardware
+ // returns the estimated battery level in values 0 ... 100 [percent],
const uint16_t batt_voltage = read_voltage();
- float batt_percent_fl = (float)(batt_voltage - BAT_MIN_VOLTAGE) /
- (float)(BAT_MAX_VOLTAGE - BAT_MIN_VOLTAGE) * 100.0f;
- const uint8_t batt_percent = static_cast(batt_percent_fl);
+ uint8_t batt_percent;
- ESP_LOGD(TAG, "batt_voltage = %dmV / batt_percent = %u%%", batt_voltage,
+ if (batt_voltage <= BAT_MIN_VOLTAGE)
+ batt_percent = 0;
+ else if (batt_voltage >= BAT_MAX_VOLTAGE)
+ batt_percent = 100;
+ else
+ batt_percent =
+ (*mapFunction)(batt_voltage, BAT_MIN_VOLTAGE, BAT_MAX_VOLTAGE);
+
+ ESP_LOGD(TAG, "batt_voltage = %dmV / batt_percent = %d%%", batt_voltage,
batt_percent);
+#if (HAS_LORA)
+ // to come with future LMIC version
+ // lora_setBattLevel(batt_percent);
+#endif
+
return batt_percent;
}