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

TESTS/unit_tests/default/main.cpp

Committer:
RobMeades
Date:
2017-11-10
Revision:
2:4c699a813451
Parent:
1:ee7cc8d75283

File content as of revision 2:4c699a813451:

#include "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include "battery_gauge_bq35100.h"

using namespace utest::v1;

// ----------------------------------------------------------------
// COMPILE-TIME MACROS
// ----------------------------------------------------------------

// The gauge enable pin
#define GAUGE_ENABLE_PIN D4

// Pick some sensible minimum and maximum numbers
#define MAX_TEMPERATURE_READING_C  80
#define MIN_TEMPERATURE_READING_C -20
#define MIN_VOLTAGE_READING_MV     0
#define MAX_VOLTAGE_READING_MV     12000
#define MAX_CURRENT_READING_MA     2000
#define MIN_CURRENT_READING_MA    -2000
#define MIN_CAPACITY_READING_UAH   0
#define MAX_CAPACITY_READING_UAH   32177000 // Some randomly chosen
#define SET_DESIGN_CAPACITY_MAH    32177    // values that match


// ----------------------------------------------------------------
// PRIVATE VARIABLES
// ----------------------------------------------------------------

// I2C interface
I2C * gpI2C = new I2C(I2C_SDA, I2C_SCL);

// ----------------------------------------------------------------
// PRIVATE FUNCTIONS
// ----------------------------------------------------------------

// ----------------------------------------------------------------
// TESTS
// ----------------------------------------------------------------

// Test that the BQ35100 battery gauge can be initialised
void test_init() {
    BatteryGaugeBq35100 * pBatteryGauge = new BatteryGaugeBq35100();
    
    TEST_ASSERT_FALSE(pBatteryGauge->init(NULL, GAUGE_ENABLE_PIN));
    TEST_ASSERT(pBatteryGauge->init(gpI2C, GAUGE_ENABLE_PIN));
}

// Test that a temperature reading can be performed
void test_temperature() {
    BatteryGaugeBq35100 * pBatteryGauge = new BatteryGaugeBq35100();
    int32_t temperatureC = MIN_TEMPERATURE_READING_C - 1;
    
    // Call should fail if the battery gauge has not been initialised
    TEST_ASSERT_FALSE(pBatteryGauge->getTemperature(&temperatureC));
    
    // Normal case
    TEST_ASSERT(pBatteryGauge->init(gpI2C, GAUGE_ENABLE_PIN));
    TEST_ASSERT(pBatteryGauge->getTemperature(&temperatureC));
    printf ("Temperature %d C.\n", (int) temperatureC);
    // Range check
    TEST_ASSERT((temperatureC >= MIN_TEMPERATURE_READING_C) && (temperatureC <= MAX_TEMPERATURE_READING_C));
    
    // The parameter is allowed to be NULL
    TEST_ASSERT(pBatteryGauge->getTemperature(NULL));
}

// Test that a voltage reading can be performed
void test_voltage() {
    BatteryGaugeBq35100 * pBatteryGauge = new BatteryGaugeBq35100();
    int32_t voltageMV = MIN_VOLTAGE_READING_MV - 1;
    
    // Call should fail if the battery gauge has not been initialised
    TEST_ASSERT_FALSE(pBatteryGauge->getVoltage(&voltageMV));
    
    // Normal case
    TEST_ASSERT(pBatteryGauge->init(gpI2C, GAUGE_ENABLE_PIN));
    TEST_ASSERT(pBatteryGauge->getVoltage(&voltageMV));
    printf ("Voltage %.3f V.\n", ((float) voltageMV) / 1000);
    // Range check
    TEST_ASSERT((voltageMV >= MIN_VOLTAGE_READING_MV) && (voltageMV <= MAX_VOLTAGE_READING_MV));
    
    // The parameter is allowed to be NULL
    TEST_ASSERT(pBatteryGauge->getVoltage(NULL));
}

