Commit bfe25c59 authored by Armin's avatar Armin
Browse files

initial version

parents
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
<storageModule moduleId="org.eclipse.cdt.core.settings">
<cconfiguration id="de.innot.avreclipse.configuration.app.release.1661013684">
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="de.innot.avreclipse.configuration.app.release.1661013684" moduleId="org.eclipse.cdt.core.settings" name="Release">
<externalSettings/>
<extensions>
<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.MakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
</extensions>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<configuration artifactName="${ProjName}" buildArtefactType="de.innot.avreclipse.buildArtefactType.app" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=de.innot.avreclipse.buildArtefactType.app,org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.release" description="" id="de.innot.avreclipse.configuration.app.release.1661013684" name="Release" parent="de.innot.avreclipse.configuration.app.release">
<folderInfo id="de.innot.avreclipse.configuration.app.release.1661013684." name="/" resourcePath="">
<toolChain id="de.innot.avreclipse.toolchain.winavr.app.release.337246503" name="AVR-GCC Toolchain" resourceTypeBasedDiscovery="false" superClass="de.innot.avreclipse.toolchain.winavr.app.release">
<option id="de.innot.avreclipse.toolchain.options.toolchain.objcopy.flash.app.release.1791940151" name="Generate HEX file for Flash memory" superClass="de.innot.avreclipse.toolchain.options.toolchain.objcopy.flash.app.release"/>
<option id="de.innot.avreclipse.toolchain.options.toolchain.objcopy.eeprom.app.release.1437207615" name="Generate HEX file for EEPROM" superClass="de.innot.avreclipse.toolchain.options.toolchain.objcopy.eeprom.app.release" value="false" valueType="boolean"/>
<option id="de.innot.avreclipse.toolchain.options.toolchain.objdump.app.release.357654205" name="Generate Extended Listing (Source + generated Assembler)" superClass="de.innot.avreclipse.toolchain.options.toolchain.objdump.app.release"/>
<option id="de.innot.avreclipse.toolchain.options.toolchain.size.app.release.1871197005" name="Print Size" superClass="de.innot.avreclipse.toolchain.options.toolchain.size.app.release"/>
<option id="de.innot.avreclipse.toolchain.options.toolchain.avrdude.app.release.1995671909" name="AVRDude" superClass="de.innot.avreclipse.toolchain.options.toolchain.avrdude.app.release"/>
<targetPlatform id="de.innot.avreclipse.targetplatform.winavr.app.release.1258308891" name="AVR Cross-Target" superClass="de.innot.avreclipse.targetplatform.winavr.app.release"/>
<builder buildPath="${workspace_loc:}/${ProjName}/Release" id="de.innot.avreclipse.target.builder.winavr.app.release.1604293556" keepEnvironmentInBuildfile="false" name="AVR GNU Make Builder" superClass="de.innot.avreclipse.target.builder.winavr.app.release"/>
<tool id="de.innot.avreclipse.tool.assembler.winavr.app.release.46340294" name="AVR Assembler" superClass="de.innot.avreclipse.tool.assembler.winavr.app.release">
<option id="de.innot.avreclipse.assembler.option.debug.level.1333531448" name="Generate Debugging Info" superClass="de.innot.avreclipse.assembler.option.debug.level" value="de.innot.avreclipse.assembler.option.debug.level.none" valueType="enumerated"/>
<inputType id="de.innot.avreclipse.tool.assembler.input.1801625326" superClass="de.innot.avreclipse.tool.assembler.input"/>
</tool>
<tool id="de.innot.avreclipse.tool.compiler.winavr.app.release.1372071518" name="AVR Compiler" superClass="de.innot.avreclipse.tool.compiler.winavr.app.release">
<option id="de.innot.avreclipse.compiler.option.debug.level.1640693399" name="Generate Debugging Info" superClass="de.innot.avreclipse.compiler.option.debug.level" value="de.innot.avreclipse.compiler.option.debug.level.g3" valueType="enumerated"/>
<option id="de.innot.avreclipse.compiler.option.optimize.33375696" name="Optimization Level" superClass="de.innot.avreclipse.compiler.option.optimize" value="de.innot.avreclipse.compiler.optimize.size" valueType="enumerated"/>
<option id="de.innot.avreclipse.compiler.option.incpath.1717064685" name="Include Paths (-I)" superClass="de.innot.avreclipse.compiler.option.incpath" valueType="includePath">
<listOptionValue builtIn="false" value="E:/Elektro/arduino/hardware/arduino/avr/variants/standard"/>
<listOptionValue builtIn="false" value="E:/Elektro/arduino/hardware/arduino/avr/cores/arduino"/>
</option>
<option id="de.innot.avreclipse.compiler.option.def.528223329" name="Define Syms (-D)" superClass="de.innot.avreclipse.compiler.option.def"/>
<inputType id="de.innot.avreclipse.compiler.winavr.input.2038006680" name="C Source Files" superClass="de.innot.avreclipse.compiler.winavr.input"/>
</tool>
<tool id="de.innot.avreclipse.tool.cppcompiler.app.release.1845316012" name="AVR C++ Compiler" superClass="de.innot.avreclipse.tool.cppcompiler.app.release">
<option id="de.innot.avreclipse.cppcompiler.option.debug.level.1171274398" name="Generate Debugging Info" superClass="de.innot.avreclipse.cppcompiler.option.debug.level" value="de.innot.avreclipse.cppcompiler.option.debug.level.g3" valueType="enumerated"/>
<option id="de.innot.avreclipse.cppcompiler.option.optimize.39578510" name="Optimization Level" superClass="de.innot.avreclipse.cppcompiler.option.optimize" value="de.innot.avreclipse.cppcompiler.optimize.size" valueType="enumerated"/>
<option id="de.innot.avreclipse.cppcompiler.option.incpath.752692776" name="Include Paths (-I)" superClass="de.innot.avreclipse.cppcompiler.option.incpath" valueType="includePath">
<listOptionValue builtIn="false" value="E:/Elektro/arduino/hardware/arduino/avr/variants/standard"/>
<listOptionValue builtIn="false" value="E:/Elektro/arduino/hardware/arduino/avr/cores/arduino"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/lib/SoftI2CMaster}&quot;"/>
</option>
<option id="de.innot.avreclipse.cppcompiler.option.def.1867196469" name="Define Syms (-D)" superClass="de.innot.avreclipse.cppcompiler.option.def"/>
<option id="de.innot.avreclipse.cppcompiler.option.optimize.other.247153119" name="Other Optimization Flags" superClass="de.innot.avreclipse.cppcompiler.option.optimize.other" value="-ffunction-sections -fdata-sections" valueType="string"/>
<option id="de.innot.avreclipse.cppcompiler.option.optimize.packstruct.477407436" name="Pack structs (-fpack-struct)" superClass="de.innot.avreclipse.cppcompiler.option.optimize.packstruct" value="false" valueType="boolean"/>
<option id="de.innot.avreclipse.cppcompiler.option.optimize.shortenums.1764657050" name="Short enums (-fshort-enums)" superClass="de.innot.avreclipse.cppcompiler.option.optimize.shortenums" value="false" valueType="boolean"/>
<inputType id="de.innot.avreclipse.cppcompiler.input.1320840307" superClass="de.innot.avreclipse.cppcompiler.input"/>
</tool>
<tool id="de.innot.avreclipse.tool.linker.winavr.app.release.1098932434" name="AVR C Linker" superClass="de.innot.avreclipse.tool.linker.winavr.app.release"/>
<tool id="de.innot.avreclipse.tool.cpplinker.app.release.698797395" name="AVR C++ Linker" superClass="de.innot.avreclipse.tool.cpplinker.app.release">
<option id="de.innot.avreclipse.cpplinker.option.libs.2014114297" name="Libraries (-l)" superClass="de.innot.avreclipse.cpplinker.option.libs" valueType="libs">
<listOptionValue builtIn="false" value="UnoCore"/>
</option>
<option id="de.innot.avreclipse.cpplinker.option.libpath.462019446" name="Libraries Path (-L)" superClass="de.innot.avreclipse.cpplinker.option.libpath" valueType="stringList">
<listOptionValue builtIn="false" value="&quot;${workspace_loc}/corelib&quot;"/>
</option>
<option id="de.innot.avreclipse.cpplinker.option.otherlinkargs.1717523034" name="Other Arguments" superClass="de.innot.avreclipse.cpplinker.option.otherlinkargs" value="-Wl,--gc-sections -mrelax" valueType="string"/>
<inputType id="de.innot.avreclipse.tool.cpplinker.input.429552073" name="OBJ Files" superClass="de.innot.avreclipse.tool.cpplinker.input">
<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
<additionalInput kind="additionalinput" paths="$(LIBS)"/>
</inputType>
</tool>
<tool id="de.innot.avreclipse.tool.archiver.winavr.base.1449436773" name="AVR Archiver" superClass="de.innot.avreclipse.tool.archiver.winavr.base"/>
<tool command="avr-objdump" id="de.innot.avreclipse.tool.objdump.winavr.app.release.1610106695" name="AVR Create Extended Listing" superClass="de.innot.avreclipse.tool.objdump.winavr.app.release"/>
<tool command="avr-objcopy" id="de.innot.avreclipse.tool.objcopy.flash.winavr.app.release.852459936" name="AVR Create Flash image" superClass="de.innot.avreclipse.tool.objcopy.flash.winavr.app.release"/>
<tool id="de.innot.avreclipse.tool.objcopy.eeprom.winavr.app.release.874539195" name="AVR Create EEPROM image" superClass="de.innot.avreclipse.tool.objcopy.eeprom.winavr.app.release"/>
<tool command="avr-size" id="de.innot.avreclipse.tool.size.winavr.app.release.1306176526" name="Print Size" superClass="de.innot.avreclipse.tool.size.winavr.app.release"/>
<tool id="de.innot.avreclipse.tool.avrdude.app.release.440173151" name="AVRDude" superClass="de.innot.avreclipse.tool.avrdude.app.release"/>
</toolChain>
</folderInfo>
</configuration>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
</cconfiguration>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
<storageModule moduleId="refreshScope"/>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<project id="${ProjName}.null.1261896103" name="AVR Cross Target Application"/>
</storageModule>
<storageModule moduleId="scannerConfiguration">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
<scannerConfigBuildInfo instanceId="de.innot.avreclipse.configuration.app.release.1661013684">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="de.innot.avreclipse.core.AVRGCCManagedMakePerProjectProfileC"/>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="de.innot.avreclipse.configuration.app.release.1661013684;de.innot.avreclipse.configuration.app.release.1661013684.;de.innot.avreclipse.tool.compiler.winavr.app.release.1372071518;de.innot.avreclipse.compiler.winavr.input.2038006680">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="de.innot.avreclipse.core.AVRGCCManagedMakePerProjectProfileC"/>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="de.innot.avreclipse.configuration.app.release.1661013684;de.innot.avreclipse.configuration.app.release.1661013684.;de.innot.avreclipse.tool.cppcompiler.app.release.1845316012;de.innot.avreclipse.cppcompiler.input.1320840307">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="de.innot.avreclipse.core.AVRGCCManagedMakePerProjectProfileCPP"/>
</scannerConfigBuildInfo>
</storageModule>
</cproject>
/Release/
/.settings/
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>SBMInfo</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
<triggers>clean,full,incremental,</triggers>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
<triggers>full,incremental,</triggers>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.cdt.core.cnature</nature>
<nature>org.eclipse.cdt.core.ccnature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
<nature>de.innot.avreclipse.core.avrnature</nature>
</natures>
<linkedResources>
<link>
<name>src/lib/SoftI2CMaster</name>
<type>2</type>
<location>E:/WORKSPACE_ARDUINO/lib/SoftI2CMaster</location>
</link>
</linkedResources>
</projectDescription>
# SMB - Smart Battery Module (Laptop Battery Pack) Info
Gibt die Daten des SMB Controllers aus.
Basiert auf https://github.com/PowerCartel/PackProbe von Power Cartel http://powercartel.com/projects/packprobe/. Hier gibt es auch weitere wertvolle Informationen.
Bentigt SoftI2CMaster Library fr I2C / SMBus / https://github.com/felias-fogg/SoftI2CMaster/archive/master.zip
## Finden der Anschlsse.
Nach dem Booten sucht das Programm nach einem angeschlossenen I2C Device.
Man kann also alle mglichen Pinkombinationen von Clock und Data am Battery Pack ausprobieren.
Bei der Richtigen hrt das Blinken der Led auf und es kommt sofort die Ausgabe "Found I2C device attached at address: ox0B" und direkt danach werden die Daten ausgegeben.
Bei den Laptop Battery Packs war Plus und Masse immer auen.
Wenn mehr als 5 Kontakte vorhanden waren, waren sie wie folgt belegt:
- Masse und Plus doppelt. z.B. + | + | Thermo | Data | Clock | - | -
- Ein Enable (nur im Laptop mit Masse verbunden) und eine Signal Anschluss (nur im Battery Pack mit Masse verbunden). z.B. + | + | Clock | Data | Signal | Enable | Thermo | - | -
Die Clock und Data Eingnge waren bei meinen Packs die Anschlsse mit einem Widerstand von ca. 300 k bis 1 MOhm nach Masse.
Der Thermo Sensor Anschluss war uneinheitlich, mal nicht messabar beschaltet, mal 1 MOhm, mal 1,6 Volt, mal 10 kOhm nach Masse.
Zur Verbindung mit den Kontakten habe ich normales 1,5 qmm Kupferkabel aus der Hausinstallation genommen, dessen eines Ende ich mit einem Hammer etwas plattgeklopft hab. Stecknadeln oder Breadboard Wires gehen auch.
Die Daten werden nur einmalig nach dem Reset ausgegeben, nur die vernderlichen Werte wie Spannung / Strom / Temperatur / Ladung werden alle 3 Sekunden auf Vernderungen gecheckt.
Tested with bq20z70, bq20z451, bq2084, bq80201DBT, bq40z50.
Einen Schaltplan zu den Batterie Modulen gibt es im Datenblatt zum TI bq29311 auf Seite 9.
![Breadboard](https://github.com/ArminJo/Smart-Battery-Module-Info_For_Arduino/blob/master/img/Breadboard.jpg)
###Beispieloutput:
I2C initalized sucessfully
Found attached I2C device at 0xB
*** STATIC INFO ***
```
Chemistry: LION
Manufacturer Name: DP
Manufacturer Data:   K24005SDI 
Device Name: bq20z451
Serial Number: 11518
Manufacture Date (YYYY-MM-DD): 2009-8-27
Design Capacity (mAh): 5450
Design Voltage: 10.950
Specification Info: 49
Cycle Count: 255
Max Error of charge calculation (%): 1
RemainingTimeAlarm (min): 10
Remaining Capacity Alarm: 300
Battery Mode (BIN): 0b110000000000001
- Internal Charge Controller Supported
- Battery OK
- Disable AlarmWarning broadcast to Host and Smart Battery Charger
- Disable broadcasts of ChargingVoltage and ChargingCurrent to Smart Battery Charger
*** MANUFACTURER INFO ***
ManufacturerAccess: 304 / 0x130
Device Type: 1105 / 0x451
Hardware Version: 0xA6
Firmware Version: 0.3
Manufacturer Status (BIN): 0b10001111
- FET Status 2
- State: 0b1111
Battery Pack removed
*** TEST INFO ***
Average Current (mA): 1
AtRateTimeToFull: 65535
AtRateTimeToEmpty: 65535
AtRateOK: 1
*** DYNAMIC INFO ***
Full Charge Capacity (mAh): 2845
Remaining Capacity (mAh): 2720
Relative Charge(%): 96
Absolute Charge(%): 50
Minutes remaining for full charge: 7500
Battery Status (BIN): 0b100100011100000
- TERMINATE_CHARGE_ALARM
- TERMINATE_DISCHARGE_ALARM
- Initialized
- Discharging
- Fully Charged
Voltage: 12.253
Current (mA): 0
Temperature (C): 24.85
State of Health: 4084
Cell 1 Voltage: 4.084
Cell 2 Voltage: 4.085
Cell 3 Voltage: 4.084
*** CHANGED VALUES ***
```
/*
* SBMInfo.cpp
* Shows Smart Battery Info
*
* Copyright (C) 2016 Armin Joachimsmeyer
* armin.joachimsmeyer@gmail.com
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/gpl.html>.
*
*/
#include <Arduino.h>
#include "SBMInfo.h"
/*
* Corresponds to A4/A5 - the hardware I2C pins on Arduinos
*/
#define SDA_PORT PORTC
#define SDA_PIN 4
#define SCL_PORT PORTC
#define SCL_PIN 5
#define I2C_SLOWMODE 1
// Otherwise it may give read errors because of the arduino 1 ms clock interrupt.
#define I2C_NOINTERRUPT 1
#include <SoftI2CMaster.h>
#define DATA_BUFFER_LENGTH 32
uint8_t sI2CDataBuffer[DATA_BUFFER_LENGTH];
uint8_t sI2CDeviceAddress;
void printBatteryMode(struct SBMFunctionDescriptionStruct * aDescription, uint16_t aMode);
void printBatteryStatus(struct SBMFunctionDescriptionStruct * aDescription, uint16_t aStatus);
void printManufacturerDate(struct SBMFunctionDescriptionStruct * aDescription, uint16_t aDate);
void printVoltage(struct SBMFunctionDescriptionStruct * aDescription, uint16_t aVoltage);
void printCurrent(struct SBMFunctionDescriptionStruct * aDescription, uint16_t aCurrent);
void printTemperature(struct SBMFunctionDescriptionStruct * aDescription, uint16_t aTemperature);
void printSMBStaticInfo(void);
void printSMBDynamicInfo(void);
void printSMBNonStandardInfo(void);
void printSMBTestInfo(void);
bool checkForAttachedI2CDevice(uint8_t aI2CDeviceAddress);
int scanForAttachedI2CDevice(void);
void BlinkLedForever(int aBinkDelay);
void TogglePin(uint8_t aPinNr);
int readWord(uint8_t aFunction);
// Pin 13 has an LED connected on most Arduino boards.
const int LED_PIN = 13;
/*
* Command definitions
*/
struct SBMFunctionDescriptionStruct sSBMStaticFunctionDescriptionArray[] = { {
SERIAL_NUM, "Serial Number: " }, {
MFG_DATE, "Manufacture Date (YYYY-MM-DD):", 0xFFFF, &printManufacturerDate }, {
DESIGN_CAPACITY, "Design Capacity (mAh): " }, {
DESIGN_VOLTAGE, "Design Voltage: ", 0xFFFF, &printVoltage }, {
CHARGING_CURRENT, "Charging Current (mA): " }, {
CHARGING_VOLTAGE, "Charging Voltage: ", 0, &printVoltage }, {
SPEC_INFO, "Specification Info: " }, {
CYCLE_COUNT, "Cycle Count: " }, {
MAX_ERROR, "Max Error of charge calculation (%): " }, {
REMAINING_TIME_ALARM, "RemainingTimeAlarm (min): " }, {
REMAINING_CAPACITY_ALARM, "Remaining Capacity Alarm: " }, {
BATTERY_MODE, "Battery Mode (BIN): 0b", 0xFFFF, &printBatteryMode } };
struct SBMFunctionDescriptionStruct sSBMDynamicFunctionDescriptionArray[] = { {
FULL_CHARGE_CAPACITY, "Full Charge Capacity (mAh): " }, {
REMAINING_CAPACITY, "Remaining Capacity (mAh): " }, {
RELATIVE_SOC, "Relative Charge(%): " }, {
ABSOLUTE_SOC, "Absolute Charge(%): " }, {
RUN_TIME_TO_EMPTY, "Minutes remaining until empty: ", 0xFFFF }, {
AVERAGE_TIME_TO_EMPTY, "Average minutes remaining until empty: ", 0xFFFF }, {
TIME_TO_FULL, "Minutes remaining for full charge: ", 0xFFFF }, {
BATTERY_STATUS, "Battery Status (BIN): 0b", 0xFFFF, &printBatteryStatus }, {
VOLTAGE, "Voltage: ", 0xFFFF, &printVoltage }, {
CURRENT, "Current (mA): ", 0xFFFF, &printCurrent }, {
TEMPERATURE, "Temperature (C): ", 0xFFFF, &printTemperature } };
/*
* These aren't part of the standard, but work with some packs.
*/
struct SBMFunctionDescriptionStruct sSBMNonStandardFunctionDescriptionArray[] = { {
STATE_OF_HEALTH, "State of Health: " }, {
CELL1_VOLTAGE, "Cell 1 Voltage: ", 0, &printVoltage }, {
CELL2_VOLTAGE, "Cell 2 Voltage: ", 0, &printVoltage }, {
CELL3_VOLTAGE, "Cell 3 Voltage: ", 0, &printVoltage }, {
CELL4_VOLTAGE, "Cell 4 Voltage: ", 0, &printVoltage } };
struct SBMFunctionDescriptionStruct sSBMTestFunctionDescriptionArray[] = { {
AverageCurrent, "Average Current (mA): " }, {
AtRate, "AtRate: " }, {
AtRateTimeToFull, "AtRateTimeToFull: " }, {
AtRateTimeToEmpty, "AtRateTimeToEmpty: " }, {
AtRateOK, "AtRateOK: " }, };
void setup() {
// initialize the digital pin as an output.
pinMode(LED_PIN, OUTPUT);
// Shutdown SPI and TWI, timers, and ADC
PRR = (1 << PRSPI) | (1 << PRTWI) | (1 << PRTIM1) | (1 << PRTIM2) | (1 << PRADC);
// Disable digital input on all unused ADC channel pins to reduce power consumption
DIDR0 = ADC0D | ADC1D | ADC2D | ADC3D;
Serial.begin(115200);
while (!Serial) {
; // wait for Leonardo enumeration, others continue immediately
}
bool tI2CSucessfullyInitialized = i2c_init();
if (tI2CSucessfullyInitialized) {
Serial.println(F("I2C initalized sucessfully"));
} else {
Serial.println(F("I2C pullups missing"));
BlinkLedForever(100);
}
Serial.flush();
/*
* Check for I2C device and blink until device attached
*/
if (!checkForAttachedI2CDevice(SBM_DEVICE_ADDRESS)) {
int tDeviceAttached;
do {
tDeviceAttached = scanForAttachedI2CDevice();
delay(500);
TogglePin(LED_PIN);
} while (tDeviceAttached < 0);
}
uint16_t tVoltage;
do {
tVoltage = readWord(VOLTAGE);
delay(500);
TogglePin(LED_PIN);
} while (tVoltage == 0xFFFF);
Serial.println(F("\r\n*** STATIC INFO ***"));
Serial.flush();
printSMBStaticInfo();
Serial.println(F("\r\n*** TEST INFO ***"));
Serial.flush();
printSMBTestInfo();
Serial.println(F("\r\n*** DYNAMIC INFO ***"));
Serial.flush();
printSMBDynamicInfo();
printSMBNonStandardInfo();
Serial.println(F("\r\n*** CHANGED VALUES ***"));
Serial.flush();
}
void loop() {
printSMBDynamicInfo();
printSMBNonStandardInfo();
delay(3000);
}
void TogglePin(uint8_t aPinNr) {
if (digitalRead(aPinNr) == HIGH) {
digitalWrite(aPinNr, LOW);
} else {
digitalWrite(aPinNr, HIGH);
}
}
void BlinkLedForever(int aBinkDelay) {
do {
digitalWrite(LED_PIN, HIGH);
delay(aBinkDelay);
digitalWrite(LED_PIN, LOW);
delay(aBinkDelay);
} while (true);
}
bool checkForAttachedI2CDevice(uint8_t aStandardDeviceAddress) {
bool tOK = i2c_start(aStandardDeviceAddress << 1 | I2C_WRITE);
i2c_stop();
if (tOK) {
Serial.print(F("Found attached I2C device at 0x"));
Serial.println(aStandardDeviceAddress, HEX);
sI2CDeviceAddress = SBM_DEVICE_ADDRESS;
return true;
} else {
return false;
}
}
int sScanCount = 0;
int scanForAttachedI2CDevice(void) {
int tFoundAdress = -1;
for (uint8_t i = 0; i < 127; i++) {
bool ack = i2c_start(i << 1 | I2C_WRITE);
if (ack) {
Serial.print(F("Found I2C device attached at address: 0x"));
Serial.println(i, HEX);
tFoundAdress = i;
}
i2c_stop();
}
if (tFoundAdress < 0) {
Serial.print(F("Found no attached I2C device - "));
Serial.println(sScanCount++);
} else {
sI2CDeviceAddress = tFoundAdress;
}
return tFoundAdress;
}
int readWord(uint8_t aFunction) {
i2c_start((sI2CDeviceAddress << 1) | I2C_WRITE);
i2c_write(aFunction);
i2c_rep_start((sI2CDeviceAddress << 1) | I2C_READ);
uint8_t tLSB = i2c_read(false);
uint8_t tMSB = i2c_read(true);
i2c_stop();
return (int) tLSB | (((int) tMSB) << 8);
}
int readWordFromManufacturerAccess(uint16_t aCommand) {
i2c_start((sI2CDeviceAddress << 1) | I2C_WRITE);
i2c_write(MANUFACTURER_ACCESS);
// Write manufacturer command word
i2c_rep_start((sI2CDeviceAddress << 1) | I2C_WRITE);
i2c_write(aCommand);
i2c_write(aCommand >> 8);
i2c_stop();
// Read manufacturer result word
i2c_start((sI2CDeviceAddress << 1) | I2C_WRITE);
i2c_write(MANUFACTURER_ACCESS);
i2c_rep_start((sI2CDeviceAddress << 1) | I2C_READ);
uint8_t tLSB = i2c_read(false);
uint8_t tMSB = i2c_read(true);
i2c_stop();
return (int) tLSB | (((int) tMSB) << 8);
}
uint8_t readBlock(uint8_t aCommand, uint8_t* aDataBufferPtr, uint8_t aDataBufferLength) {
i2c_start((sI2CDeviceAddress << 1) + I2C_WRITE);
i2c_write(aCommand);
i2c_rep_start((sI2CDeviceAddress << 1) + I2C_READ);
// First read length of data
uint8_t tLengthOfData = i2c_read(false);
if (tLengthOfData > aDataBufferLength) {
tLengthOfData = aDataBufferLength;
}
// then read data
uint8_t tIndex;
for (tIndex = 0; tIndex < tLengthOfData - 1; tIndex++) {
aDataBufferPtr[tIndex] = i2c_read(false);
}
// Read last byte with "true"
aDataBufferPtr[tIndex++] = i2c_read(true);
i2c_stop();
return tLengthOfData;
}
/*
* Read word and print if value has changed.
* To avoid spurious outputs check changed values 2 times.
*/
void readWordAndPrint(struct SBMFunctionDescriptionStruct *aSBMFunctionDescription) {
uint16_t tActualValue = readWord(aSBMFunctionDescription->FunctionCode);
if (tActualValue != aSBMFunctionDescription->lastValue) {
// check value again, maybe it was a transmit error
delay(33); // just guessed the value
uint16_t tActualValue2 = readWord(aSBMFunctionDescription->FunctionCode);
if (tActualValue2 != aSBMFunctionDescription->lastValue) {
delay(17); // just guessed the value
uint16_t tActualValue3 = readWord(aSBMFunctionDescription->FunctionCode);
if (tActualValue3 != aSBMFunctionDescription->lastValue) {
if (aSBMFunctionDescription->ValueFormatter == NULL) {
Serial.print(aSBMFunctionDescription->Description);
Serial.println(tActualValue);
aSBMFunctionDescription->lastValue = tActualValue;
} else {
aSBMFunctionDescription->ValueFormatter(aSBMFunctionDescription, tActualValue);
}
Serial.flush();
}
}
}
}
void printBatteryMode(struct SBMFunctionDescriptionStruct * aDescription, uint16_t aMode) {
Serial.print(aDescription->Description);
aDescription->lastValue = aMode;
Serial.println(aMode, BIN);
if (aMode & INTERNAL_CHARGE_CONTROLLER) {
Serial.println(F("- Internal Charge Controller Supported"));
}
if (aMode & CONDITION_FLAG) {
Serial.println(F("- Conditioning Cycle Requested"));
} else {
Serial.println(F("- Battery OK"));
}
if (aMode & CHARGE_CONTROLLER) {
Serial.println(F("- Charge Controller Enabled"));
}
if (aMode & ALARM_MODE) {
// means the battery will not be I2C master and send alarms
Serial.println(F("- Disable AlarmWarning broadcast to Host and Smart Battery Charger"));
}
if (aMode & CHARGER_MODE) {
// means the battery will not be I2C master and not send charging info (to the charger)
Serial.println(F("- Disable broadcasts of ChargingVoltage and ChargingCurrent to Smart Battery Charger"));
}
if (aMode & CAPACITY_MODE) {
Serial.println(F("- Report in 10mW or 10mWh"));
}
}
/*
* Format as ISO date
*/
void printManufacturerDate(struct SBMFunctionDescriptionStruct * aDescription, uint16_t aDate) {
Serial.print(aDescription->Description);
aDescription->lastValue = aDate;
int tDay = aDate & 0x1F;
int tMonth = (aDate >> 5) & 0x0F;
int tYear = 1980 + ((aDate >> 9) & 0x7F);
String tDateAsString = " ";
tDateAsString += tYear;
tDateAsString += "-";
tDateAsString += tMonth;
tDateAsString += "-";
tDateAsString += tDay;
Serial.println(tDateAsString);
}
void printBatteryStatus(struct SBMFunctionDescriptionStruct * aDescription, uint16_t aStatus) {
Serial.print(aDescription->Description);
aDescription->lastValue = aStatus;
Serial.println(aStatus, BIN);
/*
* Error Bits
*/
if (aStatus & OVER_CHARGED_ALARM) {
Serial.println(F("- OVER_CHARGED_ALARM"));
}
if (aStatus & TERMINATE_CHARGE_ALARM) {
Serial.println(F("- TERMINATE_CHARGE_ALARM"));
}
if (aStatus & OVER_TEMP_ALARM) {
Serial.println(F("- OVER_TEMP_ALARM"));
}
if (aStatus & TERMINATE_DISCHARGE_ALARM) {
Serial.println(F("- TERMINATE_DISCHARGE_ALARM"));
}
if (aStatus & REMAINING_CAPACITY_ALARM) {
Serial.println(F("- REMAINING_CAPACITY_ALARM"));
}
if (aStatus & REMAINING_TIME_ALARM_FLAG) {
Serial.println(F("- REMAINING_TIME_ALARM_FLAG"));
}
/*
* Status Bits
*/
if (aStatus & INITIALIZED) {
Serial.println(F("- Initialized"));
}
if (aStatus & DISCHARGING) {
Serial.println(F("- Discharging"));
}
if (aStatus & FULLY_CHARGED) {
Serial.println(F("- Fully Charged"));
}
if (aStatus & FULLY_DISCHARGED) {
Serial.println(F("- Fully Discharged"));
}
}
/*
* Print only if changed by two ore more
*/
void printVoltage(struct SBMFunctionDescriptionStruct * aDescription, uint16_t aVoltage) {
if (aVoltage < aDescription->lastValue - 1 || aDescription->lastValue + 1 < aVoltage) {
Serial.print(aDescription->Description);
Serial.println((float) aVoltage / 1000, 3);
aDescription->lastValue = aVoltage;
}
}
/*
* Print only if changed by two ore more
*/
void printCurrent(struct SBMFunctionDescriptionStruct * aDescription, uint16_t aCurrent) {
if (aCurrent < aDescription->lastValue - 1 || aDescription->lastValue + 1 < aCurrent) {
Serial.print(aDescription->Description);
aDescription->lastValue = aCurrent;
Serial.println((int) aCurrent);
}
}
/*
* Print only if changed by more than 0.1 C
*/
void printTemperature(struct SBMFunctionDescriptionStruct * aDescription, uint16_t aTemperature) {
if (aTemperature < aDescription->lastValue - 100 || aDescription->lastValue + 100 < aTemperature) {
Serial.print(aDescription->Description);
aDescription->lastValue = aTemperature;
Serial.println((float) (aTemperature / 10.0) - 273.15);
}
}
void printSMBStaticInfo(void) {
uint8_t tReceivedLength = 0;
Serial.print(F("Chemistry: "));
tReceivedLength = readBlock(CELL_CHEM, sI2CDataBuffer, DATA_BUFFER_LENGTH);
Serial.write(sI2CDataBuffer, tReceivedLength);
Serial.println("");
Serial.print(F("Manufacturer Name: "));
tReceivedLength = readBlock(MFG_NAME, sI2CDataBuffer, DATA_BUFFER_LENGTH);
Serial.write(sI2CDataBuffer, tReceivedLength);
Serial.println("");
Serial.print(F("Manufacturer Data: "));
tReceivedLength = readBlock(MFG_DATA, sI2CDataBuffer, DATA_BUFFER_LENGTH);
Serial.write(sI2CDataBuffer, tReceivedLength);
Serial.println("");
Serial.print(F("Device Name: "));
tReceivedLength = readBlock(DEV_NAME, sI2CDataBuffer, DATA_BUFFER_LENGTH);
Serial.write(sI2CDataBuffer, tReceivedLength);
Serial.println("");
for (uint8_t i = 0; i < (sizeof(sSBMStaticFunctionDescriptionArray) / sizeof(SBMFunctionDescriptionStruct)); ++i) {
readWordAndPrint(&sSBMStaticFunctionDescriptionArray[i]);
}
Serial.println(F("\r\n*** MANUFACTURER INFO ***"));
Serial.print(F("ManufacturerAccess: "));
uint16_t tManufacurerAccess = readWord(MANUFACTURER_ACCESS);
Serial.print(tManufacurerAccess);
Serial.print(F(" / 0x"));
Serial.println(tManufacurerAccess, HEX);
uint16_t tType = readWordFromManufacturerAccess(BQ20Z70_Device_Type);
uint16_t tVersion = readWordFromManufacturerAccess(BQ20Z70_Firmware_Version);
if (tType != tVersion) {
Serial.print(F("Device Type: "));
Serial.print(tType);
Serial.print(F(" / 0x"));
Serial.println(tType, HEX);
if (tType == 2084) {
Serial.print(F("Controller IC identified by device type: "));
Serial.print(F("bq2084"));
Serial.print(F("End of Discharge Voltage Level: "));
uint16_t tLevel = readWordFromManufacturerAccess(BQ2084_EDV_level);
Serial.println(((float) tLevel) / 1000, 3);
} else {
if (tType == 0x700) {
Serial.print(F("Controller IC identified by device type: "));
Serial.println(F("bq20z70"));
}
Serial.print(F("Hardware Version: 0x"));
Serial.println(readWordFromManufacturerAccess(BQ20Z70_Hardware_Version), HEX);
}
Serial.print(F("Firmware Version: "));
Serial.print((uint8_t) (tVersion >> 8), HEX);
Serial.print(".");
Serial.println((uint8_t) tVersion, HEX);
/*
* Status
*/
Serial.print(F("Manufacturer Status (BIN): 0b"));
uint8_t tStatus = readWordFromManufacturerAccess(BQ20Z70_Manufacturer_Status) >> 8;
Serial.println(tStatus, BIN);
Serial.print(F("- FET Status "));
Serial.println(tStatus >> 6);
Serial.print(F("- State: 0b"));
tStatus = tStatus & 0x0F;
Serial.println(tStatus, BIN);
if (tStatus == 0x01) {
Serial.println(F("Normal Discharge"));
} else if (tStatus == 0x05) {
Serial.println(F("Charge"));
} else if (tStatus == 0x07) {
Serial.println(F("Charge Termination"));
} else if (tStatus == 0x0C) {
Serial.println(F("Battery Failure"));
} else if (tStatus == 0x09) {
Serial.println(F("Permanent Failure"));
} else if (tStatus == 0x0F) {
Serial.println(F("Battery Pack removed"));
}
} else if (tManufacurerAccess != tType) {
Serial.print(F("Info: "));
Serial.println(tType, HEX);
}
}
void printSMBDynamicInfo(void) {
for (uint8_t i = 0; i < (sizeof(sSBMDynamicFunctionDescriptionArray) / sizeof(SBMFunctionDescriptionStruct)); ++i) {
readWordAndPrint(&sSBMDynamicFunctionDescriptionArray[i]);
}
}
int nonStandardInfoSupportedByPack = 0; // 0 not initialized, 1 supported, >1 not supported
void printSMBNonStandardInfo(void) {
if (nonStandardInfoSupportedByPack > 1) {
return;
}
if (nonStandardInfoSupportedByPack == 0) {
// check
uint16_t tActualValue = readWord(sSBMNonStandardFunctionDescriptionArray[0].FunctionCode);
uint16_t tActualValue1 = readWord(sSBMNonStandardFunctionDescriptionArray[1].FunctionCode);
if (tActualValue == tActualValue1) {
nonStandardInfoSupportedByPack = 2;
return;
}
}
for (uint8_t i = 0; i < (sizeof(sSBMNonStandardFunctionDescriptionArray) / sizeof(SBMFunctionDescriptionStruct)); ++i) {
readWordAndPrint(&sSBMNonStandardFunctionDescriptionArray[i]);
}
}
void printSMBTestInfo(void) {
for (uint8_t i = 0; i < (sizeof(sSBMTestFunctionDescriptionArray) / sizeof(SBMFunctionDescriptionStruct)); ++i) {
readWordAndPrint(&sSBMTestFunctionDescriptionArray[i]);
}
}
/*
* SBMInfo.h
*
* Copyright (C) 2016 Armin Joachimsmeyer
* armin.joachimsmeyer@gmail.com
*/
#ifndef SRC_SBMINFO_H_
#define SRC_SBMINFO_H_
// standard I2C address for Smart Battery packs
#define SBM_DEVICE_ADDRESS 0x0B
// Standard and common non-standard Smart Battery commands
#define MANUFACTURER_ACCESS 0x00 // Manufacturer specific values
#define REMAINING_CAPACITY_ALARM 0x01
#define REMAINING_TIME_ALARM 0x02
#define BATTERY_MODE 0x03
#define AtRate 0x04
#define AtRateTimeToFull 0x05
#define AtRateTimeToEmpty 0x06
#define AtRateOK 0x07
#define TEMPERATURE 0x08
#define VOLTAGE 0x09
#define CURRENT 0x0A
#define AverageCurrent 0x0B // of last minute
#define MAX_ERROR 0x0C // of state of charge calculation -Byte
#define RELATIVE_SOC 0x0D // StateOfCharge -Byte
#define ABSOLUTE_SOC 0x0E // -Byte
#define REMAINING_CAPACITY 0x0F
#define FULL_CHARGE_CAPACITY 0x10
#define RUN_TIME_TO_EMPTY 0x11
#define AVERAGE_TIME_TO_EMPTY 0x12
#define TIME_TO_FULL 0x13
#define CHARGING_CURRENT 0x14
#define CHARGING_VOLTAGE 0x15
#define BATTERY_STATUS 0x16
#define CYCLE_COUNT 0x17
#define DESIGN_CAPACITY 0x18
#define DESIGN_VOLTAGE 0x19
#define SPEC_INFO 0x1A
#define MFG_DATE 0x1B
#define SERIAL_NUM 0x1C
#define MFG_NAME 0x20 // String
#define DEV_NAME 0x21 // String
#define CELL_CHEM 0x22 // String
#define MFG_DATA 0x23 // String
#define CELL4_VOLTAGE 0x3C // Individual cell voltages don't work on Lenovo and Dell Packs
#define CELL3_VOLTAGE 0x3D
#define CELL2_VOLTAGE 0x3E
#define CELL1_VOLTAGE 0x3F
#define STATE_OF_HEALTH 0x4F // in % -Byte
/*
* Bits of BatteryMode
*/
#define INTERNAL_CHARGE_CONTROLLER 0x0001
#define CONDITION_FLAG 0x0008
#define CHARGE_CONTROLLER 0x0100
#define ALARM_MODE 0x2000
#define CHARGER_MODE 0x4000
#define CAPACITY_MODE 0x8000
/*
* Bits of BatteryStatus
*/
// Alarm Bits
#define OVER_CHARGED_ALARM 0x8000
#define TERMINATE_CHARGE_ALARM 0x4000
#define OVER_TEMP_ALARM 0x1000
#define TERMINATE_DISCHARGE_ALARM 0x0800
#define REMAINING_CAPACITY_ALARM_FLAG 0x0200
#define REMAINING_TIME_ALARM_FLAG 0x0100
// Bits of Status
#define INITIALIZED 0x0080
#define DISCHARGING 0x0040
#define FULLY_CHARGED 0x0020
#define FULLY_DISCHARGED 0x0010
struct SBMFunctionDescriptionStruct {
uint8_t FunctionCode;
const char * Description;
uint16_t lastValue;
void (*ValueFormatter)(struct SBMFunctionDescriptionStruct * aDescription, uint16_t aValueToFormat);
};
/*
* TI few ManufacturerAccess Commands
*/
#define BQ20Z70_Device_Type 0x0001
#define BQ20Z70_Firmware_Version 0x0002
#define BQ20Z70_Hardware_Version 0x0003
#define BQ2084_EDV_level 0x0003
#define BQ20Z70_Manufacturer_Status 0x0006
/*
* BQ20Z70 extended commands - Not used yet
*/
#define BQ20Z70_AFEData 0x45 // -11+1 Byte
#define BQ20Z70_FETControl 0x46 // -Byte
#define BQ20Z70_SafetyStatus 0x51
#define BQ20Z70_PFStatus 0x53 // permanent failure
#define BQ20Z70_OperationStatus 0x54
#define BQ20Z70_ChargingStatus 0x55
#define BQ20Z70_WDResetData 0x58
#define BQ20Z70_PackVoltage 0x5a
#define BQ20Z70_AverageVoltage 0x5d // of last minute
#define BQ20Z70_UnSealKey 0x60 // -4 Byte
#define BQ20Z70_FullAccessKey 0x61 // -4 Byte
#define BQ20Z70_PFKey 0x62 // -4 Byte
#define BQ20Z70_AuthenKey3 0x63 // -4 Byte
#define BQ20Z70_AuthenKey2 0x64 // -4 Byte
#define BQ20Z70_AuthenKey1 0x65 // -4 Byte
#define BQ20Z70_AuthenKey0 0x66 // -4 Byte
#define BQ20Z70_ManufacturerInfo 0x70 // -8+1 Byte
#define BQ20Z70_SenseResistor 0x71
#define BQ20Z70_DataFlashSubClassID 0x77
#define BQ20Z70_DataFlashSubClassPage1 0x78 // -32 Byte
// ...
#define BQ20Z70_DataFlashSubClassPage8 0x7f // -32 Byte
#endif /* SRC_SBMINFO_H_ */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment