mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-02-18 19:03:16 +00:00
This commit contains @josemalm32 's implementation for the Guition JC1060P470CIWY (see https://github.com/ByteWelder/Tactility/issues/427) I've added these changes: - Updated the branch for the new logging method - Updated the branch for the PR that I mentioned in the above linked issue - Replaced the manually pasted in esp_lcd_jd9165 driver with the one from the component registry - Updated Spanish to English - Updated all drivers' mutexes/locks - Fixed the display color format - Fixed bug in power deinit - Renamed I2C bus in config - Added device to continuous integration - Renamed several Guition devices from CYD to Guition - Fix for `EspLcdDisplayV2` init for when features are not supported - Pin esp_wifi_remote to version 1.2.3 - Fix in `WifiManage` logging - Fix for `WifiEsp.cpp`'s check for wifi presence - Fix for `WifiEsp`'s scan list logging - Fix for `gcc_soft_float_symbols` in TactiltyC
181 lines
5.4 KiB
C++
181 lines
5.4 KiB
C++
#include "Power.h"
|
|
|
|
#include <Tactility/Logger.h>
|
|
#include <ChargeFromVoltage.h>
|
|
#include <esp_adc/adc_oneshot.h>
|
|
#include <esp_adc/adc_cali.h>
|
|
#include <esp_adc/adc_cali_scheme.h>
|
|
|
|
using tt::hal::power::PowerDevice;
|
|
|
|
static const auto LOGGER = tt::Logger("JcPower");
|
|
|
|
namespace {
|
|
|
|
constexpr adc_unit_t ADC_UNIT = ADC_UNIT_2;
|
|
constexpr adc_channel_t ADC_CHANNEL = ADC_CHANNEL_4; // matches ADC2 CH4 used in brookesia config
|
|
constexpr adc_atten_t ADC_ATTEN = ADC_ATTEN_DB_12;
|
|
constexpr int32_t UPPER_RESISTOR_OHM = 85'000; // per brookesia settings
|
|
constexpr int32_t LOWER_RESISTOR_OHM = 100'000; // per brookesia settings
|
|
|
|
class JcPower final : public PowerDevice {
|
|
public:
|
|
JcPower() : chargeEstimator(3.3f, 4.2f) {}
|
|
~JcPower() override { deinit(); }
|
|
|
|
std::string getName() const override { return "JC Power"; }
|
|
std::string getDescription() const override { return "Battery voltage via ADC"; }
|
|
|
|
bool supportsMetric(MetricType type) const override {
|
|
switch (type) {
|
|
using enum MetricType;
|
|
case BatteryVoltage:
|
|
case ChargeLevel:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool getMetric(MetricType type, MetricData& data) override {
|
|
if (!ensureInit()) {
|
|
return false;
|
|
}
|
|
|
|
uint32_t batteryMv = 0;
|
|
if (!readBatteryMilliVolt(batteryMv)) {
|
|
return false;
|
|
}
|
|
|
|
switch (type) {
|
|
case MetricType::BatteryVoltage:
|
|
data.valueAsUint32 = batteryMv;
|
|
return true;
|
|
case MetricType::ChargeLevel:
|
|
data.valueAsUint8 = chargeEstimator.estimateCharge(batteryMv);
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private:
|
|
bool ensureInit() {
|
|
if (initialized) {
|
|
return true;
|
|
}
|
|
|
|
adc_oneshot_unit_init_cfg_t init_cfg = {
|
|
.unit_id = ADC_UNIT,
|
|
.clk_src = ADC_RTC_CLK_SRC_DEFAULT,
|
|
.ulp_mode = ADC_ULP_MODE_DISABLE,
|
|
};
|
|
if (adc_oneshot_new_unit(&init_cfg, &adcHandle) != ESP_OK) {
|
|
LOGGER.error("ADC unit init failed");
|
|
return false;
|
|
}
|
|
|
|
adc_oneshot_chan_cfg_t chan_cfg = {
|
|
.atten = ADC_ATTEN,
|
|
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
|
};
|
|
if (adc_oneshot_config_channel(adcHandle, ADC_CHANNEL, &chan_cfg) != ESP_OK) {
|
|
LOGGER.error("ADC channel config failed");
|
|
adc_oneshot_del_unit(adcHandle);
|
|
adcHandle = nullptr;
|
|
return false;
|
|
}
|
|
|
|
calibrated = tryInitCalibration();
|
|
initialized = true;
|
|
return true;
|
|
}
|
|
|
|
bool tryInitCalibration() {
|
|
#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
|
adc_cali_line_fitting_config_t cali_config = {
|
|
.unit_id = ADC_UNIT,
|
|
.atten = ADC_ATTEN,
|
|
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
|
};
|
|
if (adc_cali_create_scheme_line_fitting(&cali_config, &caliHandle) == ESP_OK) {
|
|
calScheme = CaliScheme::Line;
|
|
LOGGER.info("ADC calibration (line fitting) enabled");
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
|
adc_cali_curve_fitting_config_t curve_cfg = {
|
|
.unit_id = ADC_UNIT,
|
|
.chan = ADC_CHANNEL,
|
|
.atten = ADC_ATTEN,
|
|
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
|
};
|
|
if (adc_cali_create_scheme_curve_fitting(&curve_cfg, &caliHandle) == ESP_OK) {
|
|
calScheme = CaliScheme::Curve;
|
|
LOGGER.info("ADC calibration (curve fitting) enabled");
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
LOGGER.warn("ADC calibration not available, using raw scaling");
|
|
return false;
|
|
}
|
|
|
|
bool readBatteryMilliVolt(uint32_t& outMv) {
|
|
int raw = 0;
|
|
if (adc_oneshot_read(adcHandle, ADC_CHANNEL, &raw) != ESP_OK) {
|
|
LOGGER.error("ADC read failed");
|
|
return false;
|
|
}
|
|
|
|
int mv = 0;
|
|
if (calibrated && adc_cali_raw_to_voltage(caliHandle, raw, &mv) == ESP_OK) {
|
|
// ok
|
|
} else {
|
|
// Fallback: approximate assuming 12-bit full scale 3.3V
|
|
mv = (raw * 3300) / 4095;
|
|
}
|
|
|
|
const int64_t numerator = static_cast<int64_t>(UPPER_RESISTOR_OHM + LOWER_RESISTOR_OHM) * mv;
|
|
const int64_t denominator = LOWER_RESISTOR_OHM;
|
|
outMv = static_cast<uint32_t>(numerator / denominator);
|
|
return true;
|
|
}
|
|
|
|
void deinit() {
|
|
if (adcHandle) {
|
|
adc_oneshot_del_unit(adcHandle);
|
|
adcHandle = nullptr;
|
|
}
|
|
if (caliHandle) {
|
|
if (calScheme == CaliScheme::Line) {
|
|
#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
|
adc_cali_delete_scheme_line_fitting(caliHandle);
|
|
#endif
|
|
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
|
} else if (calScheme == CaliScheme::Curve) {
|
|
adc_cali_delete_scheme_curve_fitting(caliHandle);
|
|
#endif
|
|
}
|
|
caliHandle = nullptr;
|
|
calibrated = false;
|
|
}
|
|
}
|
|
|
|
enum class CaliScheme { None, Line, Curve };
|
|
|
|
bool initialized = false;
|
|
bool calibrated = false;
|
|
CaliScheme calScheme = CaliScheme::None;
|
|
adc_oneshot_unit_handle_t adcHandle = nullptr;
|
|
adc_cali_handle_t caliHandle = nullptr;
|
|
ChargeFromVoltage chargeEstimator;
|
|
};
|
|
} // namespace
|
|
|
|
std::shared_ptr<PowerDevice> createPower() {
|
|
return std::make_shared<JcPower>();
|
|
}
|