Battery Driver Issue

nyameaama
Posts: 11
Joined: Fri Jul 28, 2023 7:43 am

Battery Driver Issue

Postby nyameaama » Sat Dec 02, 2023 10:02 am

Hi, I wrote this implementation to calculate the battery percentage using the voltage however the battery percentage readings seem off and the output stays at 100 percent for most of the time. Can you spot the issues in the code?


#include"_battery.h"
//https://en.ovcharov.me/2020/02/29/how-t ... ontroller/

#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "driver/adc.h"
#include "esp_adc_cal.h"
#include "esp_log.h"

#define DEFAULT_VREF 1100 // Use adc2_vref_to_gpio() to obtain a better estimate
#define NO_OF_SAMPLES 16 // Multisampling

#define TAG "Meter"

static esp_adc_cal_characteristics_t *adc_chars;

static const adc_channel_t channel = ADC_CHANNEL_7; //GPIO35 if ADC1, GPIO14 if ADC2
static const adc_bits_width_t width = ADC_WIDTH_BIT_12;

static const adc_atten_t atten = ADC_ATTEN_DB_11;
static const adc_unit_t unit = ADC_UNIT_1;

#define ACS724LLCTR_OUTPUT_PIN ADC_CHANNEL_6 //GPIO34
#define SENSITIVITY 40.0 // Sensitivity of ACS724LLCTR in mV/A

#define R2 300
#define R3 100

#define VOLTAGE_OUT(Vin) (((Vin) * R3) / (R2 + R3))

#define VOLTAGE_MAX 12.6
#define VOLTAGE_MIN 10

#define VOLTAGE_TO_ADC(in) ((DEFAULT_VREF * (in)) / 4096)

#define BATTERY_MAX_ADC VOLTAGE_TO_ADC(VOLTAGE_OUT(VOLTAGE_MAX))
#define BATTERY_MIN_ADC VOLTAGE_TO_ADC(VOLTAGE_OUT(VOLTAGE_MIN))


//________________________________________________________________________
/* Check the eFuse settings
===========================================================================
| void
===========================================================================
*/
void BATTERY::check_efuse(void)
{
// Check if TP is burned into eFuse
if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP) == ESP_OK) {
ESP_LOGI(TAG, "eFuse Two Point: Supported");
} else {
ESP_LOGI(TAG, "eFuse Two Point: NOT supported");
}
// Check Vref is burned into eFuse
if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_VREF) == ESP_OK) {
ESP_LOGI(TAG, "eFuse Vref: Supported");
} else {
ESP_LOGI(TAG, "eFuse Vref: NOT supported");
}
}

//________________________________________________________________________
/* Print the character value type for ADC calibration
===========================================================================
| void
===========================================================================
*/
void BATTERY::print_char_val_type(esp_adc_cal_value_t val_type)
{
if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) {
ESP_LOGI(TAG, "Characterized using Two Point Value");
} else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) {
ESP_LOGI(TAG, "Characterized using eFuse Vref");
} else {
ESP_LOGI(TAG, "Characterized using Default Vref");
}
}

//________________________________________________________________________
/* Initialize the battery interface
===========================================================================
| Returns: double - The initialized battery voltage.
===========================================================================
*/
double BATTERY::batteryInterfaceInit()
{
// Check if Two Point or Vref are burned into eFuse
//check_efuse();

// Configure ADC
if (unit == ADC_UNIT_1) {
adc1_config_width(width);
adc1_config_channel_atten((adc1_channel_t)channel, atten);
} else {
adc2_config_channel_atten((adc2_channel_t)channel, atten);
}

// Characterize ADC
adc_chars = (esp_adc_cal_characteristics_t*)calloc(1, sizeof(esp_adc_cal_characteristics_t));
esp_adc_cal_value_t val_type = esp_adc_cal_characterize(unit, atten, width, DEFAULT_VREF, adc_chars);
//print_char_val_type(val_type);

// Sample ADC1
double adc_reading = 0;
// Multisampling
for (int i = 0; i < NO_OF_SAMPLES; i++) {
if (unit == ADC_UNIT_1) {
adc_reading += adc1_get_raw((adc1_channel_t)channel);
} else {
int raw;
adc2_get_raw((adc2_channel_t)channel, width, &raw);
adc_reading += raw;
}
}
adc_reading /= NO_OF_SAMPLES;

return adc_reading;
}

//________________________________________________________________________
/* Get the battery percentage
===========================================================================
| Returns: double - The battery percentage.
===========================================================================
*/
double BATTERY::returnBatteryPercent(){
double adc = returnBatteryVoltage();
// Calculate the ratio of the value within the source range
//Attenuation Low = 150 mv
//Attenuation High = 2450 mv
// Battery voltage scale (10V to 12.6V)
double vrs = mapValue(adc, 150, 2450, VOLTAGE_MIN, VOLTAGE_MAX);

//ESP_LOGI("TAG","VA: %f",vrs);
//Now we do the same to get a percent value
// Percent Value Scale (0% to 100%)
double brp = mapValue(vrs, VOLTAGE_MIN, VOLTAGE_MAX, 0, 100);

return brp;
}

