diff --git a/include/power.h b/include/power.h index c2c91c94..795fbb87 100644 --- a/include/power.h +++ b/include/power.h @@ -36,28 +36,40 @@ void AXP192_showstatus(void); #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 // // Battery.h - Battery library // Copyright (c) 2014 Roberto Lo Giacco // https://github.com/rlogiacco/BatterySense - + /** * Symmetric sigmoidal approximation * https://www.desmos.com/calculator/7m9lu26vpy * * c - c / (1 + k*x/v)^3 */ -static inline uint8_t sigmoidal(uint16_t voltage, uint16_t minVoltage, uint16_t maxVoltage) { - // slow - // uint8_t result = 110 - (110 / (1 + pow(1.468 * (voltage - minVoltage)/(maxVoltage - minVoltage), 6))); +static inline uint8_t sigmoidal(uint16_t voltage, uint16_t minVoltage, + uint16_t maxVoltage) { + // slow + // uint8_t result = 110 - (110 / (1 + pow(1.468 * (voltage - + // minVoltage)/(maxVoltage - minVoltage), 6))); - // steep - // uint8_t result = 102 - (102 / (1 + pow(1.621 * (voltage - minVoltage)/(maxVoltage - minVoltage), 8.1))); + // steep + // uint8_t result = 102 - (102 / (1 + pow(1.621 * (voltage - + // minVoltage)/(maxVoltage - minVoltage), 8.1))); - // normal - uint8_t result = 105 - (105 / (1 + pow(1.724 * (voltage - minVoltage)/(maxVoltage - minVoltage), 5.5))); - return result >= 100 ? 100 : result; + // normal + uint8_t result = 105 - (105 / (1 + pow(1.724 * (voltage - minVoltage) / + (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 */ -static inline uint8_t asigmoidal(uint16_t voltage, uint16_t minVoltage, uint16_t maxVoltage) { - uint8_t result = 101 - (101 / pow(1 + pow(1.33 * (voltage - minVoltage)/(maxVoltage - minVoltage) ,4.5), 3)); - return result >= 100 ? 100 : result; +static inline uint8_t asigmoidal(uint16_t voltage, uint16_t minVoltage, + uint16_t maxVoltage) { + 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 */ -static inline uint8_t linear(uint16_t voltage, uint16_t minVoltage, uint16_t maxVoltage) { - return (unsigned long)(voltage - minVoltage) * 100 / (maxVoltage - minVoltage); +static inline uint8_t linear(uint16_t voltage, uint16_t minVoltage, + uint16_t maxVoltage) { + return (unsigned long)(voltage - minVoltage) * 100 / + (maxVoltage - minVoltage); } uint8_t read_battlevel(mapFn_t mapFunction = &sigmoidal); diff --git a/src/cyclic.cpp b/src/cyclic.cpp index 3bc4ccdb..31175ff9 100644 --- a/src/cyclic.cpp +++ b/src/cyclic.cpp @@ -65,12 +65,14 @@ void doHousekeeping() { #endif // 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(); - ESP_LOGI(TAG, "Battery: %d%%", batt_level); #ifdef HAS_PMU AXP192_showstatus(); #endif +#ifdef HAS_IP5306 + printIP5306Stats(); +#endif #endif // display BME680/280 sensor data diff --git a/src/display.cpp b/src/display.cpp index f340e6c9..3fa7e62a 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -280,7 +280,7 @@ void dp_drawPage(time_t t, bool nextpage) { // line 4: Battery + GPS status + Wifi channel // 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) dp_printf("No batt "); else if (batt_level < 100) @@ -290,6 +290,7 @@ void dp_drawPage(time_t t, bool nextpage) { #else dp_printf(" "); #endif + #if (HAS_GPS) dp_setFont(MY_FONT_SMALL, !gps_hasfix()); dp_printf("Sats:%.2d", gps.satellites.value()); diff --git a/src/hal/m5core.h b/src/hal/m5core.h index 0d6c4496..b391efe6 100644 --- a/src/hal/m5core.h +++ b/src/hal/m5core.h @@ -34,12 +34,12 @@ #define HAS_DISPLAY 2 // TFT-LCD, support work in progess, not ready yet //#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_BUTTON (39) // on board button A +#define HAS_IP5306 1 + // GPS settings #define HAS_GPS 1 // use on board GPS #define GPS_SERIAL 9600, SERIAL_8N1, RXD2, TXD2 // UBlox NEO 6M RX, TX diff --git a/src/hal/m5fire.h b/src/hal/m5fire.h index 0e53a8a1..553493be 100644 --- a/src/hal/m5fire.h +++ b/src/hal/m5fire.h @@ -34,8 +34,6 @@ #define HAS_DISPLAY 2 // TFT-LCD, support work in progess, not ready yet //#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 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_BUTTON (39) // on board button A +#define HAS_IP5306 1 + // GPS settings #define HAS_GPS 0 // use on board GPS #define GPS_SERIAL 9600, SERIAL_8N1, RXD2, TXD2 // UBlox NEO 6M RX, TX diff --git a/src/lorawan.cpp b/src/lorawan.cpp index 0d67570e..9e5250f8 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -492,6 +492,9 @@ uint8_t myBattLevelCb(void *pUserData) { #ifdef HAS_PMU if (pmu.isVBUSPlug()) lmic_batt_level = MCMD_DEVS_EXT_POWER; +#elif defined HAS_IP5306 + if (IP5306_GetPowerSource()) + lmic_batt_level = MCMD_DEVS_EXT_POWER; #else if (batt_percent >= 100) lmic_batt_level = MCMD_DEVS_EXT_POWER; diff --git a/src/main.cpp b/src/main.cpp index ec9d7abc..c5ee155e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -258,10 +258,13 @@ void setup() { #endif // 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"); calibrate_voltage(); batt_level = read_battlevel(); +#ifdef HAS_IP5306 + printIP5306Stats(); +#endif #endif #if (USE_OTA) diff --git a/src/power.cpp b/src/power.cpp index bbae6157..95608586 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -215,6 +215,10 @@ uint8_t read_battlevel(mapFn_t mapFunction) { // returns the estimated battery level in values 0 ... 100 [percent] uint8_t batt_percent; + +#ifdef HAS_IP5306 + batt_percent = IP5306_GetBatteryLevel(); +#else const uint16_t batt_voltage = read_voltage(); if (batt_voltage <= BAT_MIN_VOLTAGE) @@ -224,17 +228,88 @@ uint8_t read_battlevel(mapFn_t mapFunction) { else batt_percent = (*mapFunction)(batt_voltage, BAT_MIN_VOLTAGE, BAT_MAX_VOLTAGE); - - ESP_LOGD(TAG, "batt_voltage = %dmV / batt_percent = %d%%", batt_voltage, - batt_percent); +#endif // HAS_IP5306 return batt_percent; } 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); #else return true; // we don't know batt level #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 \ No newline at end of file