Skip to content

Commit b86eeb0

Browse files
committed
Improved readout
1 parent ae53665 commit b86eeb0

File tree

3 files changed

+64
-46
lines changed

3 files changed

+64
-46
lines changed

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@ Download and extract the repository. In the Arduino IDE open the sketch with Fil
1818

1919
## Identifying the right connection
2020
The minimal connector layout is: | GROUND | THERMISTOR (103AT) | CLOCK | DATA | VCC (11 or 14 volt) | (clock and data my be switched).
21-
- The thermistor connection has 10 kOhms to ground at 25 degree celsius.
22-
- Clock und data connectors have the same resistance (between 300 kOhm to 1 MOhm) to ground.
23-
- VCC may not be enabled. Sometimes it gets enabled when *Host Present* is connected to ground or clock and data are pulled high to 3.3 or 5 volt.
21+
- The **thermistor** connection has 10 kOhms to ground at 25 degree celsius.
22+
- **Clock** und data connectors have the same resistance (around 1 MOhm) to ground.
23+
- **VCC** may not be enabled. Sometimes it gets enabled when *Host Present* is connected to ground or clock and data are pulled high to 3.3 or 5 volt.
24+
25+
Some packs (e.g.for IBM-T41 with bq29310) require once an external voltage (e.g. 11 volt) at the VCC connector to initially get alive after full discharge condition.
26+
2427
If you see more connectors, the may be used for:
2528
- A second (adjacent) ground and / or VCC to reduce the connector resistance for the supply current.
2629
- A *Battery Present* signal. This connector is internally connected to ground at the battery pack and NOT connected to ground at the PC.

SBMInfo/SBMInfo.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
#define AverageCurrent 0x0B // of last minute
3131
#define MAX_ERROR 0x0C // Byte - of state of charge calculation
3232

33-
#define RELATIVE_SOC 0x0D // Byte - StateOfCharge
33+
#define RELATIVE_SOC 0x0D // Byte - relative charge
3434
#define ABSOLUTE_SOC 0x0E // Byte
3535
#define REMAINING_CAPACITY 0x0F
3636
#define FULL_CHARGE_CAPACITY 0x10
@@ -53,11 +53,11 @@
5353
#define MANUFACTURER_DATA 0x23 // Data
5454
#define RESERVED_2 0x25 - 0x2E
5555

56-
#define PACK_STATUS 0x2F // r/w Word - OptionalMfgFunction5
56+
#define PACK_STATUS 0x2F // r/w Word - OptionalMfgFunction5, Block (size 11) for bq8011
5757

5858
#define RESERVED_3 0x30 - 0x3B
5959

60-
#define CELL4_VOLTAGE 0x3C // r/w Word - OptionalMfgFunction4 - Individual cell voltages don't work on Sony, Lenovo and Dell Packs
60+
#define CELL4_VOLTAGE 0x3C // r/w Word - OptionalMfgFunction4 - Individual cell voltages don't work on most (Sony, Lenovo and Dell) packs
6161
#define CELL3_VOLTAGE 0x3D // r/w Word - OptionalMfgFunction3
6262
#define CELL2_VOLTAGE 0x3E // r/w Word - OptionalMfgFunction2
6363
#define CELL1_VOLTAGE 0x3F // r/w Word - OptionalMfgFunction1

SBMInfo/SBMInfo.ino

Lines changed: 55 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ uint8_t scanForAttachedI2CDevice(void);
9595
bool testReadAndPrint();
9696

9797
void TogglePin(uint8_t aPinNr);
98+
99+
#define I2C_RETRY_DELAY_MILLIS 10 // 5 was successful for me
98100
uint16_t readWord(uint8_t aCommand);
99101
void writeWord(uint8_t aCommand, uint16_t aValue);
100102
int readWordFromManufacturerAccess(uint16_t aManufacturerCommand);
@@ -149,17 +151,19 @@ const char Current[] PROGMEM = "Current";
149151
const char Average_Current_of_last_minute[] PROGMEM = "Average current of last minute";
150152
const char Temperature[] PROGMEM = "Temperature";
151153

