forked from PowerCartel/PackProbe
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPackProbeGerwin.h
307 lines (234 loc) · 9.17 KB
/
PackProbeGerwin.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
/************************************************************************************
PackProbe
by Erik Speckman
For documentation and the latest version see: http://powercartel.com/projects/packprobe/
Source Repository: [email protected]:PowerCartel/PackProbe.git
Depends on SoftI2CMaster http://playground.arduino.cc/Main/SoftwareI2CLibrary
PackProbe allows you to obtain detailed history and health information on laptop battery
packs without access to the corresponding laptop.
This is is a proof of concept. It doesn't handle error conditions and spurious values
PackProbe is based on:
http://linuxehacking.blogspot.com/2014/03/recharging-and-reusing-acer-laptop.html
and http://forum.arduino.cc/index.php?topic=62955.0
*************************************************************************************/
/************************************************************************************
Configure and then load the SoftI2CMaster library
The default is for data (SDA) on DPin6 and clock (SDC) on DPin8 on the Arduino Yun.
For more details on configuring the library, see:
http://playground.arduino.cc/Main/SoftwareI2CLibrary
For background on Arduino Port and Pin Mapping, see:
http://www.arduino.cc/en/Reference/PortManipulation
To find the pin mappings for your board see:
Atmega 168/328 MCU (Uno, Mini, Nano, Fio, Pro):
http://arduino.cc/en/Hacking/PinMapping168
Atmega 32u4 MCU (Yun, Leonardo, Micro):
http://arduino.cc/en/Hacking/PinMapping32u4
*************************************************************************************/
// Arduino Dpin6 = Atmega PD7
#define SDA_PORT PORTD
#define SDA_PIN 7
// Arduino DPin8 = Atmega PB4
#define SCL_PORT PORTD
#define SCL_PIN 6
#define I2C_SLOWMODE 1
#include <SoftI2CMaster.h>
// standard I2C address for Smart Battery packs
byte deviceAddress = 11;
/************************************************************************************
Output Selection
This sketch was made to run on the WiFi-enabled Arduino Yun.
The default programming and output connection of the Yun is over TCP/IP
rather than the serial connection used by most other Arduino boards.
The default output on the Yun is available through the Serial library.
If you are using this sketch on board with a serial port, you'll need to
#include <Serial.h> instead and change every instance of "Serial." in the
code to "Serial." In addition you need to comment/uncomment the necessary
code in the setup function before compiling and installing on your board.
We are looking for a clean way to simplify this configuration in the future,
or make it automatic. Suggestions welcome.
*************************************************************************************/
//#include <Serial.h>
// Standard and common non-standard Smart Battery commands
#define BATTERY_MODE 0x03
#define TEMPERATURE 0x08
#define VOLTAGE 0x09
#define CURRENT 0x0A
#define RELATIVE_SOC 0x0D
#define ABSOLUTE_SOC 0x0E
#define REMAINING_CAPACITY 0x0F
#define FULL_CHARGE_CAPACITY 0x10
#define 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 // Indidual 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
#define bufferLen 32
uint8_t i2cBuffer[bufferLen];
void setup()
{
/**************************************************
Comment out the next two lines following lines for use with Serial output
**************************************************/
// Bridge.begin();
//Serial.begin();
/**************************************************
Uncomment the following lines for use with Serial output
**************************************************/
Serial.begin(115200); // start serial for output
Serial.println(i2c_init());
pinMode(22,INPUT_PULLUP);
pinMode(23,INPUT_PULLUP);
while (!Serial) {
; // wait for Serial port to connect.
}
Serial.println("Serial Initialized");
i2c_init();
Serial.println("I2C Inialized");
scan();
}
int fetchWord(byte func)
{
i2c_start(deviceAddress<<1 | I2C_WRITE);
i2c_write(func);
i2c_rep_start(deviceAddress<<1 | I2C_READ);
byte b1 = i2c_read(false);
byte b2 = i2c_read(true);
i2c_stop();
return (int)b1|((( int)b2)<<8);
}
uint8_t i2c_smbus_read_block ( uint8_t command, uint8_t* blockBuffer, uint8_t blockBufferLen )
{
uint8_t x, num_bytes;
i2c_start((deviceAddress<<1) + I2C_WRITE);
i2c_write(command);
i2c_rep_start((deviceAddress<<1) + I2C_READ);
num_bytes = i2c_read(false); // num of bytes; 1 byte will be index 0
num_bytes = constrain(num_bytes,0,blockBufferLen-2); // room for null at the end
for (x=0; x<num_bytes-1; x++) { // -1 because x=num_bytes-1 if x<y; last byte needs to be "nack"'d, x<y-1
blockBuffer[x] = i2c_read(false);
}
blockBuffer[x++] = i2c_read(true); // this will nack the last byte and store it in x's num_bytes-1 address.
blockBuffer[x] = 0; // and null it at last_byte+1
i2c_stop();
return num_bytes;
}
void scan()
{
byte i = 0;
for ( i= 0; i < 127; i++ )
{
Serial.print("Address: 0x");
Serial.print(i,HEX);
bool ack = i2c_start(i<<1 | I2C_WRITE);
if ( ack ) {
Serial.println(": OK");
Serial.flush();
}
else {
Serial.println(": -");
Serial.flush();
}
i2c_stop();
}
}
void loop()
{
uint8_t length_read = 0;
/*
Serial.print("Manufacturer Name: ");
length_read = i2c_smbus_read_block(MFG_NAME, i2cBuffer, bufferLen);
Serial.write(i2cBuffer, length_read);
Serial.println("");
Serial.print("Device Name: ");
length_read = i2c_smbus_read_block(DEV_NAME, i2cBuffer, bufferLen);
Serial.write(i2cBuffer, length_read);
Serial.println("");
Serial.print("Chemistry ");
length_read = i2c_smbus_read_block(CELL_CHEM, i2cBuffer, bufferLen);
Serial.write(i2cBuffer, length_read);
Serial.println("");
Serial.print("Manufacturer Data ");
length_read = i2c_smbus_read_block(MFG_DATA, i2cBuffer, bufferLen);
Serial.write(i2cBuffer, length_read);
Serial.println("");
Serial.print("Design Capacity: " );
Serial.println(fetchWord(DESIGN_CAPACITY));
Serial.print("Design Voltage: " );
Serial.println(fetchWord(DESIGN_VOLTAGE));
String formatted_date = "Manufacture Date (Y-M-D): ";
int mdate = fetchWord(MFG_DATE);
int mday = B00011111 & mdate;
int mmonth = mdate>>5 & B00001111;
int myear = 1980 + (mdate>>9 & B01111111);
formatted_date += myear;
formatted_date += "-";
formatted_date += mmonth;
formatted_date += "-";
formatted_date += mday;
Serial.println(formatted_date);
Serial.print("Serial Number: ");
Serial.println(fetchWord(SERIAL_NUM));
Serial.print("Specification Info: ");
Serial.println(fetchWord(SPEC_INFO));
Serial.print("Cycle Count: " );
Serial.println(fetchWord(CYCLE_COUNT));
*/
Serial.print("Voltage: ");
Serial.println((float)fetchWord(VOLTAGE)/1000);
Serial.print("Full Charge Capacity: " );
Serial.println(fetchWord(FULL_CHARGE_CAPACITY));
Serial.print("Remaining Capacity: " );
Serial.println(fetchWord(REMAINING_CAPACITY));
Serial.print("Relative Charge(%): ");
Serial.println(fetchWord(RELATIVE_SOC));
Serial.print("Absolute Charge(%): ");
Serial.println(fetchWord(ABSOLUTE_SOC));
Serial.print("Minutes remaining untill empty: ");
Serial.println(fetchWord(TIME_TO_EMPTY));
Serial.print("Minutes remaining for full charge: ");
Serial.println(fetchWord(TIME_TO_FULL));
/*
// These aren't part of the standard, but work with some packs.
// They don't work with the Lenovo and Dell packs we've tested
Serial.print("Cell 1 Voltage: ");
Serial.println(fetchWord(CELL1_VOLTAGE));
Serial.print("Cell 2 Voltage: ");
Serial.println(fetchWord(CELL2_VOLTAGE));
Serial.print("Cell 3 Voltage: ");
Serial.println(fetchWord(CELL3_VOLTAGE));
Serial.print("Cell 4 Voltage: ");
Serial.println(fetchWord(CELL4_VOLTAGE));
Serial.print("State of Health: ");
Serial.println(fetchWord(STATE_OF_HEALTH));
Serial.print("Battery Mode (BIN): 0b");
Serial.println(fetchWord(BATTERY_MODE),BIN);
Serial.print("Battery Status (BIN): 0b");
Serial.println(fetchWord(BATTERY_STATUS),BIN);
*/
Serial.print("Charging Current: ");
Serial.println(fetchWord(CHARGING_CURRENT));
Serial.print("Charging Voltage: ");
Serial.println(fetchWord(CHARGING_VOLTAGE));
Serial.print("Temp: ");
unsigned int tempk = fetchWord(TEMPERATURE);
Serial.println((float)tempk/10.0-273.15);
Serial.print("Current (mA): " );
Serial.println(fetchWord(CURRENT));
Serial.println(".");
delay(1000);
}