Ken Van Hoeylandt c98cb2bf10
Add Guition JC1060P470CIWY and update other Guition device IDs (#447)
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
2026-01-07 22:41:45 +01:00

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>();
}