Skip to content
Merged
13 changes: 13 additions & 0 deletions hal/ArduinoHAL.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ using String = std::string;
#define INPUT 0
#define HIGH 1
#define LOW 0
#define ADC_VOLTAGE 192

inline void pinMode(int pin, int mode) { //NOLINT
// Do nothing
Expand All @@ -37,6 +38,18 @@ inline void digitalWrite(int pin, int value) { //NOLINT
// Do nothing
}

inline void analogReadResolution(int bits) {
// Do nothing, we will just return a 12-bit value in analogRead
}

inline uint32_t analogRead(int pin) {
// Return a dummy 12 bit value for the voltage pin, and 0 for other pins.
// This allows us to test the battery voltage reading functionality without needing a real ADC.
if (pin == ADC_VOLTAGE) {
return static_cast<uint32_t>((1 << 12) - 1); // Return full-scale value for a 12-bit ADC
}
return 0; // Default dummy value for other pins
}

// millis mock which still gives us the time since the program started in milliseconds
static auto program_start = std::chrono::high_resolution_clock::now();
Expand Down
52 changes: 33 additions & 19 deletions include/PowerManagement.h
Original file line number Diff line number Diff line change
@@ -1,50 +1,64 @@
#ifndef PowerManagement_H
#define PowerManagement_H

#include "Arduino.h"
#include "pins.h"
#include "ArduinoHAL.h"

/**
* @brief Simple ADC-based battery voltage helper.
* @note When to use: quick health checks of the main battery using an analog
* pin and known divider scaling.
* This class provides a way to read the battery voltage using an ADC pin and a scaling factor.
*
* The correct pin can be found in the hardware scematics under "ADC_VOLTAGE"
* and the scaling factor can be calculated based on the voltage divider used in the hardware design.
* For MARTHA 1.4, the voltage divider is 200k and 1k5, so the factor is (200k + 1k5) / 1k5 = 134.33333.
*
* The voltage at the pin is calculated as:
* vPin = (rawValue / (2^numAdcBits - 1)) * vRef
* where rawValue is the ADC reading, numAdcBits is the resolution of the ADC, and vRef is the reference voltage for the ADC (3.3V for MARTHA 1.4).
* The battery voltage is then calculated as:
* vBat = vPin * factor
*/
class BatteryVoltage {
public:
BatteryVoltage(uint8_t adcPin, float conversionFactor)
: pin(adcPin), factor(conversionFactor) {}
/**
* @param adcPin The ADC pin connected to the battery voltage divider.
* @param factor The scaling factor to convert the pin voltage to battery voltage.
* @param numAdcBits The resolution of the ADC (e.g., 12 for 12-bit ADC).
* @param voltageThreshold A threshold voltage for low battery checks (not used in current implementation but can be useful for future extensions).
*/
BatteryVoltage(uint8_t adcPin, float factor, int numAdcBits, float voltageThreshold)
: pin(adcPin), factor(factor), numAdcBits(numAdcBits), voltageThreshold(voltageThreshold) {analogReadResolution(numAdcBits);}

/**
* @brief Sample the ADC and convert to battery voltage.
* @return Converted voltage reading.
* @note When to use: periodic health checks or brownout warnings.
*/
float readVoltage() {
// Set the pin for reading
pinMode(pin, INPUT);
uint32_t rawValue = 0;
rawValue = analogRead(pin); // Should give a value between 0 and 1023, not sure how to convert this to the true voltage.
// Conversion notes -> respect to Vref you would do Vref * analogRead()/2^12.
// After that you can use the voltage divider equation to get the battery voltage level
// Vref is 134.33?
// float voltage = pow((rawValue/2),12) * factor;
Serial.println(rawValue);
return rawValue;
uint32_t rawValue = analogRead(pin); // Should give a value between 0 and 2^numAdcBits - 1
float vRef = 3.3f; // reference voltage for the ADC on MARTHA 1.4

// Convert raw ADC value to voltage at the pin, then apply the factor to get battery voltage
float vPin = (static_cast<float>(rawValue) / (static_cast<float>(1 << numAdcBits) - 1)) * vRef;
float vBat = vPin * factor;

return vBat;
}

/**
* @brief Check whether voltage exceeds a minimal threshold.
* @return true if voltage is above the survival threshold.
* @note When to use: lightweight go/no-go checks before more detailed
* power analysis.
* @note Could be useful for future extension, like triggering a low power mode
*/
bool isAlive(){
return readVoltage() > .3; // Not sure what a good cutoff is
bool isLow(){
return readVoltage() < voltageThreshold;
}

private:
uint8_t pin;
float factor;
int numAdcBits;
float voltageThreshold;
};

#endif // PowerManagement_H