// Test that a current reading can be performed
void test_current() {
    BatteryGaugeBq35100 * pBatteryGauge = new BatteryGaugeBq35100();
    int32_t currentMA = MIN_CURRENT_READING_MA - 1;
    
    // Call should fail if the battery gauge has not been initialised
    TEST_ASSERT_FALSE(pBatteryGauge->getCurrent(&currentMA));
    
    // Normal case
    TEST_ASSERT(pBatteryGauge->init(gpI2C, GAUGE_ENABLE_PIN));
    TEST_ASSERT(pBatteryGauge->getCurrent(&currentMA));
    printf ("Current %.3f A.\n", ((float) currentMA) / 1000);
    // Range check
    TEST_ASSERT((currentMA >= MIN_CURRENT_READING_MA) && (currentMA <= MAX_CURRENT_READING_MA));
    
    // The parameter is allowed to be NULL
    TEST_ASSERT(pBatteryGauge->getCurrent(NULL));
}

// Test that a capacity used reading can be performed
void test_used_capacity() {
    BatteryGaugeBq35100 * pBatteryGauge = new BatteryGaugeBq35100();
    uint32_t capacityUAh = MIN_CAPACITY_READING_UAH - 1;
    
    // Call should fail if the battery gauge has not been initialised
    TEST_ASSERT_FALSE(pBatteryGauge->getUsedCapacity(&capacityUAh));
    
    // Normal case
    TEST_ASSERT(pBatteryGauge->init(gpI2C, GAUGE_ENABLE_PIN));
    TEST_ASSERT(pBatteryGauge->getUsedCapacity(&capacityUAh));
    printf ("Used capacity %.3f mAh.\n", ((float) capacityUAh) / 1000);
    // Range check
    TEST_ASSERT((capacityUAh >= MIN_CAPACITY_READING_UAH) && (capacityUAh <= MAX_CAPACITY_READING_UAH));

    // The parameter is allowed to be NULL
    TEST_ASSERT(pBatteryGauge->getUsedCapacity(NULL));
}

// Test that the design capacity can be set and read
void test_design_capacity() {
    BatteryGaugeBq35100 * pBatteryGauge = new BatteryGaugeBq35100();
    uint32_t originalDesignCapacity;
    uint32_t newDesignCapacity = SET_DESIGN_CAPACITY_MAH;
    uint32_t readDesignCapacity = 0;
    
    // Calls should fail if the battery gauge has not been initialised
    TEST_ASSERT_FALSE(pBatteryGauge->getDesignCapacity(&readDesignCapacity));
    TEST_ASSERT_FALSE(pBatteryGauge->setDesignCapacity(newDesignCapacity));

    // First get the original design capacity
    TEST_ASSERT(pBatteryGauge->init(gpI2C, GAUGE_ENABLE_PIN));
    TEST_ASSERT(pBatteryGauge->getDesignCapacity(&originalDesignCapacity));
    printf ("Design capacity was originally %d mAh.\n", (unsigned int) originalDesignCapacity);

    // Avoid the old and new values being the same
    if (originalDesignCapacity == newDesignCapacity) {
        newDesignCapacity--;
    }

    // Now set a new value
    TEST_ASSERT(pBatteryGauge->setDesignCapacity(newDesignCapacity));
    printf ("Design capacity set to %d mAh.\n", (unsigned int) newDesignCapacity);

    // Read the value back and check that it's been set
    TEST_ASSERT(pBatteryGauge->getDesignCapacity(&readDesignCapacity));
    printf ("Design capacity was read as %d mAh.\n", (unsigned int) readDesignCapacity);
    TEST_ASSERT(readDesignCapacity = newDesignCapacity)

    // The parameter in the get call is allowed to be NULL
    TEST_ASSERT(pBatteryGauge->getDesignCapacity(NULL));

    // Put the original value back
    TEST_ASSERT(pBatteryGauge->setDesignCapacity(originalDesignCapacity));
    printf ("Design capacity returned to %d mAh.\n", (unsigned int) originalDesignCapacity);
}

