This class provides APIs to all of the registers of the TI BQ35100 battery gauge, as used on the u-blox C030 primary battery shield.

Dependents:   example-battery-gauge-bq35100

Revision:
1:ee7cc8d75283
Parent:
0:cec745c014b7
Child:
2:4c699a813451
--- a/bq35100.cpp	Mon Jul 03 16:12:22 2017 +0000
+++ b/bq35100.cpp	Thu Nov 09 22:55:13 2017 +0000
@@ -20,7 +20,8 @@
  */
 
 /** Define these to print debug information. */
-//#define DEBUG_BQ35100
+#define DEBUG_BQ35100
+#define DEBUG_BQ35100_BLOCK_DATA
 
 #include <mbed.h>
 #include <battery_gauge_bq35100.h>
@@ -33,6 +34,22 @@
 // COMPILE-TIME MACROS
 // ----------------------------------------------------------------
 
+/** How many loops to wait for a configuration update to be permitted.
+ * Experience suggests that the limit really does need to be this large. */
+#define CONFIG_UPDATE_LOOPS 200
+
+/** How long to delay when running around the config update loop. */
+#define CONFIG_UPDATE_LOOP_DELAY_MS 100
+
+/** How long to wait for each loop while device is initialising. */
+#define INIT_LOOP_WAIT_MS 100
+
+/** The maximum number of init loops to wait for. */
+#define INIT_LOOP_COUNT 10
+
+/** How long to wait for a security mode change to succeed. */
+#define SET_SECURITY_MODE_RETRY_SECONDS 5
+
 // ----------------------------------------------------------------
 // PRIVATE VARIABLES
 // ----------------------------------------------------------------
