Skip to content

Commit 3d9fdde

Browse files
committed
Version 4.4.0 - Fixed ESR (Equivalent Series Resistor) computing bug; Fixed I2C global error handling; Improved optional info handling.
1 parent 3906e2c commit 3d9fdde

File tree

10 files changed

+358
-477
lines changed

10 files changed

+358
-477
lines changed

README.md

Lines changed: 50 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ Based on the unmaintained [PackProbe program](https://github.com/PowerCartel/Pac
2626

2727
<br/>
2828

29+
# Features
30+
- Incudes a combined volt / resistance meter, to determine the function of unknown SBM connections.
31+
- Starts with I2C address scanning.
32+
- Computing of pack ESR (Equivalent Series Resistor) if current changes from 0 to != 0 e.g. at start to charge or discharge.
33+
- Activating a discharge MOsFet, as long as voltages are above 3.3 volt per cell.
34+
- Reconnecting after I2C failure.
2935

3036
# Disclaimer
3137
**I do not know how to enter full access mode, clear permanent failure or unlock any controller IC.** Unfortunately according to most datasheets, you need an unlock key.
@@ -140,36 +146,37 @@ Sample outputs can be found in folder [extras](https://github.com/ArminJo/Smart-
140146

141147
```
142148
START ../src/SBMInfo.cpp
143-
Version 4.3 from Nov 23 2023
149+
Version 4.4 from Oct 4 2025
144150
Configured to set charge control pin 9 to low above 95 %
145-
Configured to stop discharge control pin 10 to low below 5 % or 3300 mV
146-
Found attached I2C device at 0xB
151+
Configured to set discharge control pin 10 to low below 5 % or 3300 mV
152+
Found attached I2C device at address 0xB
147153
148154
Battery mode 0x6081 | 0b110000010000001
149155
- Internal Charge Controller Supported
150156
- Battery OK
151157
- Disable AlarmWarning broadcast to Host and Smart Battery Charger
152158
- Disable broadcasts of ChargingVoltage and ChargingCurrent to Smart Battery Charger
153159
154-
Manufacturer Name DP-SDI51
155-
Chemistry LION
156-
Manufacturer Data 0x6 7D B B1 67 14 96 D 0 C8 0 A9 2A
157-
Device Name DAVOS
158-
Serial number 55982 | 0xDAAE
160+
Value1=3687, Value2=3667 - Non standard info is supported
161+
Manufacturer Name SANYO | 0x53 41 4E 59 4F
162+
Chemistry LION | 0x4C 49 4F 4E
163+
Manufacturer Data W'4=5 | 0x57 7 27 3 34 E 3D E 1F E 35 E
164+
Device Name M10B1 | 0x4D 31 30 42 31
165+
Serial number 11444 | 0x2CB4
159166
Manufacture date (YYYY-MM-DD) 2008-5-25
160-
Design voltage 10.800 V
161-
Design capacity 5100 mAh
162-
Charging current 3570 mA
163-
Charging voltage 12.600 V
167+
Design voltage 14.400 V | 4 cells
168+
Design capacity 4400 mAh
169+
Charging current 3080 mA
170+
Charging voltage 16.800 V
164171
SBM protocol (Version / Revision) 1.1 with optional PEC support / 1
165-
Cycle count 277
172+
Cycle count 331
166173
167174
Max error of charge calculation 100%
168175
Remaining time alarm 10 min
169176
Remaining capacity alarm 510 mAh
170177
171178
*** MANUFACTURER INFO ***
172-
Device Type 0 | 0x0
179+
Device Type 1794 | 0x702
173180
174181
*** RATE TEST INFO ***
175182
Setting AT rate to 100 mA
@@ -201,31 +208,39 @@ Pack config and status 0x8230 | 0b1000001000110000
201208
- Discharge is qualified for capacity learning
202209
203210
204-
*** DYNAMIC NON STANDARD INFO ***
211+
*** DYNAMIC NON STANDARD INFO / Cell Voltages + SOH ***
205212
Cell 1 Voltage 3.826 V
206213
Cell 2 Voltage 3.823 V
207214
Cell 3 Voltage 3.819 V
208215
Cell 4 Voltage 0x0
216+
State of Health 5911 | 0x1717
209217
210218
*** CHANGED VALUES ***
211219
212-
--- Next values are from another Pack! I connected 22 ohm Resistor -> 699 mA discharging
220+
--- After onnecting the 100 ohm discharge resistor, ESR is computed!
213221
214-
Voltage 15.788 V
215-
Average current of last minute -187 mA
216-
Average minutes remaining until empty: 15 h 24 min
217-
Cell 4 Voltage: 3.935 V
218-
Voltage 15.807 V
222+
Current -147 mA
223+
Voltage 14.653 V
224+
Average current of last minute -31 mA
225+
Minutes remaining until empty 1 min
226+
Average minutes remaining until empty 1 min
227+
Battery status (BIN) 0x3C0 | 0b1111000000
228+
- REMAINING_CAPACITY_ALARM
229+
- REMAINING_TIME_ALARM_FLAG
230+
80 Initialized
231+
40 Discharging
232+
233+
Cell 1 Voltage 3.666 V
234+
Cell 2 Voltage 3.646 V
235+
Cell 3 Voltage 3.674 V
236+
Cell 4 Voltage 3.666 V
237+
Voltage 14.650 V | ESR = 0.578 ohm
238+
Average current of last minute -78 mA
239+
Cell 2 Voltage 3.645 V
240+
Cell 3 Voltage 3.673 V
241+
Average current of last minute -104 mA
242+
Voltage 14.648 V
219243
Average current of last minute -120 mA
220-
Average minutes remaining until empty: 24 h 1 min
221-
Cell 4 Voltage: 3.949 V
222-
Average current of last minute -77 mA
223-
Average minutes remaining until empty: 37 h 25 min
224-
Voltage 15.714 V
225-
Current -699 mA
226-
Average current of last minute -178 mA
227-
Minutes remaining until empty 4 h 7 min
228-
Average minutes remaining until empty: 16 h 11 min
229244
```
230245

231246
<br/>
@@ -236,6 +251,11 @@ Average minutes remaining until empty: 16 h 11 min
236251
![Fritzing schematics](extras/SBMInfo_Schaltplan.png)
237252

238253
# Revision History
254+
### Version 4.4.0
255+
- Fixed ESR (Equivalent Series Resistor) computing bug.
256+
- Fixed I2C global error handling.
257+
- Improved optional info handling.
258+
239259
### Version 4.3.0
240260
- Fixed no voltage measurement bug.
241261
- Improved print and LCD display after I2C reconnection.

SBMInfo/ADCUtils.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
#if defined(__AVR__) && defined(ADCSRA) && defined(ADATE) && (!defined(__AVR_ATmega4809__))
3030
#define ADC_UTILS_ARE_AVAILABLE
3131

32+
// External Reference Current is 150 uA for 5 V and 100 uA for 3.5 V
33+
#define READING_FOR_AREF 1024L // Datasheet 24.2: The minimum value represents GND and the maximum value represents the voltage on the AREF pin minus 1 LSB
34+
3235
// PRESCALE4 => 13 * 4 = 52 microseconds per ADC conversion at 1 MHz Clock => 19,2 kHz
3336
#define ADC_PRESCALE2 1 // 26 microseconds per ADC conversion at 1 MHz
3437
#define ADC_PRESCALE4 2 // 52 microseconds per ADC conversion at 1 MHz

SBMInfo/ADCUtils.hpp

Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,15 @@
2727

2828
#include "ADCUtils.h"
2929
#if defined(ADC_UTILS_ARE_AVAILABLE) // set in ADCUtils.h, if supported architecture was detected
30+
#define ADC_UTILS_ARE_INCLUDED
3031

31-
#if !defined(STR_HELPER)
32+
#if !defined(STR)
3233
#define STR_HELPER(x) #x
3334
#define STR(x) STR_HELPER(x)
3435
#endif
36+
#if !defined(BITS_PER_BYTE)
37+
#define BITS_PER_BYTE 8
38+
#endif
3539

3640
/*
3741
* By replacing this value with the voltage you measured a the AREF pin after a conversion
@@ -58,11 +62,16 @@ union WordUnionForADCUtils {
5862
* Enable this to see information on each call.
5963
* Since there should be no library which uses Serial, it should only be enabled for development purposes.
6064
*/
61-
#if defined(DEBUG) && !defined(LOCAL_DEBUG)
65+
#if defined(DEBUG)
6266
#define LOCAL_DEBUG
6367
#else
6468
//#define LOCAL_DEBUG // This enables debug output only for this file
6569
#endif
70+
#if defined(INFO)
71+
#define LOCAL_INFO
72+
#else
73+
//#define LOCAL_INFO // This enables debug output only for this file
74+
#endif
6675

6776
/*
6877
* Persistent storage for VCC value
@@ -403,7 +412,7 @@ uint16_t readUntil4ConsecutiveValuesAreEqual(uint8_t aADCChannelNumber, uint8_t
403412
/*
404413
* Get min and max of the last 4 values
405414
*/
406-
tMin = 1024;
415+
tMin = READING_FOR_AREF;
407416
tMax = 0;
408417
for (uint_fast8_t i = 0; i < 4; ++i) {
409418
if (tValues[i] < tMin) {
@@ -472,7 +481,7 @@ uint16_t readUntil4ConsecutiveValuesAreEqual(uint8_t aADCChannelNumber, uint8_t
472481
float getVCCVoltageSimple(void) {
473482
// use AVCC with (optional) external capacitor at AREF pin as reference
474483
float tVCC = readADCChannelMultiSamplesWithReference(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT, 4);
475-
return ((1023 * 1.1 * 4) / tVCC);
484+
return ((READING_FOR_AREF * 1.1 * 4) / tVCC);
476485
}
477486

478487
/*
@@ -483,19 +492,19 @@ float getVCCVoltageSimple(void) {
483492
uint16_t getVCCVoltageMillivoltSimple(void) {
484493
// use AVCC with external capacitor at AREF pin as reference
485494
uint16_t tVCC = readADCChannelMultiSamplesWithReference(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT, 4);
486-
return ((1023L * ADC_INTERNAL_REFERENCE_MILLIVOLT * 4) / tVCC);
495+
return ((READING_FOR_AREF * ADC_INTERNAL_REFERENCE_MILLIVOLT * 4) / tVCC);
487496
}
488497

489498
/*
490499
* Gets the hypothetical 14 bit reading of VCC using 1.1 volt reference
491-
* Similar to getVCCVoltageMillivolt() * 1023 / 1100
500+
* Similar to getVCCVoltageMillivolt() * 1024 / 1100
492501
*/
493502
uint16_t getVCCVoltageReadingFor1_1VoltReference(void) {
494503
uint16_t tVCC = waitAndReadADCChannelWithReference(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT);
495504
/*
496505
* Do not switch back ADMUX to enable checkAndWaitForReferenceAndChannelToSwitch() to work correctly for the next measurement
497506
*/
498-
return ((1023L * 1023L) / tVCC);
507+
return ((READING_FOR_AREF * READING_FOR_AREF) / tVCC);
499508
}
500509

501510
/*
@@ -519,7 +528,7 @@ uint16_t getVCCVoltageMillivolt(void) {
519528
/*
520529
* Do not switch back ADMUX to enable checkAndWaitForReferenceAndChannelToSwitch() to work correctly for the next measurement
521530
*/
522-
return ((1023L * ADC_INTERNAL_REFERENCE_MILLIVOLT) / tVCC);
531+
return ((READING_FOR_AREF * ADC_INTERNAL_REFERENCE_MILLIVOLT) / tVCC);
523532
}
524533

525534
/*
@@ -547,7 +556,7 @@ void readAndPrintVCCVoltageMillivolt(Print *aSerial) {
547556
void readVCCVoltageSimple(void) {
548557
// use AVCC with (optional) external capacitor at AREF pin as reference
549558
float tVCC = readADCChannelMultiSamplesWithReference(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT, 4);
550-
sVCCVoltage = (1023 * (((float) ADC_INTERNAL_REFERENCE_MILLIVOLT) / 1000) * 4) / tVCC;
559+
sVCCVoltage = (READING_FOR_AREF * (((float) ADC_INTERNAL_REFERENCE_MILLIVOLT) / 1000) * 4) / tVCC;
551560
}
552561

553562
/*
@@ -558,7 +567,7 @@ void readVCCVoltageSimple(void) {
558567
void readVCCVoltageMillivoltSimple(void) {
559568
// use AVCC with external capacitor at AREF pin as reference
560569
uint16_t tVCCVoltageMillivoltRaw = readADCChannelMultiSamplesWithReference(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT, 4);
561-
sVCCVoltageMillivolt = (1023L * ADC_INTERNAL_REFERENCE_MILLIVOLT * 4) / tVCCVoltageMillivoltRaw;
570+
sVCCVoltageMillivolt = (READING_FOR_AREF * ADC_INTERNAL_REFERENCE_MILLIVOLT * 4) / tVCCVoltageMillivoltRaw;
562571
}
563572

564573
/*
@@ -579,7 +588,7 @@ void readVCCVoltageMillivolt(void) {
579588
/*
580589
* Do not switch back ADMUX to enable checkAndWaitForReferenceAndChannelToSwitch() to work correctly for the next measurement
581590
*/
582-
sVCCVoltageMillivolt = (1023L * ADC_INTERNAL_REFERENCE_MILLIVOLT) / tVCCVoltageMillivoltRaw;
591+
sVCCVoltageMillivolt = (READING_FOR_AREF * ADC_INTERNAL_REFERENCE_MILLIVOLT) / tVCCVoltageMillivoltRaw;
583592
}
584593

585594
/*
@@ -588,7 +597,7 @@ void readVCCVoltageMillivolt(void) {
588597
*/
589598
uint16_t getVoltageMillivolt(uint16_t aVCCVoltageMillivolt, uint8_t aADCChannelForVoltageMeasurement) {
590599
uint16_t tInputVoltageRaw = waitAndReadADCChannelWithReference(aADCChannelForVoltageMeasurement, DEFAULT);
591-
return (aVCCVoltageMillivolt * (uint32_t) tInputVoltageRaw) / 1023;
600+
return (aVCCVoltageMillivolt * (uint32_t) tInputVoltageRaw) / READING_FOR_AREF;
592601
}
593602

594603
/*
@@ -597,16 +606,18 @@ uint16_t getVoltageMillivolt(uint16_t aVCCVoltageMillivolt, uint8_t aADCChannelF
597606
*/
598607
uint16_t getVoltageMillivolt(uint8_t aADCChannelForVoltageMeasurement) {
599608
uint16_t tInputVoltageRaw = waitAndReadADCChannelWithReference(aADCChannelForVoltageMeasurement, DEFAULT);
600-
return (getVCCVoltageMillivolt() * (uint32_t) tInputVoltageRaw) / 1023;
609+
return (getVCCVoltageMillivolt() * (uint32_t) tInputVoltageRaw) / READING_FOR_AREF;
601610
}
602611

603612
uint16_t getVoltageMillivoltWith_1_1VoltReference(uint8_t aADCChannelForVoltageMeasurement) {
604613
uint16_t tInputVoltageRaw = waitAndReadADCChannelWithReference(aADCChannelForVoltageMeasurement, INTERNAL);
605-
return (ADC_INTERNAL_REFERENCE_MILLIVOLT * (uint32_t) tInputVoltageRaw) / 1023;
614+
return (ADC_INTERNAL_REFERENCE_MILLIVOLT * (uint32_t) tInputVoltageRaw) / READING_FOR_AREF;
606615
}
607616

608617
/*
609618
* Return true if sVCCVoltageMillivolt is > 4.3 V and < 4.95 V
619+
* This does not really work for the UNO board, because it has no series Diode in the USB VCC
620+
* and therefore a very low voltage drop.
610621
*/
611622
bool isVCCUSBPowered() {
612623
readVCCVoltageMillivolt();
@@ -634,13 +645,13 @@ bool isVCCUSBPowered(Print *aSerial) {
634645
}
635646

636647
/*
648+
* It checks every 10 seconds for 6 times, and then returns true if the undervoltage condition ( <3.4V ) still applies.
637649
* @ return true only once, when VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP (6) times voltage too low -> shutdown
638650
*/
639651
bool isVCCUndervoltageMultipleTimes() {
640652
/*
641653
* Check VCC every VCC_CHECK_PERIOD_MILLIS (10) seconds
642654
*/
643-
644655
if (millis() - sLastVCCCheckMillis >= VCC_CHECK_PERIOD_MILLIS) {
645656
sLastVCCCheckMillis = millis();
646657

@@ -650,30 +661,32 @@ bool isVCCUndervoltageMultipleTimes() {
650661
readVCCVoltageMillivolt();
651662
# endif
652663

653-
if (sVCCTooLowCounter < VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP) {
654-
/*
655-
* Do not check again if shutdown has happened
656-
*/
664+
/*
665+
* Do not check again if shutdown signaling (sVCCTooLowCounter >= 6) has happened
666+
*/
667+
if (sVCCTooLowCounter < VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP) { // VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP = 6
657668
if (sVCCVoltageMillivolt > VCC_UNDERVOLTAGE_THRESHOLD_MILLIVOLT) {
658669
sVCCTooLowCounter = 0; // reset counter
659670
} else {
660671
/*
661-
* Voltage too low, wait VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP (6) times and then shut down.
672+
* Voltage too low, wait VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP (6) times and then signal shut down.
662673
*/
663674
if (sVCCVoltageMillivolt < VCC_EMERGENCY_UNDERVOLTAGE_THRESHOLD_MILLIVOLT) {
664675
// emergency shutdown
665676
sVCCTooLowCounter = VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP;
666-
# if defined(INFO)
677+
# if defined(LOCAL_INFO)
667678
Serial.println(
668679
F(
669-
"Voltage < " STR(VCC_EMERGENCY_UNDERVOLTAGE_THRESHOLD_MILLIVOLT) " mV detected -> emergency shutdown"));
680+
"Undervoltage < " STR(VCC_EMERGENCY_UNDERVOLTAGE_THRESHOLD_MILLIVOLT) " mV detected -> emergency shutdown"));
670681
# endif
671682
} else {
672683
sVCCTooLowCounter++;
673-
# if defined(INFO)
674-
Serial.print(F("Voltage < " STR(VCC_UNDERVOLTAGE_THRESHOLD_MILLIVOLT) " mV detected: "));
684+
# if defined(LOCAL_INFO)
685+
Serial.print(sVCCVoltageMillivolt);
686+
Serial.print(F(" mV < " STR(VCC_UNDERVOLTAGE_THRESHOLD_MILLIVOLT) " mV undervoltage detected: "));
687+
675688
Serial.print(VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP - sVCCTooLowCounter);
676-
Serial.println(F(" tries left"));
689+
Serial.println(F(" attempts left"));
677690
# endif
678691
}
679692
if (sVCCTooLowCounter == VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP) {
@@ -815,4 +828,7 @@ float getVCCVoltage() {
815828
#if defined(LOCAL_DEBUG)
816829
#undef LOCAL_DEBUG
817830
#endif
831+
#if defined(LOCAL_INFO)
832+
#undef LOCAL_INFO
833+
#endif
818834
#endif // _ADC_UTILS_HPP

SBMInfo/LiquidCrystal.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ void LiquidCrystal::createChar(uint8_t location, uint8_t charmap[]) {
283283
for (int i=0; i<8; i++) {
284284
write(charmap[i]);
285285
}
286-
command(LCD_SETDDRAMADDR); // set cursor to 0.0, this avoids overwriting CGRAM by next write() command.
286+
// command(LCD_SETDDRAMADDR); // set cursor to 0.0, this avoids overwriting CGRAM by next write() command. Not contained anymore in the Arduino version
287287
}
288288

289289
/*********** mid level commands, for sending data/cmds */
@@ -294,7 +294,7 @@ inline void LiquidCrystal::command(uint8_t value) {
294294

295295
inline size_t LiquidCrystal::write(uint8_t value) {
296296
send(value, HIGH);
297-
return 1; // assume sucess
297+
return 1; // assume success
298298
}
299299

300300
/************ low level data pushing commands **********/

0 commit comments

Comments
 (0)