// Test that a remaining capacity reading can be performed
void test_remaining_capacity() {
    BatteryGaugeBq35100 * pBatteryGauge = new BatteryGaugeBq35100();
    uint32_t capacityUAh = MIN_CAPACITY_READING_UAH - 1;

    // Call should fail if the battery gauge has not been initialised
    TEST_ASSERT_FALSE(pBatteryGauge->getRemainingCapacity(&capacityUAh));
    
    // Normal case
    TEST_ASSERT(pBatteryGauge->init(gpI2C, GAUGE_ENABLE_PIN));
    TEST_ASSERT(pBatteryGauge->getRemainingCapacity(&capacityUAh));
    printf ("Remaining capacity %.3f mAh.\n", ((float) capacityUAh) / 1000);
    // Range check
    TEST_ASSERT((capacityUAh >= MIN_CAPACITY_READING_UAH) && (capacityUAh <= MAX_CAPACITY_READING_UAH));

    // The parameter is allowed to be NULL
    TEST_ASSERT(pBatteryGauge->getRemainingCapacity(NULL));
}

// Test that a remaining precentage reading can be performed
void test_remaining_percentage() {
    BatteryGaugeBq35100 * pBatteryGauge = new BatteryGaugeBq35100();
    int32_t percentage = -1;

    // Call should fail if the battery gauge has not been initialised
    TEST_ASSERT_FALSE(pBatteryGauge->getRemainingPercentage(&percentage));
    
    // Normal case
    TEST_ASSERT(pBatteryGauge->init(gpI2C, GAUGE_ENABLE_PIN));
    TEST_ASSERT(pBatteryGauge->getRemainingPercentage(&percentage));
    printf ("Remaining capacity %d%%.\n", (signed int) percentage);
    // Range check
    TEST_ASSERT((percentage >= 0) && (percentage <= 100));

    // The parameter is allowed to be NULL
    TEST_ASSERT(pBatteryGauge->getRemainingPercentage(NULL));
}

// Test behaviours with gauging on
void test_gauging() {
    BatteryGaugeBq35100 * pBatteryGauge = new BatteryGaugeBq35100();
    uint32_t capacityUAh = MIN_CAPACITY_READING_UAH - 1;
    
    // Call should fail if the battery gauge has not been initialised
    TEST_ASSERT_FALSE(pBatteryGauge->enableGauge());
    TEST_ASSERT_FALSE(pBatteryGauge->disableGauge());
    TEST_ASSERT_FALSE(pBatteryGauge->isGaugeEnabled());
    
    // Normal case, gauging should be off to begin with
    TEST_ASSERT(pBatteryGauge->init(gpI2C, GAUGE_ENABLE_PIN));
    TEST_ASSERT_FALSE(pBatteryGauge->isGaugeEnabled());
    
    // Enable gauge, without non-volatile storage

    TEST_ASSERT(pBatteryGauge->enableGauge());
    TEST_ASSERT(pBatteryGauge->getUsedCapacity(&capacityUAh));
    printf ("Used capacity %.3f mAh.\n", ((float) capacityUAh) / 1000);
    // Range check
    TEST_ASSERT((capacityUAh >= MIN_CAPACITY_READING_UAH) && (capacityUAh <= MAX_CAPACITY_READING_UAH));
    TEST_ASSERT(pBatteryGauge->isGaugeEnabled());
    TEST_ASSERT(pBatteryGauge->disableGauge());
    TEST_ASSERT_FALSE(pBatteryGauge->isGaugeEnabled());

    // Enable gauge, with non-volatile storage
    TEST_ASSERT(pBatteryGauge->enableGauge(true));
    TEST_ASSERT(pBatteryGauge->getUsedCapacity(&capacityUAh));
    printf ("Used capacity %.3f mAh.\n", ((float) capacityUAh) / 1000);
    // Range check
    // TODO: any way to check that the non-volatileness has worked?
    TEST_ASSERT((capacityUAh >= MIN_CAPACITY_READING_UAH) && (capacityUAh <= MAX_CAPACITY_READING_UAH));
    TEST_ASSERT(pBatteryGauge->isGaugeEnabled());
    TEST_ASSERT(pBatteryGauge->disableGauge());
    TEST_ASSERT_FALSE(pBatteryGauge->isGaugeEnabled());
}

