M5 battery monitor improved (read IP5306 chip)

This commit is contained in:
Klaus K Wilting 2020-04-19 19:51:23 +02:00
parent 53be14d8a6
commit 2624f7b35b
8 changed files with 128 additions and 26 deletions

View File

@ -36,28 +36,40 @@ void AXP192_showstatus(void);
#endif // HAS_PMU #endif // HAS_PMU
#ifdef HAS_IP5306
void printIP5306Stats(void);
uint8_t IP5306_GetPowerSource(void);
uint8_t IP5306_GetBatteryLevel(void);
uint8_t IP5306_GetBatteryFull(void);
#endif
// The following map functions were taken from // The following map functions were taken from
// //
// Battery.h - Battery library // Battery.h - Battery library
// Copyright (c) 2014 Roberto Lo Giacco // Copyright (c) 2014 Roberto Lo Giacco
// https://github.com/rlogiacco/BatterySense // https://github.com/rlogiacco/BatterySense
/** /**
* Symmetric sigmoidal approximation * Symmetric sigmoidal approximation
* https://www.desmos.com/calculator/7m9lu26vpy * https://www.desmos.com/calculator/7m9lu26vpy
* *
* c - c / (1 + k*x/v)^3 * c - c / (1 + k*x/v)^3
*/ */
static inline uint8_t sigmoidal(uint16_t voltage, uint16_t minVoltage, uint16_t maxVoltage) { static inline uint8_t sigmoidal(uint16_t voltage, uint16_t minVoltage,
// slow uint16_t maxVoltage) {
// uint8_t result = 110 - (110 / (1 + pow(1.468 * (voltage - minVoltage)/(maxVoltage - minVoltage), 6))); // slow
// uint8_t result = 110 - (110 / (1 + pow(1.468 * (voltage -
// minVoltage)/(maxVoltage - minVoltage), 6)));
// steep // steep
// uint8_t result = 102 - (102 / (1 + pow(1.621 * (voltage - minVoltage)/(maxVoltage - minVoltage), 8.1))); // uint8_t result = 102 - (102 / (1 + pow(1.621 * (voltage -
// minVoltage)/(maxVoltage - minVoltage), 8.1)));
// normal // normal
uint8_t result = 105 - (105 / (1 + pow(1.724 * (voltage - minVoltage)/(maxVoltage - minVoltage), 5.5))); uint8_t result = 105 - (105 / (1 + pow(1.724 * (voltage - minVoltage) /
return result >= 100 ? 100 : result; (maxVoltage - minVoltage),
5.5)));
return result >= 100 ? 100 : result;
} }
/** /**
@ -66,9 +78,13 @@ static inline uint8_t sigmoidal(uint16_t voltage, uint16_t minVoltage, uint16_t
* *
* c - c / [1 + (k*x/v)^4.5]^3 * c - c / [1 + (k*x/v)^4.5]^3
*/ */
static inline uint8_t asigmoidal(uint16_t voltage, uint16_t minVoltage, uint16_t maxVoltage) { static inline uint8_t asigmoidal(uint16_t voltage, uint16_t minVoltage,
uint8_t result = 101 - (101 / pow(1 + pow(1.33 * (voltage - minVoltage)/(maxVoltage - minVoltage) ,4.5), 3)); uint16_t maxVoltage) {
return result >= 100 ? 100 : result; uint8_t result = 101 - (101 / pow(1 + pow(1.33 * (voltage - minVoltage) /
(maxVoltage - minVoltage),
4.5),
3));
return result >= 100 ? 100 : result;
} }
/** /**
@ -77,8 +93,10 @@ static inline uint8_t asigmoidal(uint16_t voltage, uint16_t minVoltage, uint16_t
* *
* x * 100 / v * x * 100 / v
*/ */
static inline uint8_t linear(uint16_t voltage, uint16_t minVoltage, uint16_t maxVoltage) { static inline uint8_t linear(uint16_t voltage, uint16_t minVoltage,
return (unsigned long)(voltage - minVoltage) * 100 / (maxVoltage - minVoltage); uint16_t maxVoltage) {
return (unsigned long)(voltage - minVoltage) * 100 /
(maxVoltage - minVoltage);
} }
uint8_t read_battlevel(mapFn_t mapFunction = &sigmoidal); uint8_t read_battlevel(mapFn_t mapFunction = &sigmoidal);

View File

