Perfect! Works like a dream! Full control over ACK and NACK is exactly what I needed, and with the exception of a few "oops, didn't realize it didn't do that" - like turning on pull-ups and using i2c_start_wait() - it was a flawless conversion. Code blew up pretty big in size, though, to the tune of about 6.4kb, but meh, I'm not really going for efficiency
This is such a sweet sight to see, coming from 2 wires poking into the connector of a laptop battery (and a 12v/2a switching power supply providing the manual "but assisted" charging):
Ready!
------
Mfg: OEM | Dev: - | Chem: NIMH
Date: 2/22/2000 | Cycles: 1
11.92v, 1.28 A, 87.89 F, 57%, 87 min remaining.
11.91v, 1.28 A, 87.89 F, 57%, 87 min remaining.
11.90v, 1.28 A, 87.89 F, 57%, 87 min remaining.
11.92v, 1.28 A, 87.89 F, 57%, 85 min remaining.
11.92v, 1.28 A, 87.89 F, 57%, 85 min remaining.
11.91v, 1.28 A, 87.89 F, 57%, 85 min remaining.
>>b0x20;
Reading block using command: 20
Completed, received 3 bytes:
OEM
11.92v, 1.28 A, 87.89 F, 57%, 82 min remaining.
11.92v, 1.28 A, 87.89 F, 57%, 82 min remaining.
11.91v, 1.28 A, 88.07 F, 57%, 82 min remaining.
Yep, it grabs the "block read" flawlessly! Through this little thing, I now have full control over all the functions of the battery as documented in the datasheet I just send a read command with, e.g. "r0x0c;", for "read command 0x0C", and it confirms the interpreted input, sends the command, and returns the result. Woo!
Here's the full code, now that it's working pretty well... this is based on a bq2040 "Gas Gauge IC with SMBus Interface". By tweaking the command parameters (sorry, I didn't #define them), it's pretty easy to adapt it to other batteries... all batteries that I know of use this SMBus interface
#include <i2cmaster.h>
#define readBufferLen 17
char readBuffer[readBufferLen];
uint8_t i2cBuffer[readBufferLen];
#define deviceaddress B00010110
uint8_t serialCommand, loopCount;
unsigned int serialData;
void setup() {
聽 i2c_init();
聽 PORTC = (1 << PORTC4) | (1 << PORTC5); //enable pullups
聽 Serial.begin(9600);
聽 Serial.println("Ready!");
聽 Serial.println("------");
聽 Serial.print("Mfg: ");
聽 i2c_smbus_read_block(0x20,i2cBuffer,readBufferLen);
聽 Serial.print((char*)i2cBuffer);
聽 Serial.print(" | Dev: ");
聽 i2c_smbus_read_block(0x21,i2cBuffer,readBufferLen);
聽 Serial.print((char*)i2cBuffer);
聽 Serial.print(" | Chem: ");
聽 i2c_smbus_read_block(0x22,i2cBuffer,readBufferLen);
聽 Serial.println((char*)i2cBuffer);
聽 serialData = i2c_smbus_read_word(0x1b);
聽 Serial.print("Date: ");
聽 Serial.print((uint8_t)(serialData >> 5) & B00001111,DEC);
聽 Serial.print('/');
聽 Serial.print((uint8_t)serialData & B00011111,DEC);
聽 Serial.print('/');
聽 Serial.print((serialData >> 9) + 1980,DEC);
聽 Serial.print(" | Cycles: ");
聽 Serial.println(i2c_smbus_read_word(0x17));
}
void loop() {
聽 if (loopCount > 10) {
聽 聽 // gather things
聽 聽 int currVoltage, currAmps, estPercent, currTemp, timeLeft;
聽 聽 currVoltage = i2c_smbus_read_word(0x9);
聽 聽 currAmps = i2c_smbus_read_word(0xA);
聽 聽 estPercent = i2c_smbus_read_word(0xD);
聽 聽 currTemp = i2c_smbus_read_word(0x8);
聽 聽 timeLeft = i2c_smbus_read_word(0x13);
聽
聽 聽 Serial.print((float)currVoltage / 1000, 2);
聽 聽 Serial.print("v, ");
聽 聽 Serial.print((float)currAmps / 1000, 2);
聽 聽 Serial.print(" A, ");
聽 聽 Serial.print(((float)currTemp/10 - 273.15) * 1.8 + 32, 2);
聽 聽 Serial.print(" F, ");
聽 聽 Serial.print(estPercent,DEC);
聽 聽 Serial.print("%, ");
聽 聽 Serial.print(timeLeft,DEC);
聽 聽 Serial.println(" min remaining.");
聽 聽 loopCount = 0;
聽 }
聽 loopCount++;
聽 if (Serial.available()) {
聽 聽 delay(500);
聽 聽 uint8_t x = 0;
聽 聽 if (Serial.peek() == 'w') {
聽 聽 聽 // write
聽 聽 聽 Serial.read(); // get the state out of the buffer
聽 聽 聽 while (Serial.peek() != ';') {
聽 聽 聽 聽 readBuffer[x] = Serial.read();
聽 聽 聽 聽 x++;
聽 聽 聽 聽 if (x > readBufferLen) return;
聽 聽 聽 }
聽 聽 聽 readBuffer[x] = 0; // null the buffer
聽 聽 聽 Serial.read(); // get the semicolon out of there
聽 聽 聽 serialCommand = strtoul(readBuffer, NULL, 0);
聽 聽 聽 Serial.print("Writing word using command: ");
聽 聽 聽 Serial.print(serialCommand,HEX);
聽 聽 聽 x = 0;
聽 聽 聽 while (Serial.peek() != ';') {
聽 聽 聽 聽 readBuffer[x] = Serial.read();
聽 聽 聽 聽 x++;
聽 聽 聽 聽 if (x > readBufferLen) return;
聽 聽 聽 }
聽 聽 聽 readBuffer[x] = 0; // null the buffer
聽 聽 聽 Serial.read(); // clear the semicolon
聽 聽 聽 serialData = strtoul(readBuffer, NULL, 0);
聽 聽 聽 Serial.print(", data: ");
聽 聽 聽 Serial.println(serialData,HEX);
聽 聽 聽 i2c_smbus_write_word(serialCommand,serialData);
聽 聽 聽 Serial.println("Completed.");
聽 聽 聽
聽 聽 } else if (Serial.peek() == 'r') {
聽 聽 聽 // read word
聽 聽 聽 Serial.read(); // get the state out of the buffer
聽 聽 聽 while (Serial.peek() != ';') {
聽 聽 聽 聽 readBuffer[x] = Serial.read();
聽 聽 聽 聽 x++;
聽 聽 聽 聽 if (x > readBufferLen) return;
聽 聽 聽 }
聽 聽 聽 readBuffer[x] = 0; // null the buffer
聽 聽 聽 Serial.read(); // clear the semicolon
聽 聽 聽 serialCommand = strtoul(readBuffer, NULL, 0);
聽 聽 聽 Serial.print("Reading word using command: ");
聽 聽 聽 Serial.println(serialCommand,HEX);
聽 聽 聽 serialData = i2c_smbus_read_word(serialCommand);
聽 聽 聽 Serial.print("Completed, received: ");
聽 聽 聽 Serial.print(serialData,HEX);
聽 聽 聽 Serial.print(" (hex), ");
聽 聽 聽 Serial.print(serialData,DEC);
聽 聽 聽 Serial.println(" (dec)");
聽 聽 } else if (Serial.peek() == 'b') {
聽 聽 聽 // read block
聽 聽 聽 Serial.read(); // get the state out of the buffer
聽 聽 聽 while (Serial.peek() != ';') {
聽 聽 聽 聽 readBuffer[x] = Serial.read();
聽 聽 聽 聽 x++;
聽 聽 聽 聽 if (x > readBufferLen) return;
聽 聽 聽 }
聽 聽 聽 Serial.read(); // clear the semicolon
聽 聽 聽 readBuffer[x] = 0;
聽 聽 聽 serialCommand = strtoul(readBuffer, NULL, 0);
聽 聽 聽 Serial.print("Reading block using command: ");
聽 聽 聽 Serial.println(serialCommand,HEX);
聽 聽 聽 x = i2c_smbus_read_block(serialCommand,i2cBuffer,readBufferLen);
聽 聽 聽 Serial.print("Completed, received ");
聽 聽 聽 Serial.print(x,DEC);
聽 聽 聽 Serial.println(" bytes:");
聽 聽 聽 x = 0;
聽 聽 聽 for (x=0; x<readBufferLen; x++) {
聽 聽 聽 聽 Serial.print(i2cBuffer[x]);
聽 聽 聽 }
聽 聽 聽 Serial.println();
聽 聽 } else {
聽 聽 聽 Serial.print("Junk: ");
聽 聽 聽 Serial.println(Serial.read(),HEX);
聽 聽 }
聽 }
聽 delay(500);
}
void i2c_smbus_write_word ( uint8_t command, unsigned int data ) {
聽 i2c_start_wait(deviceaddress + I2C_WRITE);
聽 i2c_write(command);
聽 i2c_write((uint8_t)data);
聽 i2c_write((uint8_t)(data>>8));
聽 i2c_stop();
聽 return;
}
unsigned int i2c_smbus_read_word ( uint8_t command ) {
聽 unsigned int buffer = 0;
聽 i2c_start_wait(deviceaddress + I2C_WRITE);
聽 i2c_write(command);
聽 i2c_rep_start(deviceaddress + I2C_READ);
聽 buffer = i2c_readAck();
聽 buffer += i2c_readNak() << 8;
聽 return buffer;
}
uint8_t i2c_smbus_read_block ( uint8_t command, uint8_t* blockBuffer, uint8_t blockBufferLen ) {
聽 uint8_t x = 0;
聽 uint8_t y = 0;
聽 i2c_start_wait(deviceaddress + I2C_WRITE);
聽 i2c_write(command);
聽 i2c_rep_start(deviceaddress + I2C_READ);
聽 y = i2c_readAck();
聽 for (x=0; x<y-1; x++) {
聽 聽 blockBuffer[x] = i2c_readAck();
聽 }
聽 blockBuffer[x] = i2c_readNak();
聽 blockBuffer[x+1] = 0;
聽 return y;
}
Also, it was mostly designed for my use, but since I had to ask for help in the forum (and got it, thanks!), I figured I'd return my work to the community