diff --git a/include/i2c.h b/include/i2c.h index 30f4119b..4f7d71ea 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -8,6 +8,7 @@ #define BME_PRIMARY_ADDRESS (0x77) #define BME_SECONDARY_ADDRESS (0x76) #define AXP192_PRIMARY_ADDRESS (0x34) +#define IP5306_PRIMARY_ADDRESS (0x75) #define MCP_24AA02E64_PRIMARY_ADDRESS (0x50) #define QUECTEL_GPS_PRIMARY_ADDRESS (0x10) 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/platformio.ini b/platformio.ini index 93fad964..aab79dcf 100644 --- a/platformio.ini +++ b/platformio.ini @@ -45,7 +45,7 @@ description = Paxcounter is a device for metering passenger flows in realtime. I [common] ; for release_version use max. 10 chars total, use any decimal format like "a.b.c" -release_version = 1.9.99 +release_version = 1.9.991 ; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running! ; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose debug_level = 3 @@ -53,16 +53,16 @@ extra_scripts = pre:build.py otakeyfile = ota.conf lorakeyfile = loraconf.h lmicconfigfile = lmic_config.h -platform_espressif32 = espressif32@1.12.0 +platform_espressif32 = espressif32@1.12.1 monitor_speed = 115200 upload_speed = 115200 lib_deps_lora = MCCI LoRaWAN LMIC library@3.1.0 ; MCCI LMIC by Terrill Moore lib_deps_display = - ss_oled@4.1.2 ; fast and small OLED lib by Larry Bank - BitBang_I2C@2.0.3 + ss_oled@4.1.3 ; fast and small OLED lib by Larry Bank + BitBang_I2C@2.1.1 QRCode@0.0.1 - TFT_eSPI@2.2.0 + TFT_eSPI@2.2.2 lib_deps_matrix_display = Ultrathin_LED_Matrix@>=1.0.0 lib_deps_rgbled = @@ -70,8 +70,8 @@ lib_deps_rgbled = lib_deps_gps = 1655@>=1.0.2 ; #1655 TinyGPSPlus by Mikal Hart lib_deps_sensors = - Adafruit Unified Sensor@>=1.1.1 - Adafruit BME280 Library@>=2.0.0 + Adafruit Unified Sensor@>=1.1.2 + Adafruit BME280 Library@>=2.0.2 Adafruit BMP085 Library@>=1.0.1 BSEC Software Library@1.5.1474 https://github.com/ricki-z/SDS011.git @@ -110,7 +110,7 @@ framework = arduino board = esp32dev board_build.partitions = min_spiffs.csv upload_speed = ${common.upload_speed} -;upload_port = COM8 +;upload_port = COM5 platform = ${common.platform_espressif32} lib_deps = ${common.lib_deps_all} build_flags = ${common.build_flags_all} 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/i2c.cpp b/src/i2c.cpp index 783c2e1a..4cb9fe1c 100644 --- a/src/i2c.cpp +++ b/src/i2c.cpp @@ -5,8 +5,7 @@ // Local logging tag static const char TAG[] = __FILE__; -void i2c_init(void) { - Wire.begin(MY_DISPLAY_SDA, MY_DISPLAY_SCL, 400000); } +void i2c_init(void) { Wire.begin(MY_DISPLAY_SDA, MY_DISPLAY_SCL, 400000); } void i2c_deinit(void) { Wire.~TwoWire(); // shutdown/power off I2C hardware @@ -53,6 +52,10 @@ int i2c_scan(void) { ESP_LOGI(TAG, "0x%X: AXP192 power management", addr); break; + case IP5306_PRIMARY_ADDRESS: + ESP_LOGI(TAG, "0x%X: IP5306 power management", addr); + break; + case QUECTEL_GPS_PRIMARY_ADDRESS: ESP_LOGI(TAG, "0x%X: Quectel GPS", addr); break; diff --git a/src/led.cpp b/src/led.cpp index 4936eff4..64d33d2e 100644 --- a/src/led.cpp +++ b/src/led.cpp @@ -63,7 +63,6 @@ RGBColor rgb_hsl2rgb(float h, float s, float l) { } void rgb_set_color(uint16_t hue) { - int i = RGB_LED_COUNT; if (hue == COLOR_NONE) { // set Off for (int i = 0; i < RGB_LED_COUNT; i++) 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..062bbcb8 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,86 @@ 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(IP5306_PRIMARY_ADDRESS, 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(IP5306_PRIMARY_ADDRESS, 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(IP5306_PRIMARY_ADDRESS, reg, &v, 1) == 0xff) + ESP_LOGW(TAG, "IP5306 register write fail: 0x%02x", reg); +} + +uint8_t IP5306_GetPowerSource(void) { + return ip5306_get_bits(IP5306_REG_READ_0, 3, 1); // 0:BAT, 1:VIN +} + +uint8_t IP5306_GetBatteryFull(void) { + return 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 level = IP5306_GetBatteryLevel(); + ESP_LOGI(TAG, + "IP5306: Power Source: %s, Battery State: %s, Battery Level: %u%%", + usb ? "USB" : "BATTERY", + full ? "CHARGED" : (usb ? "CHARGING" : "DISCHARGING"), level); +} + +#endif // HAS_IP5306 \ No newline at end of file