@ -65,12 +65,14 @@ void doHousekeeping() {
#endif #endif
// read battery voltage into global variable // read battery voltage into global variable
#if (defined BAT_MEASURE_ADC || defined HAS_PMU) #if (defined BAT_MEASURE_ADC || defined HAS_PMU || defined HAS_IP5306)
batt_level = read_battlevel(); batt_level = read_battlevel();
ESP_LOGI(TAG, "Battery: %d%%", batt_level);
#ifdef HAS_PMU #ifdef HAS_PMU
AXP192_showstatus(); AXP192_showstatus();
#endif #endif
#ifdef HAS_IP5306
printIP5306Stats();
#endif
#endif #endif
// display BME680/280 sensor data // display BME680/280 sensor data

View File

@ -280,7 +280,7 @@ void dp_drawPage(time_t t, bool nextpage) {
// line 4: Battery + GPS status + Wifi channel // line 4: Battery + GPS status + Wifi channel
// B:a.bcV Sats:ab ch:ab // B:a.bcV Sats:ab ch:ab
#if (defined BAT_MEASURE_ADC || defined HAS_PMU) #if (defined BAT_MEASURE_ADC || defined HAS_PMU || defined HAS_IP5306)
if (batt_level == 0) if (batt_level == 0)
dp_printf("No batt "); dp_printf("No batt ");
else if (batt_level < 100) else if (batt_level < 100)
@ -290,6 +290,7 @@ void dp_drawPage(time_t t, bool nextpage) {
#else #else
dp_printf(" "); dp_printf(" ");
#endif #endif
#if (HAS_GPS) #if (HAS_GPS)
dp_setFont(MY_FONT_SMALL, !gps_hasfix()); dp_setFont(MY_FONT_SMALL, !gps_hasfix());
dp_printf("Sats:%.2d", gps.satellites.value()); dp_printf("Sats:%.2d", gps.satellites.value());

View File

@ -34,12 +34,12 @@
#define HAS_DISPLAY 2 // TFT-LCD, support work in progess, not ready yet #define HAS_DISPLAY 2 // TFT-LCD, support work in progess, not ready yet
//#define MY_DISPLAY_FLIP 1 // use if display is rotated //#define MY_DISPLAY_FLIP 1 // use if display is rotated
//#define BAT_MEASURE_ADC ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7
//#define BAT_VOLTAGE_DIVIDER 2 // voltage divider 100k/100k on board
#define HAS_LED NOT_A_PIN // no on board LED (?) #define HAS_LED NOT_A_PIN // no on board LED (?)
#define HAS_BUTTON (39) // on board button A #define HAS_BUTTON (39) // on board button A
#define HAS_IP5306 1
// GPS settings // GPS settings
#define HAS_GPS 1 // use on board GPS #define HAS_GPS 1 // use on board GPS
#define GPS_SERIAL 9600, SERIAL_8N1, RXD2, TXD2 // UBlox NEO 6M RX, TX #define GPS_SERIAL 9600, SERIAL_8N1, RXD2, TXD2 // UBlox NEO 6M RX, TX

View File

@ -34,8 +34,6 @@
#define HAS_DISPLAY 2 // TFT-LCD, support work in progess, not ready yet #define HAS_DISPLAY 2 // TFT-LCD, support work in progess, not ready yet
//#define MY_DISPLAY_FLIP 1 // use if display is rotated //#define MY_DISPLAY_FLIP 1 // use if display is rotated
//#define BAT_MEASURE_ADC ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7
//#define BAT_VOLTAGE_DIVIDER 2 // voltage divider 100k/100k on board
#define HAS_LED NOT_A_PIN // no on board LED (?) #define HAS_LED NOT_A_PIN // no on board LED (?)
#define RGB_LED_COUNT 10 #define RGB_LED_COUNT 10
@ -43,6 +41,8 @@
#define HAS_RGB_LED SmartLed rgb_led(LED_SK6812, RGB_LED_COUNT, GPIO_NUM_15) // LED_SK6812 RGB LED on GPIO15 #define HAS_RGB_LED SmartLed rgb_led(LED_SK6812, RGB_LED_COUNT, GPIO_NUM_15) // LED_SK6812 RGB LED on GPIO15
#define HAS_BUTTON (39) // on board button A #define HAS_BUTTON (39) // on board button A
#define HAS_IP5306 1
// GPS settings // GPS settings
#define HAS_GPS 0 // use on board GPS #define HAS_GPS 0 // use on board GPS
#define GPS_SERIAL 9600, SERIAL_8N1, RXD2, TXD2 // UBlox NEO 6M RX, TX #define GPS_SERIAL 9600, SERIAL_8N1, RXD2, TXD2 // UBlox NEO 6M RX, TX

View File

@ -492,6 +492,9 @@ uint8_t myBattLevelCb(void *pUserData) {
#ifdef HAS_PMU #ifdef HAS_PMU
if (pmu.isVBUSPlug()) if (pmu.isVBUSPlug())
lmic_batt_level = MCMD_DEVS_EXT_POWER; lmic_batt_level = MCMD_DEVS_EXT_POWER;
#elif defined HAS_IP5306
if (IP5306_GetPowerSource())
lmic_batt_level = MCMD_DEVS_EXT_POWER;
#else #else
if (batt_percent >= 100) if (batt_percent >= 100)
lmic_batt_level = MCMD_DEVS_EXT_POWER; lmic_batt_level = MCMD_DEVS_EXT_POWER;

View File

@ -258,10 +258,13 @@ void setup() {
#endif #endif
// initialize battery status // initialize battery status
#if (defined BAT_MEASURE_ADC || defined HAS_PMU) #if (defined BAT_MEASURE_ADC || defined HAS_PMU || defined HAS_IP5306)
strcat_P(features, " BATT"); strcat_P(features, " BATT");
calibrate_voltage(); calibrate_voltage();
batt_level = read_battlevel(); batt_level = read_battlevel();
#ifdef HAS_IP5306
printIP5306Stats();
#endif
#endif #endif
#if (USE_OTA) #if (USE_OTA)

View File

@ -215,6 +215,10 @@ uint8_t read_battlevel(mapFn_t mapFunction) {
// returns the estimated battery level in values 0 ... 100 [percent] // returns the estimated battery level in values 0 ... 100 [percent]
uint8_t batt_percent; uint8_t batt_percent;
#ifdef HAS_IP5306
batt_percent = IP5306_GetBatteryLevel();
#else
const uint16_t batt_voltage = read_voltage(); const uint16_t batt_voltage = read_voltage();
if (batt_voltage <= BAT_MIN_VOLTAGE) if (batt_voltage <= BAT_MIN_VOLTAGE)
@ -224,17 +228,88 @@ uint8_t read_battlevel(mapFn_t mapFunction) {
else else
batt_percent = batt_percent =
(*mapFunction)(batt_voltage, BAT_MIN_VOLTAGE, BAT_MAX_VOLTAGE); (*mapFunction)(batt_voltage, BAT_MIN_VOLTAGE, BAT_MAX_VOLTAGE);
#endif // HAS_IP5306
ESP_LOGD(TAG, "batt_voltage = %dmV / batt_percent = %d%%", batt_voltage,
batt_percent);
return batt_percent; return batt_percent;
} }
bool batt_sufficient() { bool batt_sufficient() {
#if (defined HAS_PMU || defined BAT_MEASURE_ADC) #if (defined HAS_PMU || defined BAT_MEASURE_ADC || defined HAS_IP5306)
return (batt_level > OTA_MIN_BATT); return (batt_level > OTA_MIN_BATT);
#else #else
return true; // we don't know batt level return true; // we don't know batt level
#endif #endif
} }
#ifdef HAS_IP5306
// IP5306 code snippet was taken from
// https://gist.github.com/me-no-dev/7702f08dd578de5efa47caf322250b57
#define IP5306_REG_SYS_0 0x00
#define IP5306_REG_SYS_1 0x01
#define IP5306_REG_SYS_2 0x02
#define IP5306_REG_CHG_0 0x20
#define IP5306_REG_CHG_1 0x21
#define IP5306_REG_CHG_2 0x22
#define IP5306_REG_CHG_3 0x23
#define IP5306_REG_CHG_4 0x24
#define IP5306_REG_READ_0 0x70
#define IP5306_REG_READ_1 0x71
#define IP5306_REG_READ_2 0x72
#define IP5306_REG_READ_3 0x77
#define IP5306_REG_READ_4 0x78
#define IP5306_LEDS2PCT(byte) \
((byte & 0x01 ? 25 : 0) + (byte & 0x02 ? 25 : 0) + (byte & 0x04 ? 25 : 0) + \
(byte & 0x08 ? 25 : 0))
uint8_t ip5306_get_bits(uint8_t reg, uint8_t index, uint8_t bits) {
uint8_t value;
if (i2c_readBytes(0x75, reg, &value, 1) == 0xff) {
ESP_LOGW(TAG, "IP5306 get bits fail: 0x%02x", reg);
return 0;
}
return (value >> index) & ((1 << bits) - 1);
}
void ip5306_set_bits(uint8_t reg, uint8_t index, uint8_t bits, uint8_t value) {
uint8_t mask = (1 << bits) - 1, v;
if (i2c_readBytes(0x75, reg, &v, 1) == 0xff) {
ESP_LOGW(TAG, "IP5306 register read fail: 0x%02x", reg);
return;
}
v &= ~(mask << index);
v |= ((value & mask) << index);
if (i2c_writeBytes(0x75, reg, &v, 1) == 0xff)
ESP_LOGW(TAG, "IP5306 register write fail: 0x%02x", reg);
}
uint8_t IP5306_GetPowerSource(void) {
ip5306_get_bits(IP5306_REG_READ_0, 3, 1); // 0:BAT, 1:VIN
}
uint8_t IP5306_GetBatteryFull(void) {
ip5306_get_bits(IP5306_REG_READ_1, 3, 1); // 0:CHG/DIS, 1:FULL
}
uint8_t IP5306_GetBatteryLevel(void) {
uint8_t state = (~ip5306_get_bits(IP5306_REG_READ_4, 4, 4)) & 0x0F;
// LED[0-4] State (inverted)
return IP5306_LEDS2PCT(state);
}
void printIP5306Stats(void) {
bool usb = IP5306_GetPowerSource();
bool full = IP5306_GetBatteryFull();
uint8_t leds = IP5306_GetBatteryLevel();
ESP_LOGI(TAG,
"IP5306: Power Source: %s, Battery State: %s, Battery "
"Level: %u%%",
usb ? "USB" : "BATTERY",
full ? "CHARGED" : (usb ? "CHARGING" : "DISCHARGING"),
IP5306_LEDS2PCT(leds));
}
#endif // HAS_IP5306