├── .github └── PULL_REQUEST_TEMPLATE.md ├── .library.json ├── Lorro_BQ4050.cpp ├── Lorro_BQ4050.h ├── README.md ├── README.txt ├── examples ├── readFuel │ └── readFuel.ino └── writeConfig │ └── writeConfig.ino ├── library.properties └── license.txt /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thank you for creating a pull request to contribute to Lorro's GitHub code! 2 | Before you open the request please review the following guidelines and tips to 3 | help it be more easily integrated: 4 | 5 | - **Describe the scope of your change--i.e. what the change does and what parts 6 | of the code were modified.** This will help us understand any risks of integrating 7 | the code. 8 | 9 | - **Describe any known limitations with your change.** For example if the change 10 | doesn't apply to a supported platform of the library please mention it. 11 | 12 | - **Please run any tests or examples that can exercise your modified code.** We 13 | strive to not break users of the code and running tests/examples helps with this 14 | process. 15 | 16 | Thank you again for contributing! We will try to test and integrate the change 17 | as soon as we can, but be aware we have many GitHub repositories to manage and 18 | can't immediately respond to every request. There is no need to bump or check in 19 | on a pull request (it will clutter the discussion of the request). 20 | 21 | Also don't be worried if the request is closed or not integrated--sometimes the 22 | priorities of Lorro's GitHub code (education, ease of use) might not match the 23 | priorities of the pull request. Don't fret, the open source community thrives on 24 | forks and GitHub makes it easy to keep your changes in a forked repo. 25 | 26 | After reviewing the guidelines above you can delete this text from the pull request. 27 | -------------------------------------------------------------------------------- /.library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Lorro BQ4050", 3 | "version": "0.2", 4 | "keywords": [ 5 | "communication" 6 | ], 7 | "description": "Arduino library for BQ4050 battery fuel gauge, using SMBUS communication which is based on I2C", 8 | "frameworks": [ 9 | "arduino" 10 | ], 11 | "platforms": [ 12 | "teensy" 13 | ], 14 | "authors": [ 15 | { 16 | "email": null, 17 | "url": null, 18 | "maintainer": true, 19 | "name": "Lorro" 20 | } 21 | ], 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/Lawmate/BQ4050" 25 | }, 26 | "homepage": null, 27 | "export": { 28 | "include": null, 29 | "exclude": [ 30 | "extras", 31 | "docs", 32 | "tests", 33 | "test", 34 | "*.doxyfile", 35 | "*.pdf" 36 | ] 37 | }, 38 | "id": 199 39 | } 40 | -------------------------------------------------------------------------------- /Lorro_BQ4050.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file Lorro_BQ4050.h 4 | @author Lorro 5 | 6 | 7 | A library for interfacing with TI BQ4050 battery fuel gauge chip 8 | */ 9 | /**************************************************************************/ 10 | #include "Arduino.h" 11 | 12 | #include 13 | 14 | #include "Lorro_BQ4050.h" 15 | 16 | Lorro_BQ4050::Lorro_BQ4050( char addr ){ 17 | 18 | //Initialisation function 19 | 20 | //Load in address 21 | BQ4050addr = addr; 22 | //Start the I2C comms 23 | Wire.begin(); 24 | //Create the lookup table for doing CRC8 checks for the PEC 25 | CalculateTable_CRC8(); 26 | 27 | } 28 | 29 | //Create instances of structs for use across the functions 30 | Lorro_BQ4050::DFt dataFlash; 31 | Lorro_BQ4050::Regt registers; 32 | 33 | //Prints a binary number with leading zeros (Automatic Handling) 34 | //Useful for debugging 35 | #define PRINTBIN(Num) for (uint64_t t = (1ULL<< ((sizeof(Num)*8)-1)); t; t >>= 1) Serial.write(Num & t ? '1' : '0'); 36 | 37 | boolean Lorro_BQ4050::getOperationStatus(){ 38 | if( readBlockReg( registers.operationStatus ) ){ 39 | Serial.println( "Operation status bits:" ); 40 | for( int i = 0; i < 4; i++ ){ 41 | for( int j = 0; j < 8; j++ ){ 42 | Serial.print( "(" ); 43 | Serial.print( ( registers.operationStatus.val[ i ] & ( 0x01 << ( 7 - j ) ) ) >> ( 7 - j ) ); 44 | Serial.print( ") " ); 45 | Serial.print( registers.operationStatus.label[ i ][ j ] ); 46 | Serial.print( "\t" ); 47 | } 48 | Serial.println(); 49 | } 50 | return true; //return if the bit read has been successful 51 | }else{ 52 | return false; 53 | } 54 | } 55 | 56 | boolean Lorro_BQ4050::getChargingStatus(){ 57 | if( readBlockReg( registers.chargingStatus ) ){ 58 | Serial.println( "Charging status bits:" ); 59 | for( int i = 0; i < 2; i++ ){ 60 | for( int j = 0; j < 8; j++ ){ 61 | Serial.print( "(" ); 62 | Serial.print( ( registers.chargingStatus.val[ i ] & ( 0x01 << ( 7 - j ) ) ) >> ( 7 - j ) ); 63 | Serial.print( ") " ); 64 | Serial.print( registers.chargingStatus.label[ i ][ j ] ); 65 | Serial.print( "\t" ); 66 | } 67 | Serial.println(); 68 | } 69 | return true; //return if the bit read has been successful 70 | }else{ 71 | return false; 72 | } 73 | } 74 | 75 | boolean Lorro_BQ4050::getGaugingStatus(){ 76 | if( readBlockReg( registers.gaugingStatus ) ){ 77 | Serial.println( "Gauging status bits:" ); 78 | for( int i = 0; i < 2; i++ ){ 79 | for( int j = 0; j < 8; j++ ){ 80 | Serial.print( "(" ); 81 | Serial.print( ( registers.gaugingStatus.val[ i ] & ( 0x01 << ( 7 - j ) ) ) >> ( 7 - j ) ); 82 | Serial.print( ") " ); 83 | Serial.print( registers.gaugingStatus.label[ i ][ j ] ); 84 | Serial.print( "\t" ); 85 | } 86 | Serial.println(); 87 | } 88 | return true; //return if the bit read has been successful 89 | }else{ 90 | return false; 91 | } 92 | } 93 | 94 | boolean Lorro_BQ4050::getSafetyStatus(){ 95 | if( readBlockReg( registers.safetyStatus ) ){ 96 | Serial.println( "Safety status bits:" ); 97 | for( int i = 0; i < 4; i++ ){ 98 | for( int j = 0; j < 8; j++ ){ 99 | Serial.print( "(" ); 100 | Serial.print( ( registers.safetyStatus.val[ i ] & ( 0x01 << ( 7 - j ) ) ) >> ( 7 - j ) ); 101 | Serial.print( ") " ); 102 | Serial.print( registers.safetyStatus.label[ i ][ j ] ); 103 | Serial.print( "\t" ); 104 | } 105 | Serial.println(); 106 | } 107 | return true; //return if the bit read has been successful 108 | }else{ 109 | return false; 110 | } 111 | } 112 | 113 | boolean Lorro_BQ4050::getSafetyAlert(){ 114 | if( readBlockReg( registers.safetyAlert ) ){ 115 | Serial.println( "Safety alert bits:" ); 116 | for( int i = 0; i < 4; i++ ){ 117 | for( int j = 0; j < 8; j++ ){ 118 | Serial.print( "(" ); 119 | Serial.print( ( registers.safetyAlert.val[ i ] & ( 0x01 << ( 7 - j ) ) ) >> ( 7 - j ) ); 120 | Serial.print( ") " ); 121 | Serial.print( registers.safetyAlert.label[ i ][ j ] ); 122 | Serial.print( "\t" ); 123 | } 124 | Serial.println(); 125 | } 126 | return true; //return if the bit read has been successful 127 | }else{ 128 | return false; 129 | } 130 | } 131 | 132 | boolean Lorro_BQ4050::getXCHGstatus(){ 133 | if( readBlockReg( registers.operationStatus ) ){ 134 | //AND the bit with the byte then shift it right 135 | if( ( ( registers.operationStatus.val[ 1 ] & 0x40 ) >> 6 ) == 1 ){ 136 | Serial.println("Charging is disabled \t\t\t(XCHG high)"); 137 | }else{ 138 | Serial.println("Charging is not disabled \t\t(XCHG low)"); 139 | } 140 | return true; //return if the bit read has been successful 141 | }else{ 142 | return false; 143 | } 144 | } 145 | 146 | boolean Lorro_BQ4050::getXDSGstatus(){ 147 | 148 | //Function to read the register and extract the status bit for human reading 149 | 150 | if( readBlockReg( registers.operationStatus ) ){ 151 | //AND the bit with the byte then shift it right 152 | if( ( ( registers.operationStatus.val[ 1 ] & 0x20 ) >> 5 ) == 1 ){ 153 | Serial.println("Discharging is disabled \t(XDSG high)"); 154 | }else{ 155 | Serial.println("Discharging is not disabled \t\t(XDSG low)"); 156 | } 157 | return true; //return if the bit read has been successful 158 | }else{ 159 | return false; 160 | } 161 | } 162 | 163 | boolean Lorro_BQ4050::getPCHGstatus(){ 164 | if( readBlockReg( registers.operationStatus ) ){ 165 | //AND the bit with the byte then shift it right 166 | if( ( ( registers.operationStatus.val[ 0 ] & 0x08 ) >> 3 ) == 1 ){ 167 | Serial.println("Pre-charge FET is enabled (on) \t\t(PCHG high)"); 168 | }else{ 169 | Serial.println("Pre-charge FET is disabled (off) \t(PCHG low)"); 170 | } 171 | return true; //return if the bit read has been successful 172 | }else{ 173 | return false; 174 | } 175 | } 176 | 177 | boolean Lorro_BQ4050::getCHGstatus(){ 178 | if( readBlockReg( registers.operationStatus ) ){ 179 | //AND the bit with the byte then shift it right 180 | if( ( ( registers.operationStatus.val[ 0 ] & 0x04 ) >> 2 ) == 1 ){ 181 | Serial.println("Charging FET is enabled (on) \t\t(CHG high)"); 182 | }else{ 183 | Serial.println("Charging FET is disabled (off) \t\t(CHG low)"); 184 | } 185 | return true; //return if the bit read has been successful 186 | }else{ 187 | return false; 188 | } 189 | } 190 | 191 | boolean Lorro_BQ4050::getDSGstatus(){ 192 | if( readBlockReg( registers.operationStatus ) ){ 193 | //AND the bit with the byte then shift it right 194 | if( ( ( registers.operationStatus.val[ 0 ] & 0x02 ) >> 1 ) == 1 ){ 195 | Serial.println("Discharging FET is enabled (on) \t(DSG high)"); 196 | }else{ 197 | Serial.println("Discharging FET is disabled (off) \t(DSG low)"); 198 | } 199 | return true; //return if the bit read has been successful 200 | }else{ 201 | return false; 202 | } 203 | } 204 | 205 | boolean Lorro_BQ4050::getCellBalancingStatus(){ 206 | if( readBlockReg( registers.gaugingStatus ) ){ 207 | //AND the bit with the byte then shift it right 208 | if( ( ( registers.gaugingStatus.val[ 0 ] & 0x10 ) >> 4 ) == 1 ){ 209 | Serial.println("Cell balancing is enabled (on) \t\t(BAL_EN high)"); 210 | }else{ 211 | Serial.println("Cell balancing is disabled (off) \t(BAL_EN low)"); 212 | } 213 | return true; //return if the bit read has been successful 214 | }else{ 215 | return false; 216 | } 217 | } 218 | 219 | boolean Lorro_BQ4050::numberOfCells( uint8_t cellNum ){ 220 | //Catch to make sure no values too high pass 221 | if( cellNum > 4 ) cellNum = 4; 222 | //Reduce cell number value, so it starts from zero 223 | cellNum = cellNum - 1; 224 | //Mask off the end 2 bits by ANDing the byte with all ones apart from the end 2 bits 225 | dataFlash.settings.configuration.dAConfiguration.val = \ 226 | ( dataFlash.settings.configuration.dAConfiguration.val & 0xFC ); 227 | //Load the new data in by ORing the masked off value with the data value. 228 | dataFlash.settings.configuration.dAConfiguration.val = \ 229 | ( dataFlash.settings.configuration.dAConfiguration.val | cellNum ); 230 | //Use the writeFlash function to write the new number of cells value 231 | Serial.print( "DA config byte from cpp: " ); 232 | Serial.println( dataFlash.settings.configuration.dAConfiguration.val, HEX ); 233 | // writeFlash( DF.settings.configuration.dAConfiguration, DF.settings.configuration.dAConfiguration.val ); 234 | return true; 235 | } 236 | 237 | void Lorro_BQ4050::FETtoggle(){ 238 | writeCommand( BQ4050addr, FETcontrol ); 239 | } 240 | 241 | boolean Lorro_BQ4050::deviceReset(){ 242 | if( writeCommand( BQ4050addr, deviceResetReg ) ){ 243 | return true; 244 | }else{ 245 | return false; 246 | } 247 | } 248 | 249 | // I2C functions below here 250 | //---------------------------------------------- 251 | 252 | 253 | boolean Lorro_BQ4050::readDataReg( char devAddress, byte regAddress, byte *dataVal, uint8_t arrLen ){ 254 | 255 | //Function to read data from the device registers 256 | 257 | //Add in the device address to the buffer 258 | Wire.beginTransmission( devAddress ); 259 | //Add the one byte register address 260 | Wire.write( regAddress ); 261 | //Send out buffer and log response 262 | byte ack = Wire.endTransmission(); 263 | //If data is ackowledged, proceed 264 | if( ack == 0 ){ 265 | //Request a number of bytes from the device address 266 | Wire.requestFrom( devAddress , (int16_t) arrLen ); 267 | //If there is data in the in buffer 268 | if( Wire.available() > 0 ){ 269 | //Cycle through, loading data into array 270 | for( uint8_t i = 0; i < arrLen; i++ ){ 271 | dataVal[i] = Wire.read(); 272 | } 273 | } 274 | return true; 275 | }else{ 276 | return false; //if I2C comm fails 277 | } 278 | 279 | } 280 | 281 | boolean Lorro_BQ4050::writeDataReg( char devAddress, byte regAddress, byte *dataVal, uint8_t arrLen ){ 282 | 283 | //Function that writes data to a register with a one byte address 284 | 285 | //Create an array of all byte to be sent to enable PEC calculation later 286 | //Device address is shifted right 1 bit to simulate a write bit 287 | byte byteArr[ 6 ] = { ( byte )( devAddress << 1 ), regAddress, 0, 0, 0, 0 }; 288 | //Load address into buffer 289 | Wire.beginTransmission( devAddress ); 290 | //The register addres we're writing to 291 | Wire.write( regAddress ); 292 | //Cycle through the byte array 293 | for( int i = 0; i < arrLen; i++ ){ 294 | //Add the data after the device address and the register address 295 | byteArr[ i + 2 ] = dataVal [ i ]; 296 | //Send the data to the buffer 297 | Wire.write( dataVal[ i ] ); 298 | } 299 | //Calculate the PEC byte based on the other bytes being sent 300 | byte PECcheck = Compute_CRC8( byteArr, arrLen + 2 ); 301 | //Add PEC byte to buffer 302 | Wire.write( PECcheck ); 303 | //Send out buffer, logging whether an ack is received 304 | byte ack = Wire.endTransmission(); 305 | //return result of ack 306 | if( ack == 0 ){ 307 | return true; 308 | }else{ 309 | return false; //if I2C comm fails 310 | } 311 | 312 | } 313 | 314 | boolean Lorro_BQ4050::writeDFByteReg( char devAddress, int16_t regAddress, byte *data, uint8_t arrSize ){ 315 | 316 | //function that handles writing data to the flash on the BQ4050. 317 | //Data is between 1 and 4 bytes long 318 | //Addresses are 2 bytes long 319 | 320 | //For CRC calculation, the address needs to include the write bit, so shifts to the left 321 | byte addr = devAddress << 1; 322 | //This is a value that gets sent to the BQ4050 to let it know how many bytes are heading its way 323 | byte blockLen = arrSize + 2; 324 | //Desconstructs the 16 bit int into 2 bytes 325 | byte regAddress1 = byte( regAddress ); 326 | byte regAddress2 = byte( regAddress >> 8 ); 327 | //for simplicity, we create an array to hold all the byte we will be sending 328 | byte byteArr[37] = { addr, 0x44, blockLen, regAddress1, regAddress2, data[0], data[1], data[2], data[3] }; 329 | //This array is then sent to the CRC checker. The CRC includes all bytes sent. 330 | //The arrSize var is only the data, so regardless of its size, there are another 5 bytes to be sent 331 | byte PECcheck = Compute_CRC8( byteArr, arrSize + 5 ); 332 | 333 | //Send the bytes out 334 | Wire.beginTransmission( devAddress ); 335 | Wire.write( 0x44 ); 336 | Wire.write( arrSize + 2 ); 337 | //The address is little endian, so LSB is sent first 338 | Wire.write( regAddress1 ); 339 | Wire.write( regAddress2 ); 340 | //loop for the number of data bytes 341 | for( int i = 0; i < arrSize; i++ ){ 342 | Wire.write( data[i] ); 343 | } 344 | //send CRC at the end 345 | Wire.write( PECcheck ); 346 | byte ack = Wire.endTransmission(); 347 | if( ack == 0 ){ 348 | return true; 349 | }else{ 350 | return false; //if I2C comm fails 351 | } 352 | 353 | } 354 | 355 | boolean Lorro_BQ4050::writeCommand( char devAddress, byte regAddress ){ 356 | 357 | //Function that simply sends out a command to the device by way of writing 358 | //a zero to a particular address 359 | 360 | Wire.beginTransmission( devAddress ); 361 | //MAC access 362 | Wire.write( 0x44 ); 363 | //little endian of address 364 | Wire.write( regAddress ); 365 | Wire.write( 0x00 ); 366 | byte ack = Wire.endTransmission(); 367 | if( ack == 0 ){ 368 | return true; 369 | }else{ 370 | return false; //if I2C comm fails 371 | } 372 | 373 | } 374 | 375 | boolean Lorro_BQ4050::writeMACommand( char devAddress, byte regAddress ){ 376 | 377 | //Function that simply sends out a Manufacture Access command to the device 378 | //by way of writing a zero to a particular address 379 | 380 | //for simplicity, we create an array to hold all the byte we will be sending 381 | byte byteArr[5] = { devAddress, 0x44, 0x02, regAddress, 0x00 }; 382 | //This array is then sent to the CRC checker. The CRC includes all bytes sent. 383 | //The arrSize var is static, so there are always 5 bytes to be sent 384 | byte PECcheck = Compute_CRC8( byteArr, 5 ); 385 | 386 | Wire.beginTransmission( devAddress ); 387 | //MAC access 388 | Wire.write( 0x44 ); 389 | //number of bytes in to be sent. In when sending a command, this is always 2 390 | Wire.write( 0x02 ); 391 | //little endian of address 392 | Wire.write( regAddress ); 393 | Wire.write( 0x00 ); 394 | //send CRC at the end 395 | Wire.write( PECcheck ); 396 | byte ack = Wire.endTransmission(); 397 | if( ack == 0 ){ 398 | return true; 399 | }else{ 400 | return false; //if I2C comm fails 401 | } 402 | 403 | } 404 | 405 | byte crctable[256]; 406 | boolean printResults = false; 407 | 408 | void Lorro_BQ4050::CalculateTable_CRC8(){ 409 | 410 | //Function that generates byte array as a lookup table to quickly create a CRC8 for the PEC 411 | 412 | const byte generator = 0x07; 413 | /* iterate over all byte values 0 - 255 */ 414 | for (int divident = 0; divident < 256; divident++){ 415 | byte currByte = (byte)divident; 416 | /* calculate the CRC-8 value for current byte */ 417 | for (byte bit = 0; bit < 8; bit++){ 418 | if ((currByte & 0x80) != 0){ 419 | currByte <<= 1; 420 | currByte ^= generator; 421 | } 422 | else{ 423 | currByte <<= 1; 424 | } 425 | } 426 | /* store CRC value in lookup table */ 427 | crctable[divident] = currByte; 428 | if( printResults ){ 429 | if( divident % 16 == 0 && divident > 2 ){ 430 | Serial.println(); 431 | } 432 | if( currByte < 16 ) Serial.print( "0" ); 433 | Serial.print( currByte, HEX ); 434 | Serial.print( "\t" ); 435 | } 436 | } 437 | if( printResults ){ 438 | Serial.println(); 439 | } 440 | } 441 | 442 | byte Lorro_BQ4050::Compute_CRC8(byte *bytes, uint8_t byteLen){ 443 | 444 | //Function to check byte array to be sent out against lookup table to efficiently calculate PEC 445 | 446 | byte crc = 0; 447 | for( int i = 0; i < byteLen; i++ ){ 448 | /* XOR-in next input byte */ 449 | byte data = (byte)(bytes[i] ^ crc); 450 | /* get current CRC value = remainder */ 451 | crc = (byte)(crctable[data]); 452 | if( printResults ){ 453 | Serial.print( "byte value: " ); 454 | Serial.print( bytes[i], HEX ); 455 | Serial.print( "\tlookup position: " ); 456 | Serial.print( data, HEX ); 457 | Serial.print( "\tlookup value: " ); 458 | Serial.println( crc, HEX ); 459 | } 460 | } 461 | 462 | return crc; 463 | } 464 | -------------------------------------------------------------------------------- /Lorro_BQ4050.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file Lorro_BQ4050.h 4 | @author Lorro 5 | 6 | 7 | A library for interfacing with TI BQ4050 battery fuel gauge chip 8 | Includes template function in header file 9 | 10 | 11 | */ 12 | /**************************************************************************/ 13 | 14 | #include "Arduino.h" 15 | 16 | #define AtRateReg 0x04 17 | #define AtRateTimeToFullReg 0x05 18 | #define AtRateTimetoEmptyReg 0x06 19 | #define AtRateOKReg 0x07 20 | #define VoltageReg 0x08 21 | #define CurrentReg 0x0A 22 | #define StateOfChargeReg 0x0E 23 | #define DAStatus1reg 0x71 24 | #define CellVoltageFour 0x3C 25 | #define CellVoltageThree 0x3D 26 | #define CellVoltageTwo 0x3E 27 | #define CellVoltageOne 0x3F 28 | #define BatteryMode 0x03 29 | #define ManufacturerName 0x20 30 | #define TemperatureReg 0x20 31 | #define DeviceChemistryReg 0x22 32 | #define DFDAConfigurationReg1 0x45 //set to 0x13 for 4 cells (default 0x12 - 3 cell) 33 | #define DFDAConfigurationReg2 0x7B //set to 0x13 for 4 cells (default 0x12 - 3 cell) 34 | #define operationStatusReg 0x54 35 | #define FETcontrol 0x22 36 | #define deviceResetReg 0x41 37 | 38 | // char *operationStatusLabels[ 4 ][ 8 ] = { { "SLEEP", "XCHG", "XDSG", "PF", "SS", "SDV", "SEC1", "SEC0" }, 39 | // { "BTP_INT", "SMOOTH", "FUSE", "--", "PCHG", "CHG", "DSG", "PRES" }, 40 | // { "--", "--", "EMSHUT", "CB", "SLPCC", "SLPAD", "SMBLCAL", "INIT" }, 41 | // { "SLEEPM", "XL", "CAL_OFFSET", "CAL", "AUTOCALM", "AUTH", "LED", "SDM" } }; 42 | 43 | class Lorro_BQ4050{ 44 | public: 45 | Lorro_BQ4050( char addr ); 46 | boolean getOperationStatus(); 47 | boolean getChargingStatus(); 48 | boolean getGaugingStatus(); 49 | boolean getSafetyStatus(); 50 | boolean getSafetyAlert(); 51 | boolean getXCHGstatus(); 52 | boolean getXDSGstatus(); 53 | boolean getPCHGstatus(); 54 | boolean getCHGstatus(); 55 | boolean getDSGstatus(); 56 | boolean getCellBalancingStatus(); 57 | boolean numberOfCells( uint8_t cellNum ); 58 | void FETtoggle(); 59 | boolean deviceReset(); 60 | template 61 | boolean writeFlash( const T& dataParam, const S datVal){ 62 | 63 | //Function for writing to the data Flash register. 64 | //The first variable to come in is the struct and the second is the data. 65 | //This could be the dataParam.val value or an arbitrary value of correct type. 66 | 67 | //Calculate length of data 68 | constexpr uint8_t byteLen = sizeof( datVal ); 69 | //Create an array to hold the returned data 70 | byte valBytes[ byteLen ]; 71 | //Loop through array 72 | for( int i = 0; i < byteLen; i++ ){ 73 | //Shift the data out to the individual bytes, 8 bits at a time. 74 | valBytes[ i ] = datVal >> ( i * 8 ); 75 | } 76 | //Run function to write bytes into the register. 77 | if( writeDFByteReg( BQ4050addr, dataParam.addr, valBytes, byteLen ) ){ 78 | //delay to allow for the BQ4050 to process the newly flashed registers 79 | delay( 15 ); 80 | return true; 81 | }else{ 82 | return false; 83 | } 84 | 85 | } 86 | template 87 | boolean writeReg( const T& dataParam, const S datVal){ 88 | 89 | //Function for writing to the data register. 90 | //The first variable to come in is the struct and the second is the data. 91 | //This could be the dataParam.val value or an arbitrary value of correct type. 92 | 93 | //Calculate length of data 94 | constexpr uint8_t byteLen = sizeof( datVal ); 95 | //Create an array to hold the returned data 96 | byte valBytes[ byteLen ]; 97 | //Loop through array 98 | for( int i = 0; i < byteLen; i++ ){ 99 | //Shift the data out to the individual bytes, 8 bits at a time. 100 | valBytes[ i ] = datVal >> ( i * 8 ); 101 | } 102 | //Run function to write bytes into the register. 103 | if( writeDataReg( BQ4050addr, ( byte )dataParam.addr, valBytes, byteLen ) ){ 104 | return true; 105 | }else{ 106 | return false; 107 | } 108 | 109 | } 110 | template 111 | boolean readReg( T& dataParam ){ 112 | 113 | //This is a function for reading data words. 114 | //The number of bytes that make up a word is either 1 or 2. 115 | 116 | //Measure the number of bytes to look for 117 | constexpr uint8_t byteLen = sizeof( dataParam.val ); 118 | //Create an array to hold the returned data 119 | byte valBytes[ byteLen ]; 120 | //Function to handle the I2C comms. 121 | if( readDataReg( BQ4050addr, ( byte )dataParam.addr, valBytes, byteLen ) ){ 122 | //Cycle through array of data 123 | for( int i = 0; i < byteLen; i++ ){ 124 | //Shift each byte in to the right, in steps of 8 bits. The resulting 125 | //data is type cast, by getting the type with decltype 126 | dataParam.val = ( decltype( dataParam.val ) ) ( dataParam.val | ( valBytes[ i ] << ( 8 * i ) ) ); 127 | } 128 | return true; 129 | }else{ 130 | return false; 131 | } 132 | 133 | } 134 | template 135 | boolean readBlockReg( T& dataParam ){ 136 | 137 | //This is a function for reading data blocks. 138 | //Data blocks are returned with the first byte denoting how many data bytes are to follow 139 | //so ignore the value of the first byte as it is not data. 140 | //Function must be passed an array of bytes. 141 | 142 | //Work out how many bytes to request, plus 1 for number of bytes to follow byte 143 | constexpr uint8_t byteLen = sizeof( dataParam.val ) + 1; 144 | //Create a byte array to store the returned data 145 | byte valBytes[ byteLen ]; 146 | //Send the read function the correct data 147 | if( readDataReg( BQ4050addr, ( byte )dataParam.addr, valBytes, byteLen ) ){ 148 | //cycle through the array, one less than the length, to ignore the first byte 149 | for( int i = 0; i < ( byteLen - 1 ); i++ ){ 150 | //Skip over the first byte of the read array 151 | dataParam.val[ i ] = valBytes[ i + 1 ]; 152 | } 153 | return true; 154 | }else{ 155 | return false; 156 | } 157 | 158 | } 159 | void writeThreshold( int16_t datVal ); 160 | void writeProtectionsOCD1Delay( uint8_t datVal ); 161 | void writeOCD1Threshold(); 162 | char BQ4050addr; 163 | boolean getDABlock(); 164 | 165 | String operationStatusBits[32] = { "", 166 | "", 167 | "Emergency shutdown (active)", 168 | "Cell balancing (active)", 169 | "CC in sleep mode (active)", 170 | "ADC in sleep mode (active)", 171 | "CC calibration", 172 | "Initialisation after reset (active)", 173 | "Sleep mode triggered (active)", 174 | "400khz SMBus mode (active)", 175 | "Calibration offset", 176 | "Calibration output", 177 | "Auto calibrate CC", 178 | "Authentication in progress", 179 | "LED display on", 180 | "Shutdown triggered by command", 181 | "Sleep mode conditions met", 182 | "XCHG charging disabled", 183 | "XDSG discharging disabled", 184 | "Permanent failiure made", 185 | "Safety mode (active)", 186 | "Shutdown triggered by low pack voltage", 187 | "Security mode", 188 | "Security mode", 189 | "Battery trip point", 190 | "Smoothing active status", 191 | "fuse status", 192 | "", 193 | "Precharge FET status", 194 | "Charge FET status", 195 | "Discharge FET status", 196 | "System present low"}; 197 | struct Regt{ 198 | struct RemainingCapAlarmt{ 199 | uint16_t val = 300; //mAh 200 | uint8_t addr = 0x01; 201 | } remainingCapAlarm; 202 | struct RemainingTimeAlarmt{ 203 | uint16_t val = 10; //min 204 | uint8_t addr = 0x02; 205 | } remainingTimeAlarm; 206 | struct BatteryModet{ 207 | uint16_t val = 0; 208 | uint8_t addr = 0x03; 209 | } batteryMode; 210 | struct AtRatet{ 211 | int16_t val = 0; 212 | uint8_t addr = 0x04; 213 | } atRate; 214 | struct AtRateTimeToFullt{ 215 | uint16_t val = 0; 216 | uint8_t addr = 0x05; 217 | } atRateTimeToFull; 218 | struct AtRateTimeToEmptyt{ 219 | uint16_t val = 0; 220 | uint8_t addr = 0x06; 221 | } atRateTimeToEmpty; 222 | struct AtRateOKt{ 223 | uint16_t val = 0; 224 | uint8_t addr = 0x07; 225 | } atRateOK; 226 | struct Temperaturet{ 227 | uint16_t val = 0; 228 | uint8_t addr = 0x08; 229 | } temperature; 230 | struct Voltaget{ 231 | uint16_t val = 0; 232 | uint8_t addr = 0x09; 233 | } voltage; 234 | struct Currentt{ 235 | int16_t val = 0; 236 | uint8_t addr = 0x0A; 237 | } current; 238 | struct AverageCurrentt{ 239 | int16_t val = 0; 240 | uint8_t addr = 0x0B; 241 | } averageCurrent; 242 | struct MaxErrort{ 243 | uint8_t val = 0; 244 | uint8_t addr = 0x0C; 245 | } maxError; 246 | struct RelativeStateOfCharget{ 247 | uint8_t val = 0; 248 | uint8_t addr = 0x0D; 249 | } relativeStateOfCharge; 250 | struct AbsoluteStateOfCharget{ 251 | uint8_t val = 0; 252 | uint8_t addr = 0x0E; 253 | } absoluteStateOfCharge; 254 | struct RemainingCapacityt{ 255 | uint16_t val = 0; 256 | uint8_t addr = 0x0F; 257 | } remainingCapacity; 258 | struct FullChargeCapacityt{ 259 | uint16_t val = 0; 260 | uint8_t addr = 0x10; 261 | } fullChargeCapacity; 262 | struct RunTimeToEmptyt{ 263 | uint16_t val = 0; 264 | uint8_t addr = 0x11; 265 | } runTimeToEmpty; 266 | struct AverageTimeToEmptyt{ 267 | uint16_t val = 0; 268 | uint8_t addr = 0x12; 269 | } averageTimeToEmpty; 270 | struct AverageTimeToFullt{ 271 | uint16_t val = 0; 272 | uint8_t addr = 0x13; 273 | } averageTimeToFull; 274 | struct ChargingCurrentt{ 275 | uint16_t val = 0; 276 | uint8_t addr = 0x14; 277 | } chargingCurrent; 278 | struct ChargingVoltaget{ 279 | uint16_t val = 0; 280 | uint8_t addr = 0x15; 281 | } chargingVoltage; 282 | struct BatteryStatust{ 283 | uint16_t val = 0; 284 | uint8_t addr = 0x16; 285 | } batteryStatus; 286 | struct CycleCountt{ 287 | uint16_t val = 0; 288 | uint8_t addr = 0x17; 289 | } cycleCount; 290 | struct DesignCapacityt{ 291 | uint16_t val = 0; 292 | uint8_t addr = 0x18; 293 | } designCapacity; 294 | struct DesignVoltaget{ 295 | uint16_t val = 0; 296 | uint8_t addr = 0x19; 297 | } designVoltage; 298 | struct Specificationt{ 299 | uint16_t val = 0; 300 | uint8_t addr = 0x1A; 301 | } specification; 302 | struct ManufacturerDatat{ 303 | uint16_t val = 0; 304 | uint8_t addr = 0x1B; 305 | } manufacturerData; 306 | struct SerialNumbert{ 307 | uint16_t val = 0; 308 | uint8_t addr = 0x1C; 309 | } serialNumber; 310 | struct ManufacturerNamet{ 311 | byte val[ 18 ]; 312 | uint8_t addr = 0x20; 313 | } manufacturerName; 314 | struct DeviceNamet{ 315 | byte val[ 7 ]; 316 | uint8_t addr = 0x21; 317 | } deviceName; 318 | struct DeviceChemistryt{ 319 | byte val[ 5 ]; 320 | uint8_t addr = 0x22; 321 | } deviceChemistry; 322 | struct CellVoltage4t{ 323 | uint16_t val = 0; 324 | uint8_t addr = 0x3C; 325 | } cellVoltage4; 326 | struct CellVoltage3t{ 327 | uint16_t val = 0; 328 | uint8_t addr = 0x3D; 329 | } cellVoltage3; 330 | struct CellVoltage2t{ 331 | uint16_t val = 0; 332 | uint8_t addr = 0x3E; 333 | } cellVoltage2; 334 | struct CellVoltage1t{ 335 | uint16_t val = 0; 336 | uint8_t addr = 0x3F; 337 | } cellVoltage1; 338 | struct BTPDischargeSett{ 339 | uint16_t val = 0; 340 | uint8_t addr = 0x4A; 341 | } bTPDischargeSet; 342 | struct BTPChargeSett{ 343 | uint16_t val = 0; 344 | uint8_t addr = 0x4B; 345 | } bTPChargeSet; 346 | struct StateOfHealtht{ 347 | uint16_t val = 0; 348 | uint8_t addr = 0x4F; 349 | } stateOfHealth; 350 | struct SafetyAlertt{ 351 | byte val[ 5 ]; 352 | uint8_t addr = 0x50; 353 | char label[ 4 ][ 8 ][ 11 ] = { 354 | { "AOLDL", "-- ", "OCD2", "OCD1", "OCC2", "OCC1", "COV ", "CUV " }, 355 | { "-- ", "-- ", "OTD ", "OTC ", "ASCDL", "-- ", "ASCCL", "-- " }, 356 | { "CHGC", "OC ", "CTOS", "-- ", "PTOS", "-- ", "-- ", "OTF " }, 357 | { "-- ", "-- ", "-- ", "-- ", "UTD ", "UTC ", "PCHGC", "CHGV" } 358 | }; 359 | } safetyAlert; 360 | struct SafetyStatust{ 361 | byte val[ 5 ]; 362 | uint8_t addr = 0x51; 363 | char label[ 4 ][ 8 ][ 11 ] = { 364 | { "AOLDL", "AOLD", "OCD2", "OCD1", "OCC2", "OCC1", "COV ", "CUV " }, 365 | { "-- ", "-- ", "OTD ", "OTC ", "ASCDL", "ASCD", "ASCCL", "ASCC" }, 366 | { "CHGC", "OC ", "-- ", "CTO ", "-- ", "PTO ", "HWDF", "OTF " }, 367 | { "-- ", "-- ", "-- ", "-- ", "UTD ", "UTC ", "PCHGC", "CHGV" } 368 | }; 369 | } safetyStatus; 370 | struct PFAlertt{ 371 | byte val[ 5 ]; 372 | uint8_t addr = 0x52; 373 | } pFAlert; 374 | struct PFStatust{ 375 | byte val[ 5 ]; 376 | uint8_t addr = 0x53; 377 | } pFStatus; 378 | struct OperationStatust{ 379 | byte val[ 5 ]; 380 | uint8_t addr = 0x54; 381 | char label[ 4 ][ 8 ][ 11 ] = { 382 | { "BTP_INT", "SMOOTH", "FUSE", "-- ", "PCHG", "CHG ", "DSG ", "PRES" }, 383 | { "SLEEP", "XCHG", "XDSG", "PF ", "SS ", "SDV ", "SEC1", "SEC0" }, 384 | { "SLEEPM", "XL ", "CAL_OFFSET", "CAL ", "AUTOCALM", "AUTH", "LED ", "SDM " }, 385 | { "-- ", "-- ", "EMSHUT", "CB ", "SLPCC", "SLPAD", "SMBLCAL", "INIT" } 386 | }; 387 | } operationStatus; 388 | struct ChargingStatust{ 389 | byte val[ 5 ]; 390 | uint8_t addr = 0x55; 391 | char label[ 2 ][ 8 ][ 11 ] = { 392 | { "VCT ", "MCHG", "SU ", "IN ", "HV ", "MV ", "LV ", "PV " }, 393 | { "TAPER", "-- ", "-- ", "-- ", "-- ", "CCC ", "CVR ", "CCR " } 394 | }; 395 | } chargingStatus; 396 | struct GaugingStatust{ 397 | byte val[ 5 ]; 398 | uint8_t addr = 0x56; 399 | char label[ 2 ][ 8 ][ 11 ] = { 400 | { "CF ", "DSG ", "EDV ", "BAL_EN", "TC ", "TD ", "FC ", "FD " }, 401 | { "VDQ ", "EDV2", "EDV1 ", "-- ", "-- ", "FCCX", "-- ", "-- " } 402 | }; 403 | } gaugingStatus; 404 | struct ManufacturingStatust{ 405 | byte val[ 5 ]; 406 | uint8_t addr = 0x57; 407 | } manufacturingStatus; 408 | struct AFERegistert{ 409 | byte val[ 21 ]; 410 | uint8_t addr = 0x58; 411 | } aFERegister; 412 | struct LifetimeDataBlock1t{ 413 | byte val[ 32 ]; 414 | uint8_t addr = 0x60; 415 | } lifetimeDataBlock1; 416 | struct LifetimeDataBlock2t{ 417 | byte val[ 8 ]; 418 | uint8_t addr = 0x61; 419 | } lifetimeDataBlock2; 420 | struct LifetimeDataBlock3t{ 421 | byte val[ 16 ]; 422 | uint8_t addr = 0x62; 423 | } lifetimeDataBlock3; 424 | struct LifetimeDataBlock4t{ 425 | byte val[ 32 ]; 426 | uint8_t addr = 0x63; 427 | } lifetimeDataBlock4; 428 | struct LifetimeDataBlock5t{ 429 | byte val[ 20 ]; 430 | uint8_t addr = 0x64; 431 | } lifetimeDataBlock5; 432 | struct ManufacturerInfot{ 433 | byte val[ 32 ]; 434 | uint8_t addr = 0x70; 435 | } manufacturerInfo; 436 | struct DAStatust1t{ 437 | byte val[ 32 ]; 438 | uint8_t addr = 0x71; 439 | } dAStatust1; 440 | struct DAStatust2t{ 441 | byte val[ 14 ]; 442 | uint8_t addr = 0x72; 443 | } dAStatust2; 444 | } ; 445 | struct DFt{ 446 | struct Settingst{ 447 | struct Protectiont{ 448 | struct ProtectionConfigurationt{ 449 | uint8_t val = 0x02; 450 | uint16_t addr = 0x447C; 451 | } protectionConfiguration; 452 | } protection; 453 | struct Configurationt{ 454 | struct DAConfigurationt{ 455 | byte val = 0x12; //includes number of cells 456 | uint16_t addr = 0x457B; 457 | } dAConfiguration; 458 | struct SOCFlagConfigA{ 459 | uint16_t val = 0x0C8C; 460 | uint16_t addr = 0x4455; 461 | } sOCFlagConfigA; 462 | struct SOCFlagConfigB{ 463 | uint8_t val = 0x8C; 464 | uint16_t addr = 0x4457; 465 | } sOCFlagConfigB; 466 | struct CEDVGaugingConfigurationt{ 467 | uint16_t val = 0x0208; 468 | uint16_t addr = 0x458E; 469 | } cEDVGaugingConfiguration; 470 | } configuration; 471 | struct Manufacturingt{ 472 | struct MfgStatusInitt{ 473 | uint16_t val = 0x0218; 474 | uint16_t addr = 0x4340; 475 | } mfgStatusInit; 476 | } manufacturing; 477 | } settings; 478 | struct Protectionst{ 479 | struct CUVt{ //Cell undervoltage settings 480 | struct Thresholdt{ 481 | int16_t val = 2850; //milliVolts 482 | uint16_t addr = 0x4481; 483 | } threshold; 484 | struct Delayt{ 485 | uint8_t val = 2; //seconds 486 | uint16_t addr = 0x4483; 487 | } delay; 488 | struct Recoveryt{ 489 | int16_t val = 2900; //milliVolts 490 | uint16_t addr = 0x4484; 491 | } recovery; 492 | } CUV; 493 | struct COVt{ //Cell overvoltage settings 494 | struct ThresholdLowTempt{ 495 | int16_t val = 4280; //milliVolts 496 | static const uint16_t addr = 0x4486; 497 | } thresholdLowTemp; 498 | struct ThresholdStandardTempt{ 499 | int16_t val = 4280; //milliVolts 500 | uint16_t addr = 0x4488; 501 | } thresholdStandardTemp; 502 | struct ThresholdHighTempt{ 503 | int16_t val = 4280; //milliVolts 504 | uint16_t addr = 0x448A; 505 | } thresholdHightTemp; 506 | struct ThresholdRecTempt{ 507 | int16_t val = 4280; //milliVolts 508 | uint16_t addr = 0x448C; 509 | } thresholdRecTemp; 510 | struct Delayt{ 511 | uint8_t val = 2; //seconds 512 | uint16_t addr = 0x448E; 513 | } delay; 514 | struct RecoveryLowTempt{ 515 | int16_t val = 4280; //milliVolts 516 | uint16_t addr = 0x448F; 517 | } recoveryLowTemp; 518 | struct RecoveryStandardTempt{ 519 | int16_t val = 4280; //milliVolts 520 | uint16_t addr = 0x4491; 521 | } recoveryStandardTemp; 522 | struct RecoveryHighTempt{ 523 | int16_t val = 4280; //milliVolts 524 | uint16_t addr = 0x4493; 525 | } recoveryHighTemp; 526 | struct RecoveryRecTempt{ 527 | int16_t val = 4280; //milliVolts 528 | uint16_t addr = 0x4495; 529 | } recoveryRecTemp; 530 | } COV; 531 | struct OCC1t{ //Over Current Charge settings 532 | struct Thresholdt{ 533 | int16_t val = 5000; //milliAmps (positive value is charging, negative is discharging) 534 | uint16_t addr = 0x4497; 535 | } threshold; 536 | struct Delayt{ 537 | uint8_t val = 6; //seconds 538 | uint16_t addr = 0x4499; 539 | } delay; 540 | } OCC1; 541 | struct OCC2t{ 542 | struct Thresholdt{ 543 | int16_t val = 5900; //milliAmps (positive value is charging, negative is discharging) 544 | uint16_t addr = 0x449A; 545 | } threshold ; 546 | struct Delayt{ 547 | uint8_t val = 3; //seconds 548 | uint16_t addr = 0x449C; 549 | } delay; 550 | } OCC2; 551 | struct OCCt{ 552 | struct Thresholdt{ 553 | int16_t val = -200; //milliAmps (positive value is charging, negative is discharging) 554 | uint16_t addr = 0x449D; 555 | } threshold ; 556 | struct Delayt{ 557 | uint8_t val = 5; //seconds 558 | uint16_t addr = 0x449F; 559 | } delay; 560 | } OCC; 561 | struct OCD1t{ //Over Current Discharge settings 562 | struct Thresholdt{ 563 | int16_t val = -5000; //milliAmps (positive value is charging, negative is discharging) 564 | static const uint16_t addr = 0x44A0; 565 | } threshold; 566 | struct Delayt{ 567 | uint8_t val = 6; //seconds 568 | static const uint16_t addr = 0x44A2; 569 | } delay; 570 | } OCD1; 571 | struct OCD2t{ 572 | struct Thresholdt{ 573 | int16_t val = -6000; //milliAmps (positive value is charging, negative is discharging) 574 | uint16_t addr = 0x44A3; 575 | } threshold ; 576 | struct Delayt{ 577 | uint8_t val = 3; //seconds 578 | uint16_t addr = 0x44A5; 579 | } delay; 580 | } OCD2; 581 | struct OCDt{ 582 | struct Thresholdt{ 583 | int16_t val = 200; //milliAmps (positive value is charging, negative is discharging) 584 | uint16_t addr = 0x44A6; 585 | } threshold ; 586 | struct Delayt{ 587 | int16_t val = 5; //seconds 588 | uint16_t addr = 0x44A8; 589 | } delay; 590 | } OCD; 591 | struct OCt{ 592 | struct Thresholdt{ 593 | int16_t val = 2000; //mAh 594 | uint16_t addr = 0x44DF; 595 | } threshold; 596 | struct Recoveryt{ 597 | uint8_t val = 2; //mAh 598 | uint16_t addr = 0x44E1; 599 | } recovery; 600 | struct RSOCRecoveryt{ 601 | uint8_t val = 90; //% 602 | uint16_t addr = 0x44E3; 603 | } rSOCRecovery; 604 | } OC; 605 | } protections; 606 | struct AdvancedChargeAlgorithmt{ 607 | struct LowTempChargingt{ 608 | struct Voltaget{ 609 | int16_t val = 4000; //milliVolts 610 | uint16_t addr = 0x453C; 611 | } voltage; 612 | struct CurrentLowt{ 613 | int16_t val = 80; //milliAmps 614 | uint16_t addr = 0x453E; 615 | } currentLow; 616 | struct CurrentMedt{ 617 | int16_t val = 400; //milliAmps 618 | uint16_t addr = 0x4540; 619 | } currentMed; 620 | struct CurrentHight{ 621 | int16_t val = 800; //milliAmps 622 | uint16_t addr = 0x4542; 623 | } currentHigh; 624 | } lowTempCharging; 625 | struct StandardTempChargingt{ 626 | struct Voltaget{ 627 | int16_t val = 4150; //milliVolts 628 | uint16_t addr = 0x4544; 629 | } voltage; 630 | struct CurrentLowt{ 631 | int16_t val = 100; //milliAmps 632 | uint16_t addr = 0x4546; 633 | } currentLow; 634 | struct CurrentMedt{ 635 | int16_t val = 500; //milliAmps 636 | uint16_t addr = 0x4548; 637 | } currentMed; 638 | struct CurrentHight{ 639 | int16_t val = 1000; //milliAmps 640 | uint16_t addr = 0x454A; 641 | } currentHigh; 642 | } standardTempCharging; 643 | struct HighTempChargingt{ 644 | struct Voltaget{ 645 | int16_t val = 4000; //milliVolts 646 | uint16_t addr = 0x454C; 647 | } voltage; 648 | struct CurrentLowt{ 649 | int16_t val = 80; //milliAmps 650 | uint16_t addr = 0x454E; 651 | } currentLow; 652 | struct CurrentMedt{ 653 | int16_t val = 400; //milliAmps 654 | uint16_t addr = 0x4550; 655 | } currentMed; 656 | struct CurrentHight{ 657 | int16_t val = 800; //milliAmps 658 | uint16_t addr = 0x4552; 659 | } currentHigh; 660 | } highTempCharging; 661 | struct RecTempChargingt{ 662 | struct Voltaget{ 663 | int16_t val = 4150; //milliVolts 664 | uint16_t addr = 0x4554; 665 | } voltage; 666 | struct CurrentLowt{ 667 | int16_t val = 100; //milliAmps 668 | uint16_t addr = 0x4556; 669 | } currentLow; 670 | struct CurrentMedt{ 671 | int16_t val = 500; //milliAmps 672 | uint16_t addr = 0x4558; 673 | } currentMed; 674 | struct CurrentHight{ 675 | int16_t val = 1000; //milliAmps 676 | uint16_t addr = 0x455A; 677 | } currentHigh; 678 | } recTempCharging; 679 | } advancedChargeAlgorithm; 680 | struct GasGaugingt{ 681 | struct Designt{ 682 | struct DesignCapacitymAht{ 683 | int16_t val = 2000; //milliAmp hours 684 | uint16_t addr = 0x444D; 685 | } designCapacitymAh; 686 | struct DesignCapacitymWht{ 687 | int16_t val = 2960; //milliWatt hours 688 | uint16_t addr = 0x444F; 689 | } designCapacitymWh; 690 | struct DesignVoltaget{ 691 | int16_t val = 14800; //milliVolts 692 | uint16_t addr = 0x4451; 693 | } designVoltage; 694 | } design; 695 | struct Statet{ 696 | struct LearnedFCCt{ 697 | int16_t val = 2600; 698 | uint16_t addr = 0x4100; 699 | } learnedFCC; 700 | struct CycleCountt{ 701 | uint16_t val = 3; 702 | uint16_t addr = 0x4140; 703 | } cycleCount; 704 | } state; 705 | struct CEDVcfgt{ 706 | struct EMFt{ 707 | uint16_t val = 3515; 708 | uint16_t addr = 0x4590; 709 | } EMF; 710 | struct C0t{ 711 | uint16_t val = 115; 712 | uint16_t addr = 0x4592; 713 | } C0; 714 | struct R0t{ 715 | uint16_t val = 1; 716 | uint16_t addr = 0x4594; 717 | } R0; 718 | struct T0t{ 719 | uint16_t val = 37941; 720 | uint16_t addr = 0x4596; 721 | } T0; 722 | struct R1t{ 723 | uint16_t val = 618; 724 | uint16_t addr = 0x4598; 725 | } R1; 726 | struct TCt{ 727 | uint8_t val = 9; 728 | uint16_t addr = 0x459A; 729 | } TC; 730 | struct C1t{ 731 | uint8_t val = 0; 732 | uint16_t addr = 0x459B; 733 | } C1; 734 | } CEDVcfg; 735 | } gasGauging; 736 | } ; 737 | 738 | private: 739 | boolean readDataReg( char devAddress, byte regAddress, byte *dataVal, uint8_t arrLen ); 740 | boolean writeDataReg( char devAddress, byte regAddress, byte *dataVal, uint8_t arrLen ); 741 | boolean writeDFByteReg( char devAddress, int16_t regAddress, byte *data, uint8_t arrSize ); 742 | boolean writeCommand( char devAddress, byte regAddress ); 743 | boolean writeMACommand( char devAddress, byte regAddress ); 744 | void CalculateTable_CRC8(); 745 | byte Compute_CRC8(byte *bytes, uint8_t byteLen); 746 | 747 | }; 748 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lorro_BQ4050 2 | 3 | A library for interfacing with Texas Instruments BQ4050 fuel gauge chip. 4 | This chip has a comprehensive set of capabilities and many settings required 5 | to use it properly. You will need to refer to the TI TRM to get a description 6 | of all the different registers 7 | http://www.ti.com/lit/pdf/sluuaq3 8 | This library enables easy reading of the registers and reading and writing of 9 | the flash registers. The data is organised into a data structure similar to that 10 | outlined in the TRM and for each entry includes the value, address and data type. 11 | 12 | 13 | 14 | ## Compatibility 15 | 16 | MCU | Tested Works | Doesn't Work | Not Tested | Notes 17 | ------------------ | :----------: | :----------: | :---------: | ----- 18 | Atmega328 @ 16MHz | | | X | 19 | Atmega328 @ 12MHz | | | X | 20 | Atmega32u4 @ 16MHz | | | X | 21 | Atmega32u4 @ 8MHz | | | X | 22 | ESP8266 | | | X | 23 | ESP32 | | | X | 24 | Atmega2560 @ 16MHz | | | X | 25 | ATSAM3X8E | | | X | 26 | ATSAM21D | | | X | 27 | ATtiny85 @ 16MHz | | | X | 28 | ATtiny85 @ 8MHz | | | X | 29 | Intel Curie @ 32MHz| | | X | 30 | STM32F2 | | | X | 31 | Teensy 3.2 | X | | | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | Lorro BQ4050 2 | 3 | Arduino library for BQ4050 battery fuel gauge, using SMBUS communication which is based on I2C 4 | -------------------------------------------------------------------------------- /examples/readFuel/readFuel.ino: -------------------------------------------------------------------------------- 1 | /** 2 | Example of how to read battery fuel percentage, total voltage, cell voltages and current 3 | from TI BQ4050 fuel gauge chip. 4 | Fuel percentage reading will not be correct unless the battery has been calibrated for this chip 5 | **/ 6 | 7 | //Libraries to be included 8 | #include "Arduino.h" 9 | #include 10 | 11 | //Default address for device. Note, it is without read/write bit. When read with analyser, 12 | //this will appear 1 bit shifted to the left 13 | #define BQ4050addr 0x0B 14 | //Initialise the device and library 15 | Lorro_BQ4050 BQ4050( BQ4050addr ); 16 | //Instantiate the structs 17 | extern Lorro_BQ4050::Regt registers; 18 | 19 | uint32_t previousMillis; 20 | uint16_t loopInterval = 1000; 21 | 22 | void setup() { 23 | 24 | Serial.begin(115200); 25 | 26 | } 27 | 28 | void loop() { 29 | 30 | uint32_t currentMillis = millis(); 31 | 32 | if( currentMillis - previousMillis > loopInterval ){ 33 | previousMillis = currentMillis; 34 | 35 | // Lorro_BQ4050::Regt registers; 36 | BQ4050.readReg( registers.relativeStateOfCharge ); 37 | Serial.print( "State of charge: " ); 38 | Serial.print( registers.relativeStateOfCharge.val ); 39 | Serial.println( "%" ); 40 | delay( 15 ); 41 | 42 | BQ4050.readReg( registers.voltage ); 43 | Serial.print( "Pack voltage: " ); 44 | Serial.print( registers.voltage.val ); 45 | Serial.println( "mV" ); 46 | delay( 15 ); 47 | 48 | BQ4050.readReg( registers.cellVoltage1 ); 49 | Serial.print( "Cell voltage 1: " ); 50 | Serial.print( registers.cellVoltage1.val ); 51 | Serial.println( "mV" ); 52 | delay( 15 ); 53 | 54 | BQ4050.readReg( registers.cellVoltage2 ); 55 | Serial.print( "Cell voltage 2: " ); 56 | Serial.print( registers.cellVoltage2.val ); 57 | Serial.println( "mV" ); 58 | delay( 15 ); 59 | 60 | BQ4050.readReg( registers.cellVoltage3 ); 61 | Serial.print( "Cell voltage 3: " ); 62 | Serial.print( registers.cellVoltage3.val ); 63 | Serial.println( "mV" ); 64 | delay( 15 ); 65 | 66 | BQ4050.readReg( registers.cellVoltage4 ); 67 | Serial.print( "Cell voltage 4: " ); 68 | Serial.print( registers.cellVoltage4.val ); 69 | Serial.println( "mV" ); 70 | delay( 15 ); 71 | 72 | BQ4050.readReg( registers.current ); 73 | Serial.print( "Current: " ); 74 | Serial.print( registers.current.val ); 75 | Serial.println( "mA" ); 76 | delay( 15 ); 77 | 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /examples/writeConfig/writeConfig.ino: -------------------------------------------------------------------------------- 1 | /** 2 | Example of how to write values to the flash storage on the TI BQ4050 fuel gauge chip. 3 | Writing to the data flash will only work if the chip is in the unsealed state. 4 | Most shipped products that use the chip such as smart batteries, will be in the 5 | sealed state. 6 | If you are starting with a fresh chip and want to calibrate your own battery, 7 | this example will be useful. 8 | These values are purely demonstrative. You will need to read through the docs 9 | at https://www.ti.com/tool/GPCCEDV 10 | **/ 11 | 12 | //Libraries to be included 13 | #include "Arduino.h" 14 | #include "Lorro_BQ4050.h" 15 | 16 | //Default address for device. Note, it is without read/write bit. When read with analyser, 17 | //this will appear 1 bit shifted to the left 18 | #define BQ4050addr 0x0B 19 | //Initialise the device and library 20 | Lorro_BQ4050 BQ4050( BQ4050addr ); 21 | //Instantiate the structs 22 | // Lorro_BQ4050::Regt registers; 23 | 24 | void setup(){ 25 | 26 | //In the setup function, we simply write 3 values in 3 different ways. You can either go through 27 | //the header file and edit the values there, or send them from here in the main program 28 | //The thing to be aware of is the data type. The values range from 1 to 4 bytes in length 29 | //and are either signed or unsigned. 30 | 31 | //Instantiate the data structure 32 | Lorro_BQ4050::DFt DataFlash; 33 | 34 | //This is how to write a value from the value that is stored in the header file. 35 | //If you edit the header file directly, use this method to write the flash from 36 | //your main program. This method means you don't need to know the data type in 37 | //the main program. 38 | BQ4050.writeFlash( DataFlash.protections.OCD1.threshold, DataFlash.protections.OCD1.threshold.val ); 39 | 40 | //Put a delay of at least 15mS between writing different values, otherwise the 41 | //chip won't reliably respond properly 42 | delay(15); 43 | 44 | //The second method is if you write the value from the main program. You need to 45 | //know the data type of the value you are sending for this method. 46 | uint8_t delayVal = 6; 47 | BQ4050.writeFlash( DataFlash.protections.OCD1.delay, delayVal ); 48 | delay(15); 49 | 50 | //The third method is if you don't want to look up the data type, you can use 51 | //the decltype function to return the datatype from the header file and cast the 52 | //value to be written to that type. 53 | BQ4050.writeFlash( DataFlash.gasGauging.state.learnedFCC, ( decltype( DataFlash.gasGauging.state.learnedFCC.val ) )2600 ); 54 | 55 | //This is a function dedicated to writing the flash value for the number of number 56 | //of cells in the pack. Any value above 4 will be cut to 4. 57 | BQ4050.numberOfCells( 4 ); 58 | 59 | } 60 | 61 | void loop(){ 62 | 63 | } 64 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Lorro BQ4050 2 | version=0.2 3 | author=Lorro 4 | maintainer=Lorro 5 | sentence=Arduino library for BQ4050 battery fuel gauge, using SMBUS communication which is based on I2C 6 | paragraph=Arduino library for BQ4050 battery fuel gauge, using SMBUS communication which is based on I2C. Library incorporates basic reading and writing functions. 7 | category=Communication 8 | url=https://github.com/Lawmate/BQ4050 9 | architectures=* 10 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Software License Agreement (BSD License) 2 | 3 | Copyright (c) 2019, Lorro 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. Neither the name of the copyright holders nor the 14 | names of its contributors may be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY 18 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY 21 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | --------------------------------------------------------------------------------