//________________________________________________________________________
/* Get the current battery voltage
===========================================================================
| Returns: double - The current battery voltage.
===========================================================================
*/
double BATTERY::returnBatteryVoltage(){
double b_II = batteryInterfaceInit();
//Convert adc_reading to voltage in mV
double voltage = esp_adc_cal_raw_to_voltage(b_II, adc_chars);
return voltage;
}

//________________________________________________________________________
/* Get the current battery current draw
===========================================================================
| Returns: double - The current battery current draw.
===========================================================================
*/
double BATTERY::returnBatteryCurrentDraw(){
initCurrentADC();
// Sample ADC1
int adc_reading = 0;
// Multisampling
for (int i = 0; i < NO_OF_SAMPLES; i++) {
if (unit == ADC_UNIT_1) {
adc_reading += adc1_get_raw((adc1_channel_t)ACS724LLCTR_OUTPUT_PIN);
} else {
int raw;
adc2_get_raw((adc2_channel_t)ACS724LLCTR_OUTPUT_PIN, width, &raw);
adc_reading += raw;
}
}
adc_reading /= NO_OF_SAMPLES;
// Convert adc_reading to voltage in mV
int voltage = esp_adc_cal_raw_to_voltage(adc_reading, adc_chars);
// Convert voltage to current using the sensor's sensitivity
//Convert mv to V and use datasheet to calculate current
double current = ((voltage / 1000) - 2.5) / SENSITIVITY;
//Convert to milliamps
return current * 1000;
}

//________________________________________________________________________
/* Initialize the ADC for measuring current
===========================================================================
| void
===========================================================================
*/
void BATTERY::initCurrentADC(){
// Configure ADC
if (unit == ADC_UNIT_1) {
adc1_config_width(width);
adc1_config_channel_atten((adc1_channel_t)ACS724LLCTR_OUTPUT_PIN, atten);
} else {
adc2_config_channel_atten((adc2_channel_t)ACS724LLCTR_OUTPUT_PIN, atten);
}

// Characterize ADC
adc_chars = (esp_adc_cal_characteristics_t*)calloc(1, sizeof(esp_adc_cal_characteristics_t));
esp_adc_cal_value_t val_type = esp_adc_cal_characterize(unit, atten, width, DEFAULT_VREF, adc_chars);
}

//________________________________________________________________________
/* Map a value from one range to another
===========================================================================
| Parameters:
| - value: The value to map.
| - fromLow: The low end of the input range.
| - fromHigh: The high end of the input range.
| - toLow: The low end of the output range.
| - toHigh: The high end of the output range.
| Returns: double - The mapped value.
===========================================================================
*/
double BATTERY::mapValue(double value, double fromLow, double fromHigh, double toLow, double toHigh) {
// Check if the value is within the source range
if (value < fromLow) {
value = fromLow;
} else if (value > fromHigh) {
value = fromHigh;
}

// Calculate the ratio of the value within the source range
double ratio = (value - fromLow) / (fromHigh - fromLow);

// Map the ratio to the target range
double result = toLow + ratio * (toHigh - toLow);

return result;
}
Attachments
IMG_2122.jpeg
IMG_2122.jpeg (212.16 KiB) Viewed 6223 times

MicroController
Posts: 1749
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Battery Driver Issue

Postby MicroController » Sat Dec 02, 2023 1:07 pm

Code: Select all

double adc = returnBatteryVoltage();
// Calculate the ratio of the value within the source range
//Attenuation Low = 150 mv
//Attenuation High = 2450 mv
// Battery voltage scale (10V to 12.6V)
double vrs = mapValue(adc, 150, 2450, VOLTAGE_MIN, VOLTAGE_MAX);
The code assumes that the battery is close to 0.6V (i.e. ~150mV from the voltage divider) when empty.
Try this:

Code: Select all

#define ADC_FULL (2450)
#define ADC_EMPTY ((int)((ADC_FULL * VOLTAGE_MIN) / VOLTAGE_MAX))
...
double vrs = mapValue(adc, ADC_EMPTY, ADC_FULL, VOLTAGE_MIN, VOLTAGE_MAX);
And btw, 300kOhm/100kOhm are too big for the ADC; impedance should be <10kOhm, IIRC, so 3kOhm/1kOhm will yield better results. Or, if the ADC is only sampling periodically, you can try putting a small (0.1-1uF) capacitor across the 100k resistor instead.
Last edited by MicroController on Sun Dec 03, 2023 5:43 pm, edited 1 time in total.

nyameaama
Posts: 11
Joined: Fri Jul 28, 2023 7:43 am

Re: Battery Driver Issue

Postby nyameaama » Sun Dec 03, 2023 8:11 am

Thanks for your help!

Who is online

Users browsing this forum: No registered users and 45 guests