152-
#define VOLTAGE_PRINT_DELTA_MILLIVOLT 5
154+
#define VOLTAGE_PRINT_DELTA_MILLIVOLT 5 // Print only if changed by two ore more mV
155+
#define VOLTAGE_PRINT_DELTA_MILLIAMPERE 2 // Print only if changed by two ore more mA
156+
#define VOLTAGE_PRINT_DELTA_MILLIDEGREE 100 // Print only if changed by 0.1 ore more degree
153157

154158
struct SBMFunctionDescriptionStruct sSBMDynamicFunctionDescriptionArray[] = { {
155159
FULL_CHARGE_CAPACITY, Full_Charge_Capacity, &printCapacity, "" }/* DescriptionLCD must be not NULL */, {
156160
REMAINING_CAPACITY, Remaining_Capacity, &printCapacity, " remCapacity" }, {
157161
RELATIVE_SOC, Relative_Charge, &printPercentage, " rel charge " }, {
158162
ABSOLUTE_SOC, Absolute_Charge, &printPercentage }, {
159163
VOLTAGE, Voltage, &printVoltage, "", VOLTAGE_PRINT_DELTA_MILLIVOLT } /* DescriptionLCD must be not NULL */, {
160-
CURRENT, Current, &printCurrent, "", 1 } /* DescriptionLCD must be not NULL */, {
164+
CURRENT, Current, &printCurrent, "", VOLTAGE_PRINT_DELTA_MILLIAMPERE } /* DescriptionLCD must be not NULL */, {
161165
AverageCurrent, Average_Current_of_last_minute, &printCurrent }, {
162-
TEMPERATURE, Temperature, &printTemperature, NULL, 100 }, {
166+
TEMPERATURE, Temperature, &printTemperature, NULL, VOLTAGE_PRINT_DELTA_MILLIDEGREE }, {
163167
RUN_TIME_TO_EMPTY, Minutes_remaining_until_empty, &printTime, " min to empty " }, {
164168
AVERAGE_TIME_TO_EMPTY, Average_minutes_remaining_until_empty, &printTime }, {
165169
TIME_TO_FULL, Minutes_remaining_for_full_charge, &printTime, " min to full " }, {
@@ -188,7 +192,7 @@ STATE_OF_HEALTH, State_of_Health } };
188192
bool sCapacityModePower; // false = current, true = power
189193
uint16_t sDesignVoltage; // to retrieve last value for mWh to mA conversion
190194
uint16_t sDesignCapacity; // to compute relative capacity percent
191-
uint16_t sCurrent; // to decide if print "time to" values
195+
int16_t sCurrent; // to decide if print "time to" values
192196
uint8_t sGlobalReadError;
193197
uint8_t sLastGlobalReadError;
194198

