From c4c5e65f646228bf222b0a0ed18e4009483ab2f5 Mon Sep 17 00:00:00 2001 From: willem Date: Tue, 14 Dec 2021 11:25:36 +0100 Subject: [PATCH] initial commit --- src/Battery.cpp | 74 +++++++++++++++++++++++++++ src/Battery.h | 129 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 src/Battery.cpp create mode 100644 src/Battery.h diff --git a/src/Battery.cpp b/src/Battery.cpp new file mode 100644 index 0000000..1e32103 --- /dev/null +++ b/src/Battery.cpp @@ -0,0 +1,74 @@ +/* + Battery.cpp - 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 . + */ + +#include "Battery.h" +#include + +Battery::Battery(uint16_t minVoltage, uint16_t maxVoltage, uint8_t sensePin) { + this->sensePin = sensePin; + this->activationPin = 0xFF; + this->minVoltage = minVoltage; + this->maxVoltage = maxVoltage; +} + +void Battery::begin(uint16_t refVoltage, float dividerRatio, mapFn_t mapFunction) { + this->refVoltage = refVoltage; + this->dividerRatio = dividerRatio; + pinMode(this->sensePin, INPUT); + if (this->activationPin < 0xFF) { + pinMode(this->activationPin, OUTPUT); + } + this->mapFunction = mapFunction ? mapFunction : &linear; +} + +void Battery::onDemand(uint8_t activationPin, uint8_t activationMode) { + if (activationPin < 0xFF) { + this->activationPin = activationPin; + this->activationMode = activationMode; + pinMode(this->activationPin, OUTPUT); + digitalWrite(activationPin, !activationMode); + } +} + +uint8_t Battery::level() { + return this->level(this->voltage()); +} + +uint8_t Battery::level(uint16_t voltage) { + if (voltage <= minVoltage) { + return 0; + } else if (voltage >= maxVoltage) { + return 100; + } else { + return (*mapFunction)(voltage, minVoltage, maxVoltage); + } +} + +uint16_t Battery::voltage() { + if (activationPin != 0xFF) { + digitalWrite(activationPin, activationMode); + delayMicroseconds(10); // copes with slow switching activation circuits + } + analogRead(sensePin); + delay(2); // allow the ADC to stabilize + uint16_t reading = analogRead(sensePin) * dividerRatio * refVoltage / 1024; + if (activationPin != 0xFF) { + digitalWrite(activationPin, !activationMode); + } + return reading; +} diff --git a/src/Battery.h b/src/Battery.h new file mode 100644 index 0000000..ccd283a --- /dev/null +++ b/src/Battery.h @@ -0,0 +1,129 @@ +/* + 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 . + */ + +#ifndef BATTERY_H_ +#define BATTERY_H_ + +#include + +typedef uint8_t(*mapFn_t)(uint16_t, uint16_t, uint16_t); + +class Battery { + public: + /** + * Creates an instance to monitor battery voltage and level. + * Initialization parameters depend on battery type and configuration. + * + * @param minVoltage is the voltage, expressed in millivolts, corresponding to an empty battery + * @param maxVoltage is the voltage, expressed in millivolts, corresponding to a full battery + * @param sensePin is the analog pin used for sensing the battery voltage + */ + Battery(uint16_t minVoltage, uint16_t maxVoltage, uint8_t sensePin); + + /** + * Initializes the library by optionally setting additional parameters. + * To obtain the best results use a calibrated reference using the VoltageReference library or equivalent. + * + * @param refVoltage is the board reference voltage, expressed in millivolts + * @param dividerRatio is the multiplier used to obtain the real battery voltage + * @param mapFunction is a pointer to the function used to map the battery voltage to the remaining capacity percentage (defaults to linear mapping) + */ + void begin(uint16_t refVoltage, float dividerRatio, mapFn_t = 0); + + /** + * Enables on-demand activation of the sensing circuit to limit battery consumption. + * + * @param activationPin is the pin which will be turned HIGH or LOW before starting the battery sensing + * @param activationMode is the optional value to set on the activationPin to enable battery sensing, defaults to LOW + * useful when using a resistor divider to save on battery consumption, but it can be changed to HIGH in case + * you are using a P-CH MOSFET or a PNP BJT + */ + void onDemand(uint8_t activationPin, uint8_t activationMode = LOW); + + /** + * Activation pin value disabling the on-demand feature. + */ + static const uint8_t ON_DEMAND_DISABLE = 0xFF; + + /** + * Returns the current battery level as a number between 0 and 100, with 0 indicating an empty battery and 100 a + * full battery. + */ + uint8_t level(); + uint8_t level(uint16_t voltage); + + /** + * Returns the current battery voltage in millivolts. + */ + uint16_t voltage(); + + private: + uint16_t refVoltage; + uint16_t minVoltage; + uint16_t maxVoltage; + float dividerRatio; + uint8_t sensePin; + uint8_t activationPin; + uint8_t activationMode; + mapFn_t mapFunction; +}; + +// +// 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); +} +#endif // BATTERY_H_