// Test the new battery call
void test_new_battery() {
    BatteryGaugeBq35100 * pBatteryGauge = new BatteryGaugeBq35100();
    uint32_t originalDesignCapacity;
    uint32_t newDesignCapacity = SET_DESIGN_CAPACITY_MAH;
    uint32_t readDesignCapacity = 0;

    // Call should fail if the battery gauge has not been initialised
    TEST_ASSERT_FALSE(pBatteryGauge->newBattery(1000));
    
    // Normal case
    TEST_ASSERT(pBatteryGauge->init(gpI2C, GAUGE_ENABLE_PIN));
    // Get the original design capacity so that we can set it back
    // at the end
    TEST_ASSERT(pBatteryGauge->getDesignCapacity(&originalDesignCapacity));
    printf ("Design capacity was originally %d mAh.\n", (unsigned int) originalDesignCapacity);

    // Avoid the old and new values being the same
    if (originalDesignCapacity == newDesignCapacity) {
        newDesignCapacity--;
    }

    // Now add the new battery
    TEST_ASSERT(pBatteryGauge->newBattery(newDesignCapacity));
    printf ("New battery added with design capacity %d mAh.\n", (unsigned int) newDesignCapacity);

    // Read the value back and check that it's been set
    TEST_ASSERT(pBatteryGauge->getDesignCapacity(&readDesignCapacity));
    printf ("Design capacity was read as %d mAh.\n", (unsigned int) readDesignCapacity);
    TEST_ASSERT(readDesignCapacity = newDesignCapacity)

    // Put the original value back
    TEST_ASSERT(pBatteryGauge->setDesignCapacity(originalDesignCapacity));
    printf ("Design capacity returned to %d mAh.\n", (unsigned int) originalDesignCapacity);
}

// Test get/set config
void test_advanced_get_set_config() {
    BatteryGaugeBq35100 * pBatteryGauge = new BatteryGaugeBq35100();
    int32_t address = 0x4036; // Manufacturer Info Block A01, a 1 byte field
    char originalValue;
    char oldValue;
    char newValue;
    
    // Calls should fail if the battery gauge has not been initialised
    TEST_ASSERT_FALSE(pBatteryGauge->advancedGetConfig(address, &originalValue, sizeof(originalValue)));
    TEST_ASSERT_FALSE(pBatteryGauge->advancedSetConfig(address, &newValue, sizeof(newValue)));

    // Initialise the battery gauge
    TEST_ASSERT(pBatteryGauge->init(gpI2C, GAUGE_ENABLE_PIN));
    
    // Normal case
    TEST_ASSERT(pBatteryGauge->advancedGetConfig(address, &originalValue, sizeof(originalValue)));
    // Invert the result and write it back
    oldValue = originalValue;
    printf ("Original value was 0x%02x.\n", oldValue);
    newValue = ~oldValue;
    printf ("Setting it to 0x%02x.\n", newValue);
    TEST_ASSERT(pBatteryGauge->advancedSetConfig(address, &newValue, sizeof(newValue)));
    // Read it and check that it has changed
    TEST_ASSERT(pBatteryGauge->advancedGetConfig(address, &oldValue, sizeof(oldValue)));
    printf ("Read back 0x%02x.\n", oldValue);
    TEST_ASSERT_EQUAL_UINT8(newValue, oldValue);
    
    // Put the original value back again
    TEST_ASSERT(pBatteryGauge->advancedSetConfig(address, &originalValue, sizeof(originalValue)));
}

// Send a control word
void test_advanced_control() {
    BatteryGaugeBq35100 * pBatteryGauge = new BatteryGaugeBq35100();
    uint16_t controlWord = 0x0003; // get HW version
    uint16_t response = 0;
    
    // Call should fail if the battery gauge has not been initialised
    TEST_ASSERT_FALSE(pBatteryGauge->advancedSendControlWord(controlWord, &response));
    
    // Initialise the battery gauge
    TEST_ASSERT(pBatteryGauge->init(gpI2C, GAUGE_ENABLE_PIN));
    
    // Normal case
    TEST_ASSERT(pBatteryGauge->advancedSendControlWord(controlWord, &response));
    // FW version must be 0x00a8
    TEST_ASSERT_EQUAL_UINT16(0x00a8, response);

    // The parameter is allowed to be null
    TEST_ASSERT(pBatteryGauge->advancedSendControlWord(controlWord, NULL));
}