@@ -290,6 +294,8 @@ void loop() {
290294
printFunctionDescriptionArray(sSBMDynamicFunctionDescriptionArray,
291295
(sizeof(sSBMDynamicFunctionDescriptionArray) / sizeof(SBMFunctionDescriptionStruct)), true);
292296
printSBMNonStandardInfo(true);
297+
298+
// clear the display of 'H' for sGlobalReadError
293299
myLCD.setCursor(19, 0);
294300
myLCD.print(' ');
295301
} else {
@@ -305,10 +311,12 @@ void loop() {
305311
Serial.print(F("\r\nsGlobalReadError changed to: "));
306312
Serial.println(sGlobalReadError);
307313
Serial.flush();
314+
308315
if (sGlobalReadError == 0) {
316+
// print info again
309317
printInitialInfo();
310-
311318
} else {
319+
// display 'H' for sGlobalReadError
312320
myLCD.setCursor(19, 0);
313321
myLCD.print('H');
314322
}
@@ -426,12 +434,7 @@ void printInitialInfo() {
426434
Serial.flush();
427435
}
428436

429-
/*
430-
* First write the command/function address byte, then read the word value for this function
431-
* From the BQ spec: The processor then sends the bq2060 device address of 0001011 (bits 7–1)
432-
* plus a R/W bit (bit 0) followed by an SMBus command code.
433-
*/
434-
uint16_t readWord(uint8_t aCommand) {
437+
void writeCommandWithRetry(uint8_t aCommand) {
435438
Wire.beginTransmission(sI2CDeviceAddress);
436439
Wire.write(aCommand);
437440
sGlobalReadError = Wire.endTransmission(false); // do not send stop, is required for some packs
@@ -443,12 +446,27 @@ uint16_t readWord(uint8_t aCommand) {
443446
* 4 .. other twi error (lost bus arbitration, bus error, ..)
444447
* 5 .. timeout
445448
*/
449+
if (sGlobalReadError == 2) {
450+
delay(I2C_RETRY_DELAY_MILLIS);
451+
// Try again
452+
Wire.beginTransmission(sI2CDeviceAddress);
453+
Wire.write(aCommand);
454+
sGlobalReadError = Wire.endTransmission(false); // do not send stop, is required for some packs
455+
}
456+
}
457+
458+
/*
459+
* First write the command/function address byte, then read the word value for this function
460+
* From the BQ spec: The processor then sends the bq2060 device address of 0001011 (bits 7–1)
461+
* plus a R/W bit (bit 0) followed by an SMBus command code.
462+
*/
463+
uint16_t readWord(uint8_t aCommand) {
464+
writeCommandWithRetry(aCommand);
446465
if (sGlobalReadError != 0) {
447466
#ifdef DEBUG
448467
Serial.print(F("Error at I2C access: "));
449468
Serial.println(sGlobalReadError);
450469
#endif
451-
// Wire.endTransmission(true);
452470
return 0xFFFF;
453471
} else {
454472
Wire.requestFrom(sI2CDeviceAddress, (uint8_t) 2);
@@ -478,9 +496,7 @@ int readWordFromManufacturerAccess(uint16_t aManufacturerCommand) {
478496
}
479497

480498
uint8_t readBlock(uint8_t aCommand, uint8_t *aDataBufferPtr, uint8_t aDataBufferLength) {
481-
Wire.beginTransmission(sI2CDeviceAddress);
482-
Wire.write(aCommand);
483-
Wire.endTransmission(false);
499+
writeCommandWithRetry(aCommand);
484500
Wire.requestFrom(sI2CDeviceAddress, (uint8_t) 1);
485501

486502
// First read length of data
@@ -491,6 +507,7 @@ uint8_t readBlock(uint8_t aCommand, uint8_t *aDataBufferPtr, uint8_t aDataBuffer
491507
Serial.print(F("tLengthOfData="));
492508
Serial.println(tLengthOfData);
493509
#endif
510+
aDataBufferLength = aDataBufferLength - 1; // we read later with tLengthOfData + 1
494511

495512
if (tLengthOfData > aDataBufferLength) {
496513
Serial.println();
@@ -505,9 +522,8 @@ uint8_t readBlock(uint8_t aCommand, uint8_t *aDataBufferPtr, uint8_t aDataBuffer
505522
/*
506523
* It is foolproof to start a new transmission here
507524
*/
508-
Wire.beginTransmission(sI2CDeviceAddress);
509-
Wire.write(aCommand);
510-
Wire.endTransmission(false);
525+
writeCommandWithRetry(aCommand);
526+
511527
#ifdef DEBUG
512528
uint8_t tNumberOfDataReceived = Wire.requestFrom(sI2CDeviceAddress, (uint8_t) (tLengthOfData + 1)); // +1 since the length is read again
513529
Serial.print(F("tNumberOfDataReceived="));
@@ -648,7 +664,7 @@ void printCapacity(struct SBMFunctionDescriptionStruct *aSBMFunctionDescription,
648664
if (sCapacityModePower) {
649665
// print also mA since changing capacity mode did not work
650666
Serial.print(" | ");
651-
uint8_t tCapacityCurrent = (aCapacity * 10000L) / sDesignVoltage;
667+
uint16_t tCapacityCurrent = (aCapacity * 10000L) / sDesignVoltage;
652668
Serial.print(tCapacityCurrent);
653669
Serial.print(StringCapacityModeCurrent);
654670
Serial.print('h');
@@ -697,9 +713,6 @@ void printCapacity(struct SBMFunctionDescriptionStruct *aSBMFunctionDescription,
697713
}
698714
}
699715

700-
/*
701-
* Print only if changed by two ore more mV
702-
*/
703716
void printVoltage(struct SBMFunctionDescriptionStruct *aSBMFunctionDescription, uint16_t aVoltage) {
704717
Serial.print((float) aVoltage / 1000, 3);
705718
Serial.print(" V");
@@ -722,27 +735,22 @@ void printVoltage(struct SBMFunctionDescriptionStruct *aSBMFunctionDescription,
722735
}
723736
}
724737

725-
/*
726-
* Print only if changed by two ore more mA
727-
*/
728738
void printCurrent(struct SBMFunctionDescriptionStruct *aSBMFunctionDescription, uint16_t aCurrent) {
729-
sCurrent = aCurrent;
739+
int tCurrent = (int) aCurrent; // current can be negative
740+
sCurrent = tCurrent;
730741

731-
Serial.print((int) aCurrent);
742+
Serial.print(tCurrent);
732743
Serial.print(" mA");
733744
if (aSBMFunctionDescription->DescriptionLCD != NULL) {
734745
// print 7 character from 12 to 18
735746
myLCD.setCursor(9, 0);
736747
myLCD.print(" "); // clear old value from 9 to 19 incl. leading and trailing spaces
737748
myLCD.setCursor(12, 0);
738-
myLCD.print((int) aCurrent);
749+
myLCD.print(tCurrent);
739750
myLCD.print(" mA");
740751
}
741752
}
742753

743-
/*
744-
* Print only if changed by more than 0.1 C
745-
*/
746754
void printTemperature(struct SBMFunctionDescriptionStruct *aSBMFunctionDescription, uint16_t aTemperature) {
747755
Serial.print((float) (aTemperature / 10.0) - 273.15);
748756
Serial.print(" C");
@@ -757,27 +765,30 @@ void printTime(struct SBMFunctionDescriptionStruct *aSBMFunctionDescription, uin
757765
if (aMinutes >= 0xFFFE) {
758766
Serial.print(F("Battery not being (dis)charged"));
759767
} else {
760-
uint16_t tHour;
761-
if (aMinutes >= 60) {
762-
tHour = aMinutes / 60;
768+
769+
// Hours
770+
uint16_t tHour = aMinutes / 60;
771+
if (tHour > 0) {
763772
Serial.print(tHour);
764773
Serial.print(" h ");
765774
if (aSBMFunctionDescription->DescriptionLCD != NULL && sCurrent != 0) {
766775
// clip LCD display at 99h59min
767776
if (aMinutes > ((100 * 60) - 1)) {
768777
tHour = 99;
769778
}
770-
LCDClearLine(1);
779+
LCDClearLine(2);
771780
myLCD.print(tHour);
772781
myLCD.print(" h ");
773782
}
774783
aMinutes = aMinutes % 60;
775784
}
785+
786+
// Minutes
776787
Serial.print(aMinutes);
777788
Serial.print(" min");
778789
if (aSBMFunctionDescription->DescriptionLCD != NULL && sCurrent != 0) {
779790
if (tHour == 0) {
780-
LCDClearLine(1);
791+
LCDClearLine(2);
781792
}
782793

783794
myLCD.print(aMinutes);
@@ -870,16 +881,16 @@ void printBatteryStatus(struct SBMFunctionDescriptionStruct *aSBMFunctionDescrip
870881
* Status Bits
871882
*/
872883
if (aStatus & INITIALIZED) {
873-
prettyPrintlnValueDescription(F("- Initialized"));
884+
prettyPrintlnValueDescription(F("80 Initialized"));
874885
}
875886
if (aStatus & DISCHARGING) {
876-
prettyPrintlnValueDescription(F("- Discharging"));
887+
prettyPrintlnValueDescription(F("40 Discharging"));
877888
}
878889
if (aStatus & FULLY_CHARGED) {
879-
prettyPrintlnValueDescription(F("- Fully Charged"));
890+
prettyPrintlnValueDescription(F("20 Fully Charged"));
880891
}
881892
if (aStatus & FULLY_DISCHARGED) {
882-
prettyPrintlnValueDescription(F("- Fully Discharged"));
893+
prettyPrintlnValueDescription(F("10 Fully Discharged"));
883894
}
884895
}
885896

@@ -963,6 +974,10 @@ void printSBMManufacturerInfo(void) {
963974
Serial.println(" V");
964975
Serial.println();
965976

977+
} else if (tType == 2072) {
978+
Serial.print(F("Controller IC identified by device type: "));
979+
Serial.println(F("bq8011/bq8015)"));
980+
966981
} else if (tType == 2084) {
967982
Serial.print(F("Controller IC identified by device type: "));
968983
Serial.println(F("bq2084"));

0 commit comments

Comments
 (0)