@@ -49,12 +66,12 @@
     char data[3];
 
     if (gpI2c != NULL) {
-        // Send a command to read from registerAddress
         data[0] = registerAddress;
         data[1] = 0;
         data[2] = 0;
 
-        if ((gpI2c->write(gAddress, &(data[0]), 1) == 0) &&
+        // Send a command to read from registerAddress
+        if ((gpI2c->write(gAddress, &(data[0]), 1, true) == 0) &&
             (gpI2c->read(gAddress, &(data[1]), 2) == 0)) {
             success = true;
             if (pBytes) {
@@ -66,6 +83,340 @@
     return success;
 }
 
+// Compute the checksum over an address plus the block of data.
+uint8_t BatteryGaugeBq35100::computeChecksum (const char * pData, int32_t length)
+{
+    uint8_t checkSum = 0;
+    uint8_t x = 0;
+
+    if (pData != NULL) {
+#ifdef DEBUG_BQ35100_BLOCK_DATA
+        printf ("BatteryGaugeBq35100 (I2C 0x%02x): computing check sum on data block.\n", gAddress >> 1);
+        printf (" 0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F\n");
+#endif
+        for (x = 1; x <= length; x++) {
+            checkSum += *pData;
+            
+#ifdef DEBUG_BQ35100_BLOCK_DATA
+            if (x % 16 == 8) {
+                printf ("%02x  ", *pData);
+            } else if (x % 16 == 0) {
+                printf ("%02x\n", *pData);
+            } else {
+                printf ("%02x-", *pData);
+            }
+#endif
+            pData++;
+        }
+
+        checkSum = 0xff - checkSum;
+    }
+
+#ifdef DEBUG_BQ35100_BLOCK_DATA
+    if (x % 16 !=  1) {
+        printf("\n");
+    }
+    
+    printf ("BatteryGaugeBq35100 (I2C 0x%02x): check sum is 0x%02x.\n", gAddress >> 1, checkSum);
+#endif    
+    
+    return checkSum;
+}
+
+// Read data of a given length from a given address.
+// Note: gpI2c should be locked before this is called.
+bool BatteryGaugeBq35100::readExtendedData (int32_t address, int32_t length, char * pData)
+{
+    int32_t lengthRead;
+    bool success = false;
+    SecurityMode securityMode = getSecurityMode();
+    char block[32 + 2 + 2]; // 32 bytes of data, 2 bytes of address,
+                            // 1 byte of MACDataSum and 1 byte of MACDataLen
+    char data[3];
+
+    // Handle security mode
+    if (setSecurityMode(SECURITY_MODE_UNSEALED)) {
+        if ((gpI2c != NULL) && (length <= 32) && (address >= 0x4000) && (address < 0x4400) && (pData != NULL)) {
+#ifdef DEBUG_BQ35100
+            printf("BatteryGaugeBq35100 (I2C 0x%02x): preparing to read %d byte(s) from address 0x%04x.\n", gAddress >> 1, (int) length, (unsigned int) address);
+#endif
+            // Enable Block Data Control (0x61)
+            data[0] = 0x61;
+            data[1] = 0;
+
+            if (gpI2c->write(gAddress, &(data[0]), 2) == 0) {
+                // Write address to ManufacturerAccessControl (0x3e)
+                data[0] = 0x3e;
+                data[1] = (char) address;
+                data[2] = (char) (address >> 8);
+
+                if (gpI2c->write(gAddress, &(data[0]), 3) == 0) {
+                    // Read the address from ManufacturerAccessControl (0x3e then 0x3f),
+                    // data from MACData (0x40 to 0x5f), checksum from MACDataSum (0x60) 
+                    // and length from MACDataLen (0x61)
+                    if ((gpI2c->write(gAddress, &(data[0]), 1, true) == 0) &&
+                        (gpI2c->read(gAddress, &(block[0]), sizeof (block)) == 0)) {
+                        // Check that the address matches
+                        if ((block[0] == (char) address) && (block[1] == (char) (address >> 8))) {
+                            // Check that the checksum matches (-2 on MACDataLen as it includes MACDataSum and itself)
+                            if (block[34] == computeChecksum (&(block[0]), block[35] - 2)) {
+                                // All is good, copy the data to the user
+                                lengthRead = block[35] - 4; // -4 rather than -2 to remove the two bytes of address as well
+                                 if (lengthRead > length) {
+                                    lengthRead = length;
+                                }
+                                memcpy(pData, &(block[2]), lengthRead);
+                                success = true;
+#ifdef DEBUG_BQ35100
+                                printf("BatteryGaugeBq35100 (I2C 0x%02x): %d byte(s) read successfully.\n", gAddress >> 1, (int) lengthRead);
+#endif
+                            } else {
+#ifdef DEBUG_BQ35100
+                                printf("BatteryGaugeBq35100 (I2C 0x%02x): checksum didn't match (0x%02x expected).\n", gAddress >> 1, block[34]);
+#endif
+                            }
+                        } else {
+#ifdef DEBUG_BQ35100
+                            printf("BatteryGaugeBq35100 (I2C 0x%02x): address didn't match (expected 0x%04x, received 0x%02x%02x).\n", gAddress >> 1, (unsigned int) address, block[1], block[0]);
+#endif
+                        }
+                    } else {
+#ifdef DEBUG_BQ35100
+                        printf("BatteryGaugeBq35100 (I2C 0x%02x): unable to read %d bytes from ManufacturerAccessControl.\n", gAddress >> 1, sizeof (block));
+#endif
+                    }
+                } else {
+#ifdef DEBUG_BQ35100
+                    printf("BatteryGaugeBq35100 (I2C 0x%02x): unable to write %d bytes to ManufacturerAccessControl.\r", gAddress >> 1, 3);
+#endif
+                }
+            } else {
+#ifdef DEBUG_BQ35100
+                printf("BatteryGaugeBq35100 (I2C 0x%02x): unable to set Block Data Control.\n", gAddress >> 1);
+#endif
+            }
+        } else {
+#ifdef DEBUG_BQ35100
+            printf("BatteryGaugeBq35100 (I2C 0x%02x): unable to set the security mode of the chip.\n", gAddress >> 1);
+#endif
+        }
+    }
+
+    // Put the security mode back to what it was
+    if (!setSecurityMode(securityMode)) {
+        success = false;
+    }
+
+    return success;
+}
+
+// Write data of a given length to a given address.
+// Note: gpI2c should be locked before this is called.
+bool BatteryGaugeBq35100::writeExtendedData(int32_t address, int32_t length, const char * pData)
+{
+    bool success = false;
+    SecurityMode securityMode = getSecurityMode();
+    char data[3 + 32];
+
+    if ((gpI2c != NULL) && (length <= 32) && (address >= 0x4000) && (address < 0x4400) && (pData != NULL)) {
+#ifdef DEBUG_BQ35100
+        printf("BatteryGaugeBq35100 (I2C 0x%02x): preparing to write %d byte(s) to address 0x%04x.\n", gAddress >> 1, (int) length, (unsigned int) address);
+#endif
+        // Handle security mode
+        if (setSecurityMode(SECURITY_MODE_UNSEALED)) {
+            // Enable Block Data Control (0x61)
+            data[0] = 0x61;
+            data[1] = 0;
+
+            if (gpI2c->write(gAddress, &(data[0]), 2) == 0) {
+                // Start write at ManufacturerAccessControl (0x3e)
+                data[0] = 0x3e;
+                // Next two bytes are the address we will write to
+                data[1] = (char) address;
+                data[2] = (char) (address >> 8);
+                // Remaining bytes are the data bytes we wish to write
+                memcpy (&(data[3]), pData, length);
+
+                if (gpI2c->write(gAddress, &(data[0]), 3 + length) == 0) {
+                    // Compute the checksum and write it to MACDataSum (0x60)
+                    data[1] = computeChecksum (&(data[1]), length + 2);
+                    data[0] = 0x60;
+                    
+                    if (gpI2c->write(gAddress, &(data[0]), 2) == 0) {
+                        // Write 4 + length to MACDataLen (0x61)
+                        data[1] = length + 4;
+                        data[0] = 0x61;
+
+                        if (gpI2c->write(gAddress, &(data[0]), 2) == 0) {
+                            // TODO check for successful write
+                            success = true;
+#ifdef DEBUG_BQ35100
+                            printf("BatteryGaugeBq35100 (I2C 0x%02x): write successful.\n", gAddress >> 1);
+#endif
+                        } else {
+#ifdef DEBUG_BQ35100
+                            printf("BatteryGaugeBq35100 (I2C 0x%02x): unable to read write to MACDataLen.\n", gAddress >> 1);
+#endif
+                        }
+                    } else {
+#ifdef DEBUG_BQ35100
+                        printf("BatteryGaugeBq35100 (I2C 0x%02x): unable to write to MACDataSum.\n", gAddress >> 1);
+#endif
+                    }
+                } else {
+#ifdef DEBUG_BQ35100
+                    printf("BatteryGaugeBq35100 (I2C 0x%02x): unable to write %d bytes to ManufacturerAccessControl.\r", gAddress >> 1, (int) length + 2);
+#endif
+                }
+            } else {
+#ifdef DEBUG_BQ35100
+                printf("BatteryGaugeBq35100 (I2C 0x%02x): unable to set Block Data Control.\n", gAddress >> 1);
+#endif
+            }
+        } else {
+#ifdef DEBUG_BQ35100
+            printf("BatteryGaugeBq35100 (I2C 0x%02x): unable to set the security mode of the chip.\n", gAddress >> 1);
+#endif
+        }
+    }
+    
+    // Put the security mode back to what it was
+    if (!setSecurityMode(securityMode)) {
+        success = false;
+    }
+
+    return success;
+}
+
+// Get the security mode of the chip.
+// Note: gpI2c should be locked before this is called.
+BatteryGaugeBq35100::SecurityMode BatteryGaugeBq35100::getSecurityMode(void)
+{
+    SecurityMode securityMode = SECURITY_MODE_UNKNOWN;
+    char data[1];
+    uint16_t controlStatus;
+
+    data[0] = 0;  // Set address to first register for Control
+    
+    // Send a control command to read the control status register
+    if (gpI2c->write(gAddress, &(data[0]), 1) == 0) {
+        if (getTwoBytes(0, &controlStatus)) {
+            // Bits 13 and 14 of the high byte represent the security status,
+            // 01 = full access
+            // 10 = unsealed access
+            // 11 = sealed access
+            securityMode = (SecurityMode) ((controlStatus >> 13) & 0x03);
+#ifdef DEBUG_BQ35100
+            printf("BatteryGaugeBq35100 (I2C 0x%02x): security mode is 0x%02x (control status 0x%04x).\r\n", gAddress >> 1, securityMode, controlStatus);
+#endif
+        }
+    }
+    
+    return securityMode;
+}
+
+// Set the security mode of the chip.
+// Note: gpI2c should be locked before this is called.
+bool BatteryGaugeBq35100::setSecurityMode(SecurityMode securityMode)
+{
+    bool success = false;
+    char data[3];
+    SecurityMode currentSecurityMode = getSecurityMode();
+    
+    if (securityMode != SECURITY_MODE_UNKNOWN) {
+        if (securityMode != currentSecurityMode) {
+            // For reasons that aren't clear, the BQ35100 sometimes refuses
+            // to change security mode if a previous security mode change
+            // happend only a few seconds ago, hence the retry here
+            for (int32_t x = 0; (x < SET_SECURITY_MODE_RETRY_SECONDS) && !success; x++) {
+                data[0] = 0;  // Set address to first register for Control)
+                switch (securityMode) {
+                    case SECURITY_MODE_SEALED:
+                        // Just seal the chip
+                        data[1] = 0x20;  // First byte of SEALED sub-command (0x20)
+                        data[2] = 0x00;  // Second byte of SEALED sub-command (0x00) (register address will auto-increment)
+                        gpI2c->write(gAddress, &(data[0]), 3);
+                    break;
+                    case SECURITY_MODE_FULL_ACCESS:
+                        // Send the full access code with endianness conversion
+                        // in TWO writes
+                        data[2] = (char) (gFullAccessCodes >> 24);
+                        data[1] = (char) (gFullAccessCodes >> 16);
+                        gpI2c->write(gAddress, &(data[0]), 3);
+                        data[2] = (char) (gFullAccessCodes >> 8);
+                        data[1] = (char) gFullAccessCodes;
+                        gpI2c->write(gAddress, &(data[0]), 3);
+                    break;
+                    case SECURITY_MODE_UNSEALED:
+                        data[2] = (char) (gSealCodes >> 24);
+                        data[1] = (char) (gSealCodes >> 16);
+                        gpI2c->write(gAddress, &(data[0]), 3);
+                        data[2] = (char) (gSealCodes >> 8);
+                        data[1] = (char) gSealCodes;
+                        gpI2c->write(gAddress, &(data[0]), 3);
+                    break;
+                    case SECURITY_MODE_UNKNOWN:
+                    default:
+                        MBED_ASSERT(false);
+                    break;
+                }
+
+                currentSecurityMode = getSecurityMode();
+                if (currentSecurityMode == securityMode) {
+                    success = true;
+#ifdef DEBUG_BQ35100
+                    printf("BatteryGaugeBq35100 (I2C 0x%02x): security mode is now 0x%02x.\n", gAddress >> 1, currentSecurityMode);
+#endif
+                } else {
+                    wait_ms(1000);
+#ifdef DEBUG_BQ35100
+                    printf("BatteryGaugeBq35100 (I2C 0x%02x): security mode set failed (wanted 0x%02x, got 0x%02x), will retry.\n", gAddress >> 1, securityMode, currentSecurityMode);
+#endif
+                }
+            }
+        } else {
+            success = true;
+        }
+    }
+    
+    return success;
+}
+
+// Make sure that the device is awake and has taken a reading.
+// Note: the function does its own locking of gpI2C so that it isn't
+// locked for the entire time we wait for ADC readings to complete.
+bool BatteryGaugeBq35100::makeAdcReading(void)
+{
+    bool success = false;
+    uint16_t controlStatus;
+    char data[1];
+    
+    // Wait for INITCOMP to be set
+    data[0] = 0;  // Set address to first register for Control
+
+    gpI2c->lock();
+    // Raise the pin
+    *pGaugeEnable = 1;    
+    wait_ms(GAUGE_ENABLE_SETTLING_TIME_MS);
+    for (int x = 0; !success && (x < INIT_LOOP_COUNT); x++) {
+        if (gpI2c->write(gAddress, &(data[0]), 1) == 0) {
+            if (getTwoBytes(0, &controlStatus)) {
+#ifdef DEBUG_BQ35100
+                printf("BatteryGaugeBq35100 (I2C 0x%02x): read 0x%04x as CONTROL_STATUS.\n", gAddress >> 1, controlStatus);
+#endif
+                // Bit 7 is INITCOMP
+                if (((controlStatus >> 7) & 0x01) == 0x01) {
+                    success = true;
+                }
+            }
+            wait_ms (INIT_LOOP_WAIT_MS);
+        }
+    }
+    gpI2c->unlock();
+    
+    return success;
+}
+
 //----------------------------------------------------------------
 // PUBLIC FUNCTIONS
 // ----------------------------------------------------------------
@@ -74,53 +425,475 @@
 BatteryGaugeBq35100::BatteryGaugeBq35100(void)
 {
     gpI2c = NULL;
+    pGaugeEnable = NULL;
     gReady = false;
+    gSealCodes = 0;
+    gFullAccessCodes = 0;
     gGaugeOn = false;
-    gSealCode = 0;
 }
 
 // Destructor.
 BatteryGaugeBq35100::~BatteryGaugeBq35100(void)
 {
+    if (pGaugeEnable) {
+        *pGaugeEnable = 0;
+        delete pGaugeEnable;
+    }
 }
 
 // Initialise ourselves.
-bool BatteryGaugeBq35100::init (I2C * pI2c, uint8_t address, uint16_t sealCode)
+bool BatteryGaugeBq35100::init(I2C * pI2c, PinName gaugeEnable, uint8_t address, uint32_t sealCodes)
 {
     uint16_t answer;
     char data[4];
 
     gpI2c = pI2c;
     gAddress = address << 1;
-    gSealCode = sealCode;
+    gSealCodes = sealCodes;
+    
+    pGaugeEnable = new DigitalOut(gaugeEnable, 1);
+    wait_ms(GAUGE_ENABLE_SETTLING_TIME_MS);
 
     if (gpI2c != NULL) {
         gpI2c->lock();
+        gpI2c->frequency(I2C_CLOCK_FREQUENCY);
         
-        // Send a control command to read the firmware version
-        data[0] = 0x00;  // Set address to first register for control
-        data[1] = 0x02;  // First byte of FW_VERSION sub-command (0x02)
-        data[2] = 0x00;  // Second byte of FW_VERSION sub-command (0x00) (register address will auto-increment)
+        // Send a control command to read the device type
+        data[0] = 0x3e;  // Set address to ManufacturerAccessControl
+        data[1] = 0x03;  // First byte of HW_VERSION sub-command (0x03)
+        data[2] = 0x00;  // Second byte of HW_VERSION sub-command (0x00) (register address will auto-increment)
 
-        if (gpI2c->write(gAddress, &(data[0]), 3) == 0) {
-            if (getTwoBytes (0, &answer)) {
+        if ((gpI2c->write(gAddress, &(data[0]), 3) == 0) &&
+            getTwoBytes(0x40, &answer)) {  // Read from MACData address
+            if (answer == 0x00a8) {
+                // Read the full access codes, in case we need them
+                if (readExtendedData(0x41d0, sizeof (data), &(data[0]))) {
+                    // The four bytes are the full access codes
+                    gFullAccessCodes = ((uint32_t) data[0] << 24) + ((uint32_t) data[1] << 16) + ((uint32_t) data[2] << 8) + data[3];
 #ifdef DEBUG_BQ35100
-                printf("BatteryGaugeBq35100 (I2C 0x%02x): read 0x%04x as FW_VERSION, expected 0x0109.\n", gAddress >> 1, answer);
+                    printf("BatteryGaugeBq35100 (I2C 0x%02x): full access code is 0x%08x.\n", gAddress >> 1,
+                           (unsigned int) gFullAccessCodes);
 #endif
+                    gReady = true;
+                }
             }
+
+#ifdef DEBUG_BQ35100
+            printf("BatteryGaugeBq35100 (I2C 0x%02x): read 0x%04x as HW_VERSION, expected 0x00a8.\n", gAddress >> 1, answer);
+#endif
         }
+
+        if (!gGaugeOn) {
+            *pGaugeEnable = 0;
+        }
+                
         gpI2c->unlock();
     }
 
 #ifdef DEBUG_BQ35100
     if (gReady) {
-        printf("BatteryGaugeBq35100 (I2C 0x%02x): handler initialised.\r\n", gAddress >> 1);
+        printf("BatteryGaugeBq35100 (I2C 0x%02x): handler initialised.\n", gAddress >> 1);
     } else {
-        printf("BatteryGaugeBq35100 (I2C 0x%02x): init NOT successful.\r\n", gAddress >> 1);
+        printf("BatteryGaugeBq35100 (I2C 0x%02x): init NOT successful.\n", gAddress >> 1);
     }
 #endif
 
     return gReady;
 }
 
+// Switch on the battery capacity monitor.
+bool BatteryGaugeBq35100::enableGauge(void)
+{
+    bool success = false;
+    
+    if (gReady) {
+        *pGaugeEnable = 1;
+        wait_ms(GAUGE_ENABLE_SETTLING_TIME_MS);
+        success = true;
+    }
+    
+    return success;
+}
+
+// Switch off the battery capacity monitor.
+bool BatteryGaugeBq35100::disableGauge(void)
+{
+    bool success = false;
+    
+    if (gReady) {
+        *pGaugeEnable = 0;
+        success = true;
+    }
+    
+    return success;
+}
+
+// Determine whether battery gauging is enabled.
+bool BatteryGaugeBq35100::isGaugeEnabled(void)
+{
+    bool isEnabled = false;
+    
+    if (gReady) {
+        isEnabled = true;
+    }
+    
+    return isEnabled;
+}
+
+// Set the designed capacity of the cell.
+bool BatteryGaugeBq35100::setDesignCapacity(uint32_t capacityMAh)
+{
+    bool success = false;
+    char data[2];
+
+    if (gReady) {
+        gpI2c->lock();
+        
+        data[0] = capacityMAh >> 8;  // Upper byte of design capacity 
+        data[1] = capacityMAh;  // Lower byte of design capacity
+
+        // Write to the "Cell Design Capacity mAh" address in data flash
+        if (writeExtendedData(0x41fe, sizeof(data), &(data[0]))) {
+            success = true;
+ 
+#ifdef DEBUG_BQ35100
+            printf("BatteryGaugeBq35100 (I2C 0x%02x): designed cell capacity set to %d mAh.\n", gAddress >> 1,
+                   (unsigned int) capacityMAh);
+#endif
+        }
+
+        gpI2c->unlock();
+    }
+
+    return success;
+}
+
+// Get the designed capacity of the cell.
+bool BatteryGaugeBq35100::getDesignCapacity(uint32_t *pCapacityMAh)
+{
+    bool success = false;
+    uint16_t data;
+    
+    if (gReady) {
+        gpI2c->lock();
+
+        // Read from the DesignCapacity address
+        if (getTwoBytes (0x3c, &data)) {
+            success = true;
+
+            // The answer is in mAh
+            if (pCapacityMAh) {
+                *pCapacityMAh = data;
+            }
+
+#ifdef DEBUG_BQ35100
+            printf("BatteryGaugeBq35100 (I2C 0x%02x): designed cell capacity is %d mAh.\n", gAddress >> 1, data);
+#endif
+        }
+
+    }
+    
+    return success;
+}
+
+// Get the temperature of the chip.
+bool BatteryGaugeBq35100::getTemperature(int32_t *pTemperatureC)
+{
+    bool success = false;
+    int32_t temperatureC = 0;
+    uint16_t data;
+
+    if (gReady && (gGaugeOn || makeAdcReading())) {
+        gpI2c->lock();
+        // Read from the temperature register address
+        if (getTwoBytes (0x06, &data)) {
+            success = true;
+
+            // The answer is in units of 0.1 K, so convert to C
+            temperatureC = ((int32_t) data / 10) - 273;
+
+            if (pTemperatureC) {
+                *pTemperatureC = temperatureC;
+            }
+
+#ifdef DEBUG_BQ35100
+            printf("BatteryGaugeBq35100 (I2C 0x%02x): chip temperature %.1f K, so %d C.\n", gAddress >> 1, ((float) data) / 10, (int) temperatureC);
+#endif
+        }
+        
+        if (!gGaugeOn) {
+            *pGaugeEnable = 0;
+        }
+        
+        gpI2c->unlock();
+    }
+
+    return success;
+}
+
+// Get the voltage of the battery.
+bool BatteryGaugeBq35100::getVoltage(int32_t *pVoltageMV)
+{
+    bool success = false;
+    uint16_t data = 0;
+
+    if (gReady && (gGaugeOn || makeAdcReading())) {
+        gpI2c->lock();
+        // Read from the voltage register address
+        if (getTwoBytes (0x08, &data)) {
+            success = true;
+
+            // The answer is in mV
+            if (pVoltageMV) {
+                *pVoltageMV = (int32_t) data;
+            }
+
+#ifdef DEBUG_BQ35100
+            printf("BatteryGaugeBq35100 (I2C 0x%02x): battery voltage %.3f V.\n", gAddress >> 1, ((float) data) / 1000);
+#endif
+        }
+
+        if (!gGaugeOn) {
+            *pGaugeEnable = 0;
+        }
+        
+        gpI2c->unlock();
+    }
+    
+    return success;
+}
+
+// Get the current flowing from the battery.
+bool BatteryGaugeBq35100::getCurrent(int32_t *pCurrentMA)
+{
+    bool success = false;
+    int32_t currentMA = 0;
+    uint16_t data;
+
+    if (gReady && (gGaugeOn || makeAdcReading())) {
+        gpI2c->lock();            
+        // Read from the average current register address
+        if (getTwoBytes (0x0c, &data)) {
+            success = true;
+
+            if (pCurrentMA) {
+                *pCurrentMA = currentMA;
+            }
+
+#ifdef DEBUG_BQ35100
+            printf("BatteryGaugeBq35100 (I2C 0x%02x): current %d mA.\n", gAddress >> 1, (int) currentMA);
+#endif
+        }
+
+        if (!gGaugeOn) {
+            *pGaugeEnable = 0;
+        }
+        
+        gpI2c->unlock();
+    }
+    
+    return success;
+}
+
+// Get the battery capacity used.
+bool BatteryGaugeBq35100::getUsedCapacity(uint32_t *pCapacityUAh)
+{
+    bool success = false;
+    char bytes[5];
+    uint32_t data;
+
+    if (gReady && (gGaugeOn || makeAdcReading())) {
+        gpI2c->lock();
+        // Read four bytes from the AccummulatedCapacity register address
+        
+        // Send a command to read from registerAddress
+        bytes[0] = 0x02;
+        bytes[1] = 0;
+        bytes[2] = 0;
+        bytes[3] = 0;
+        bytes[4] = 0;
+
+        if ((gpI2c->write(gAddress, &(bytes[0]), 1) == 0) &&
+            (gpI2c->read(gAddress, &(bytes[1]), 4) == 0)) {
+            success = true;
+            data = (((uint32_t) bytes[4]) << 24) + (((uint32_t) bytes[3]) << 16) + (((uint32_t) bytes[2]) << 8) + bytes[1];
+ 
+            // The answer is in uAh
+            if (pCapacityUAh) {
+                *pCapacityUAh = data;
+            }
+
+#ifdef DEBUG_BQ35100
+            printf("BatteryGaugeBq35100 (I2C 0x%02x): battery capacity used %u uAh.\n", gAddress >> 1, (unsigned int) data);
+#endif
+        }
+
+        if (!gGaugeOn) {
+            *pGaugeEnable = 0;
+        }
+        
+        gpI2c->unlock();
+    }
+    
+    return success;
+}
+
+// Get the battery capacity remaining.
+bool BatteryGaugeBq35100::getRemainingCapacity(uint32_t *pCapacityUAh)
+{
+    bool success = false;
+    uint32_t designCapacityUAh;
+    uint32_t usedCapacityUAh;
+
+    // First, get the designed capacity
+    if (getDesignCapacity(&designCapacityUAh)) {
+        designCapacityUAh *= 1000;
+        // Then get the used capacity
+        if (getUsedCapacity(&usedCapacityUAh)) {
+            success = true;
+            // Limit the result
+            if (usedCapacityUAh > designCapacityUAh) {
+                usedCapacityUAh = designCapacityUAh;
+            }
+
+            // The answer is in uAh
+            if (pCapacityUAh) {
+                *pCapacityUAh = designCapacityUAh - usedCapacityUAh;
+            }
+
+#ifdef DEBUG_BQ35100
+            printf("BatteryGaugeBq35100 (I2C 0x%02x): battery capacity remaining %u uAh (from a designed capacity of %d uAh).\n", gAddress >> 1,
+                   (unsigned int) (designCapacityUAh - usedCapacityUAh), (unsigned int) designCapacityUAh);
+#endif
+        }
+    }
+    
+    return success;
+}
+
+// Get the percentage capacity remaining.
+bool BatteryGaugeBq35100::getRemainingPercentage(int32_t *pBatteryPercentage)
+{
+    bool success = false;
+    uint32_t designCapacityUAh;
+    uint32_t usedCapacityUAh;
+    int32_t batteryPercentage;
+
+    // First, get the designed capacity
+    if (getDesignCapacity(&designCapacityUAh)) {
+        designCapacityUAh *= 1000;
+        // Then get the used capacity
+        if (getUsedCapacity(&usedCapacityUAh)) {
+            success = true;
+            // Limit the result
+            if (usedCapacityUAh > designCapacityUAh) {
+                usedCapacityUAh = designCapacityUAh;
+            }
+            batteryPercentage = (uint64_t) (designCapacityUAh - usedCapacityUAh) * 100 / designCapacityUAh;
+
+            if (pBatteryPercentage) {
+                *pBatteryPercentage = batteryPercentage;
+            }
+
+#ifdef DEBUG_BQ35100
+            printf("BatteryGaugeBq35100 (I2C 0x%02x): battery capacity remaining %d%%.\n", gAddress >> 1,
+                   (unsigned int) batteryPercentage);
+#endif
+        }
+    }
+    
+    return success;
+}
+
+// Get the security mode of the chip.
+BatteryGaugeBq35100::SecurityMode BatteryGaugeBq35100::advancedGetSecurityMode(void)
+{
+    SecurityMode securityMode = SECURITY_MODE_UNKNOWN;
+
+    if (gReady) {
+        gpI2c->lock();
+
+        if (!gGaugeOn) {
+            *pGaugeEnable = 1;
+            wait_ms(GAUGE_ENABLE_SETTLING_TIME_MS);
+        }
+
+        securityMode = getSecurityMode();
+        
+        if (!gGaugeOn) {
+            *pGaugeEnable = 0;
+        }
+        
+        gpI2c->unlock();
+    }
+
+    return securityMode;
+}
+
+// Set the security mode of the chip.
+bool BatteryGaugeBq35100::advancedSetSecurityMode(SecurityMode securityMode)
+{
+    bool success = false;
+
+    if (gReady) {
+        gpI2c->lock();
+
+        if (!gGaugeOn) {
+            *pGaugeEnable = 1;
+            wait_ms(GAUGE_ENABLE_SETTLING_TIME_MS);
+        }
+
+        success = setSecurityMode(securityMode);
+
+        if (!gGaugeOn) {
+            *pGaugeEnable = 0;
+        }
+        
+        gpI2c->unlock();
+    }
+
+    return success;
+}
+
+// Do a hard reset of the chip.
+bool BatteryGaugeBq35100::advancedReset(void)
+{
+    bool success = false;
+    SecurityMode securityMode;
+    char data[3];
+
+    if (gReady && (gpI2c != NULL)) {
+        gpI2c->lock();
+
+        securityMode = getSecurityMode(); // Must be inside lock()
+        // Handle unsealing, as this command only works when unsealed
+        if (setSecurityMode(SECURITY_MODE_UNSEALED)) {
+            // Send a RESET sub-command
+            data[0] = 0x3e;  // Set address to first register for ManufacturerAccessControl
+            data[1] = 0x41;  // First byte of RESET sub-command (0x41)
+            data[2] = 0x00;  // Second byte of RESET sub-command (0x00) (register address will auto-increment)
+
+            if (gpI2c->write(gAddress, &(data[0]), 3) == 0) {
+                success = true;
+#ifdef DEBUG_BQ35100
+                printf("BatteryGaugeBq35100 (I2C 0x%02x): chip hard reset.\n", gAddress >> 1);
+#endif
+            }
+            
+            // Set the security mode back to what it was
+            if (!setSecurityMode(securityMode)) {
+                success = false;
+            }
+        }
+        
+        if (!gGaugeOn) {
+            *pGaugeEnable = 0;
+        }
+
+        gpI2c->unlock();
+    }
+
+    return success;
+}
+
 /* End Of File */