Merge develop into main (#313)
- Add app path get() functions to `TactilityC` - Improved `Dispatcher` and `DispatcherThread` - Improved `PubSub` (type safety) - Created test for `DispatcherThread` and `PubSub` - Save properties files on app exit (various apps) by posting it to the main dispatcher (fixes UI hanging briefly on app exit) - Fixed bug with `SystemSettings` being read from the wrong file path. - `loadPropertiesFile()` now uses `file::readLines()` instead of doing that manually - Increased timer task stack size (required due to issues when reading a properties file for the very first time) - General cleanup - Created `EstimatedPower` driver that uses an ADC pin to measure voltage and estimate the battery charge that is left. - Cleanup of T-Deck board (updated to new style)
This commit is contained in:
committed by
GitHub
parent
5cc5b50694
commit
0f8380e8fe
@@ -0,0 +1,67 @@
|
||||
#include "ChargeFromAdcVoltage.h"
|
||||
#include <Tactility/Log.h>
|
||||
#include <algorithm>
|
||||
|
||||
constexpr auto TAG = "EstimatePower";
|
||||
constexpr auto MAX_VOLTAGE_SAMPLES = 15;
|
||||
|
||||
uint8_t ChargeFromAdcVoltage::estimateChargeLevelFromVoltage(uint32_t milliVolt) const {
|
||||
const float volts = std::min((float)milliVolt / 1000.f, configuration.batteryVoltageMax);
|
||||
const float voltage_percentage = (volts - configuration.batteryVoltageMin) / (configuration.batteryVoltageMax - configuration.batteryVoltageMin);
|
||||
const float voltage_factor = std::min(1.0f, voltage_percentage);
|
||||
const auto charge_level = (uint8_t) (voltage_factor * 100.f);
|
||||
TT_LOG_V(TAG, "mV = %lu, scaled = %.2f, factor = %.2f, result = %d", milliVolt, volts, voltage_factor, charge_level);
|
||||
return charge_level;
|
||||
}
|
||||
|
||||
ChargeFromAdcVoltage::ChargeFromAdcVoltage(const Configuration& configuration) : configuration(configuration) {
|
||||
if (adc_oneshot_new_unit(&configuration.adcConfig, &adcHandle) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "ADC config failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (adc_oneshot_config_channel(adcHandle, configuration.adcChannel, &configuration.adcChannelConfig) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "ADC channel config failed");
|
||||
|
||||
adc_oneshot_del_unit(adcHandle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ChargeFromAdcVoltage::~ChargeFromAdcVoltage() {
|
||||
if (adcHandle) {
|
||||
adc_oneshot_del_unit(adcHandle);
|
||||
}
|
||||
}
|
||||
|
||||
bool ChargeFromAdcVoltage::readBatteryVoltageOnce(uint32_t& output) const {
|
||||
int raw;
|
||||
if (adc_oneshot_read(adcHandle, configuration.adcChannel, &raw) == ESP_OK) {
|
||||
output = configuration.adcMultiplier * ((1000.f * configuration.adcRefVoltage) / 4096.f) * (float)raw;
|
||||
TT_LOG_V(TAG, "Raw = %d, voltage = %lu", raw, output);
|
||||
return true;
|
||||
} else {
|
||||
TT_LOG_E(TAG, "Read failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ChargeFromAdcVoltage::readBatteryVoltageSampled(uint32_t& output) const {
|
||||
size_t samples_read = 0;
|
||||
uint32_t sample_accumulator = 0;
|
||||
uint32_t sample_read_buffer;
|
||||
|
||||
for (size_t i = 0; i < MAX_VOLTAGE_SAMPLES; ++i) {
|
||||
if (readBatteryVoltageOnce(sample_read_buffer)) {
|
||||
sample_accumulator += sample_read_buffer;
|
||||
samples_read++;
|
||||
}
|
||||
}
|
||||
|
||||
if (samples_read == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
output = sample_accumulator / samples_read;
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <esp_adc/adc_oneshot.h>
|
||||
|
||||
class ChargeFromAdcVoltage {
|
||||
|
||||
public:
|
||||
|
||||
struct Configuration {
|
||||
adc_channel_t adcChannel = ADC_CHANNEL_3;
|
||||
float adcMultiplier = 1.0f;
|
||||
float adcRefVoltage = 3.3f;
|
||||
float batteryVoltageMin = 3.2f;
|
||||
float batteryVoltageMax = 4.2f;
|
||||
adc_oneshot_unit_init_cfg_t adcConfig = {
|
||||
.unit_id = ADC_UNIT_1,
|
||||
.clk_src = ADC_RTC_CLK_SRC_DEFAULT,
|
||||
.ulp_mode = ADC_ULP_MODE_DISABLE,
|
||||
};
|
||||
adc_oneshot_chan_cfg_t adcChannelConfig = {
|
||||
.atten = ADC_ATTEN_DB_12,
|
||||
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||
};
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
adc_oneshot_unit_handle_t adcHandle = nullptr;
|
||||
Configuration configuration;
|
||||
|
||||
public:
|
||||
|
||||
explicit ChargeFromAdcVoltage(const Configuration& configuration);
|
||||
|
||||
~ChargeFromAdcVoltage();
|
||||
|
||||
uint8_t estimateChargeLevelFromVoltage(uint32_t milliVolt) const;
|
||||
|
||||
bool readBatteryVoltageSampled(uint32_t& output) const;
|
||||
|
||||
bool readBatteryVoltageOnce(uint32_t& output) const;
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "EstimatedPower.h"
|
||||
|
||||
bool EstimatedPower::supportsMetric(MetricType type) const {
|
||||
switch (type) {
|
||||
using enum MetricType;
|
||||
case BatteryVoltage:
|
||||
case ChargeLevel:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool EstimatedPower::getMetric(MetricType type, MetricData& data) {
|
||||
switch (type) {
|
||||
using enum MetricType;
|
||||
case BatteryVoltage:
|
||||
return chargeFromAdcVoltage->readBatteryVoltageSampled(data.valueAsUint32);
|
||||
case ChargeLevel:
|
||||
if (chargeFromAdcVoltage->readBatteryVoltageSampled(data.valueAsUint32)) {
|
||||
data.valueAsUint32 = chargeFromAdcVoltage->estimateChargeLevelFromVoltage(data.valueAsUint32);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <ChargeFromAdcVoltage.h>
|
||||
#include <Tactility/hal/power/PowerDevice.h>
|
||||
|
||||
using tt::hal::power::PowerDevice;
|
||||
|
||||
/**
|
||||
* Uses Voltage measurements to estimate charge.
|
||||
* Supports voltage and charge level metrics.
|
||||
* Can be overridden to further extend supported metrics.
|
||||
*/
|
||||
class EstimatedPower final : public PowerDevice {
|
||||
|
||||
std::unique_ptr<ChargeFromAdcVoltage> chargeFromAdcVoltage;
|
||||
|
||||
public:
|
||||
|
||||
EstimatedPower(ChargeFromAdcVoltage::Configuration configuration) :
|
||||
chargeFromAdcVoltage(std::make_unique<ChargeFromAdcVoltage>(std::move(configuration))) {}
|
||||
|
||||
std::string getName() const override { return "ADC Power Measurement"; }
|
||||
std::string getDescription() const override { return "Power measurement interface via ADC pin"; }
|
||||
|
||||
bool supportsMetric(MetricType type) const override;
|
||||
bool getMetric(MetricType type, MetricData& data) override;
|
||||
};
|
||||
Reference in New Issue
Block a user