// Read using a standard command
void test_advanced_get() {
    BatteryGaugeBq35100 * pBatteryGauge = new BatteryGaugeBq35100();
    uint8_t address = 0x06; // Temperature
    uint16_t value = 0;
    int32_t temperatureC = -1;
    
    // Call should fail if the battery gauge has not been initialised
    TEST_ASSERT_FALSE(pBatteryGauge->advancedGet(address, &value));
    
    // Initialise the battery gauge
    TEST_ASSERT(pBatteryGauge->init(gpI2C, GAUGE_ENABLE_PIN));
    
    // Normal case
    TEST_ASSERT(pBatteryGauge->advancedGet(address, &value));
    // Get the temperature via the standard API command
    TEST_ASSERT(pBatteryGauge->getTemperature(&temperatureC));
    // Convert the value returned into a temperature reading and compare
    // it with the real answer, allowing a 1 degree tolerance in case
    // it has changed between readings.
    TEST_ASSERT_INT32_WITHIN (1, temperatureC, ((int32_t) value / 10) - 273);

    // The parameter is allowed to be null
    TEST_ASSERT(pBatteryGauge->advancedGet(address, NULL));
}

// Test that the security mode of the chip can be changed
void test_advanced_security_mode() {
    BatteryGaugeBq35100 * pBatteryGauge = new BatteryGaugeBq35100();
    BatteryGaugeBq35100::SecurityMode securityMode;

    // Get the existing device mode and then set it to sealed
    TEST_ASSERT(pBatteryGauge->init(gpI2C, GAUGE_ENABLE_PIN));
    printf ("Calling advancedGetSecurityMode()...\n");
    securityMode = pBatteryGauge->advancedGetSecurityMode();
    printf ("Calling advancedSetSecurityMode(SECURITY_MODE_SEALED)...\n");
    TEST_ASSERT(pBatteryGauge->advancedSetSecurityMode(BatteryGaugeBq35100::SECURITY_MODE_SEALED));

    delete pBatteryGauge;
    pBatteryGauge = new BatteryGaugeBq35100();
    // Calls should fail if the battery gauge has not been initialised
    printf ("Calling advancedGetSecurityMode()...\n");
    TEST_ASSERT(pBatteryGauge->advancedGetSecurityMode() == BatteryGaugeBq35100::SECURITY_MODE_UNKNOWN);
    printf ("Calling advancedSetSecurityMode(SECURITY_MODE_UNSEALED)...\n");
    TEST_ASSERT_FALSE(pBatteryGauge->advancedSetSecurityMode(BatteryGaugeBq35100::SECURITY_MODE_UNSEALED));
    
    // Normal case
    printf ("Calling init()...\n");
    TEST_ASSERT(pBatteryGauge->init(gpI2C, GAUGE_ENABLE_PIN));
    printf ("Calling advancedGetSecurityMode()...\n");
    TEST_ASSERT(pBatteryGauge->advancedGetSecurityMode() == BatteryGaugeBq35100::SECURITY_MODE_SEALED);
    
    // These calls should fail
    // TODO do a thing that only works when unsealed
    // TODO do a thing that only works when full access

    // Now unseal it
    printf ("Calling advancedSetSecurityMode(SECURITY_MODE_UNSEALED)...\n");
    TEST_ASSERT(pBatteryGauge->advancedSetSecurityMode(BatteryGaugeBq35100::SECURITY_MODE_UNSEALED));
    
    // This call should now pass
    // TODO do a thing that only works when unsealed
    
    // But this should still fail
    // TODO do a thing that only works when full access

    // Seal it again
    printf ("Calling advancedSetSecurityMode(SECURITY_MODE_SEALED)...\n");
    TEST_ASSERT(pBatteryGauge->advancedSetSecurityMode(BatteryGaugeBq35100::SECURITY_MODE_SEALED));

    // Now allow full access, which should fail as you can't get there from SEALED
    printf ("Calling advancedSetSecurityMode(SECURITY_MODE_FULL_ACCESS)...\n");
    TEST_ASSERT_FALSE(pBatteryGauge->advancedSetSecurityMode(BatteryGaugeBq35100::SECURITY_MODE_FULL_ACCESS));
    
    // Now unseal it
    printf ("Calling advancedSetSecurityMode(SECURITY_MODE_UNSEALED)...\n");
    TEST_ASSERT(pBatteryGauge->advancedSetSecurityMode(BatteryGaugeBq35100::SECURITY_MODE_UNSEALED));
    
    // *Now* allow full access, which should succeed
    printf ("Calling advancedSetSecurityMode(SECURITY_MODE_FULL_ACCESS)...\n");
    TEST_ASSERT(pBatteryGauge->advancedSetSecurityMode(BatteryGaugeBq35100::SECURITY_MODE_FULL_ACCESS));
    
    // These calls should now both pass
    // TODO do a thing that only works when unsealed
    // TODO do a thing that only works when full access

    // Put the device back the way it was
    TEST_ASSERT(pBatteryGauge->advancedSetSecurityMode(securityMode));
}

// Reset the BQ35100 battery gauge chip
void test_advanced_reset() {
    BatteryGaugeBq35100 * pBatteryGauge = new BatteryGaugeBq35100();
    BatteryGaugeBq35100::SecurityMode securityMode;

    // Call should fail if the battery gauge has not been initialised
    TEST_ASSERT_FALSE(pBatteryGauge->advancedReset());

    // Get the existing security mode and then set it to unsealed
    TEST_ASSERT(pBatteryGauge->init(gpI2C, GAUGE_ENABLE_PIN));

    printf ("Calling advancedGetSecurityMode()...\n");
    securityMode = pBatteryGauge->advancedGetSecurityMode();
    printf ("Calling advancedSetSecurityMode(SECURITY_MODE_UNSEALED)...\n");
    TEST_ASSERT(pBatteryGauge->advancedSetSecurityMode(BatteryGaugeBq35100::SECURITY_MODE_UNSEALED));
   
    // TODO: modify a thing that will later be reset
    
    // Now reset the chip
    printf ("Calling advancedReset()...\n");
    TEST_ASSERT(pBatteryGauge->advancedReset());
    
    // TODO check that the thing has been reset

    // Put the security mode back to what it was
    TEST_ASSERT(pBatteryGauge->advancedSetSecurityMode(securityMode));
}

// ----------------------------------------------------------------
// TEST ENVIRONMENT
// ----------------------------------------------------------------

// Setup the test environment
utest::v1::status_t test_setup(const size_t number_of_cases) {
    // Setup Greentea, timeout is long enough to run these tests with
    // DEBUG_BQ35100 defined
    GREENTEA_SETUP(250, "default_auto");
    return verbose_test_setup_handler(number_of_cases);
}

// Test cases
Case cases[] = {
    Case("Initialisation", test_init),
    Case("Temperature read", test_temperature),
    Case("Voltage read", test_voltage),
    Case("Current read", test_current),
    Case("Used capacity read", test_used_capacity),
    Case("Design capacity read/set", test_design_capacity),
    Case("Remaining capacity read", test_remaining_capacity),
    Case("Remaining precentage read", test_remaining_percentage),
    Case("Gauging", test_gauging),
    Case("New battery", test_new_battery),
    Case("Advanced get/set config", test_advanced_get_set_config),
    Case("Advanced control", test_advanced_control),
    Case("Advanced get", test_advanced_get),
    Case("Advanced security mode", test_advanced_security_mode),
    Case("Advanced reset", test_advanced_reset)
};

Specification specification(test_setup, cases);

// ----------------------------------------------------------------
// MAIN
// ----------------------------------------------------------------

// Entry point into the tests
int main() {    
    bool success = false;
    
    if (gpI2C != NULL) {        
        success = !Harness::run(specification);
    } else {
        printf ("Unable to instantiate I2C interface.\n");
    }
    
    return success;
}

// End Of File