Interfacing Arduino R3 with Laptop Batteries / SMB

Hello,

I’m pretty new to Arduino and have pretty much beginner skills in programming.

I found this Project that enables one to access the Data bus that most laptop
Batteries use to communicate their status to the Laptop:

The goal is to be able to access this Databus in order to be able to
readout the Battery Status and to be able to charge it in a safe manner.
Furthermore, the goal is to reuse good used batteries from broken Devices.

My Issue is that this was written for the Arduino YUN, not the Uno R3.
There are some Instructions on the Page on how to adapt the code:

If you are using PackProbe with a different board, you’ll have to make multiple edits to the PackProbe sketch:

Change the sketch to replace “#include <Console.h>” with “#include <Serial.h>”, or whatever serial port is most appropriate for your Arduino hardware.
Comment out the “Bridge.begin()” and “Console.begin()” statements.
Uncomment the statements that initialize the serial connection, and edit them to refer to whichever serial port you chose above.
Replace all references to “Console.” (ie Console.print, Console.write) with the appropriate reference to the serial port you chose above (ie Serial.print, Serial.write)

So I made the changes suggested, and if using Serial.h, I get following error message:

PackProbe_UNO_Edit:85:10: fatal error: Serial.h: No such file or directory
 #include <Serial.h>
          ^~~~~~~~~~
compilation terminated.
exit status 1
Serial.h: No such file or directory

If I use SoftwareSerial.h I get other Errors:

/home/ruessel/Arduino/PackProbe_UNO_Edit/PackProbe_UNO_Edit/PackProbe_UNO_Edit.ino: In function 'void setup()':
PackProbe_UNO_Edit:136:18: error: 'i2c_init' was not declared in this scope
   Serial.println(i2c_init());
                  ^~~~~~~~
/home/ruessel/Arduino/PackProbe_UNO_Edit/PackProbe_UNO_Edit/PackProbe_UNO_Edit.ino:136:18: note: suggested alternative: 'isDigit'
   Serial.println(i2c_init());
                  ^~~~~~~~
                  isDigit
/home/ruessel/Arduino/PackProbe_UNO_Edit/PackProbe_UNO_Edit/PackProbe_UNO_Edit.ino: In function 'int fetchWord(byte)':
PackProbe_UNO_Edit:154:32: error: 'I2C_WRITE' was not declared in this scope
   i2c_start(deviceAddress<<1 | I2C_WRITE);
                                ^~~~~~~~~
PackProbe_UNO_Edit:154:3: error: 'i2c_start' was not declared in this scope
   i2c_start(deviceAddress<<1 | I2C_WRITE);
   ^~~~~~~~~
/home/ruessel/Arduino/PackProbe_UNO_Edit/PackProbe_UNO_Edit/PackProbe_UNO_Edit.ino:154:3: note: suggested alternative: 'va_start'
   i2c_start(deviceAddress<<1 | I2C_WRITE);
   ^~~~~~~~~
   va_start
PackProbe_UNO_Edit:155:3: error: 'i2c_write' was not declared in this scope
   i2c_write(func);
   ^~~~~~~~~
/home/ruessel/Arduino/PackProbe_UNO_Edit/PackProbe_UNO_Edit/PackProbe_UNO_Edit.ino:155:3: note: suggested alternative: 'fwrite'
   i2c_write(func);
   ^~~~~~~~~
   fwrite
PackProbe_UNO_Edit:156:36: error: 'I2C_READ' was not declared in this scope
   i2c_rep_start(deviceAddress<<1 | I2C_READ);
                                    ^~~~~~~~
PackProbe_UNO_Edit:156:3: error: 'i2c_rep_start' was not declared in this scope
   i2c_rep_start(deviceAddress<<1 | I2C_READ);
   ^~~~~~~~~~~~~
PackProbe_UNO_Edit:157:13: error: 'i2c_read' was not declared in this scope
   byte b1 = i2c_read(false);
             ^~~~~~~~
/home/ruessel/Arduino/PackProbe_UNO_Edit/PackProbe_UNO_Edit/PackProbe_UNO_Edit.ino:157:13: note: suggested alternative: 'fread'
   byte b1 = i2c_read(false);
             ^~~~~~~~
             fread
PackProbe_UNO_Edit:159:3: error: 'i2c_stop' was not declared in this scope
   i2c_stop();
   ^~~~~~~~
/home/ruessel/Arduino/PackProbe_UNO_Edit/PackProbe_UNO_Edit/PackProbe_UNO_Edit.ino: In function 'uint8_t i2c_smbus_read_block(uint8_t, uint8_t*, uint8_t)':
PackProbe_UNO_Edit:166:34: error: 'I2C_WRITE' was not declared in this scope
   i2c_start((deviceAddress<<1) + I2C_WRITE);
                                  ^~~~~~~~~
PackProbe_UNO_Edit:166:3: error: 'i2c_start' was not declared in this scope
   i2c_start((deviceAddress<<1) + I2C_WRITE);
   ^~~~~~~~~
/home/ruessel/Arduino/PackProbe_UNO_Edit/PackProbe_UNO_Edit/PackProbe_UNO_Edit.ino:166:3: note: suggested alternative: 'va_start'
   i2c_start((deviceAddress<<1) + I2C_WRITE);
   ^~~~~~~~~
   va_start
PackProbe_UNO_Edit:167:3: error: 'i2c_write' was not declared in this scope
   i2c_write(command);
   ^~~~~~~~~
/home/ruessel/Arduino/PackProbe_UNO_Edit/PackProbe_UNO_Edit/PackProbe_UNO_Edit.ino:167:3: note: suggested alternative: 'fwrite'
   i2c_write(command);
   ^~~~~~~~~
   fwrite
PackProbe_UNO_Edit:168:38: error: 'I2C_READ' was not declared in this scope
   i2c_rep_start((deviceAddress<<1) + I2C_READ);
                                      ^~~~~~~~
PackProbe_UNO_Edit:168:3: error: 'i2c_rep_start' was not declared in this scope
   i2c_rep_start((deviceAddress<<1) + I2C_READ);
   ^~~~~~~~~~~~~
PackProbe_UNO_Edit:169:15: error: 'i2c_read' was not declared in this scope
   num_bytes = i2c_read(false); // num of bytes; 1 byte will be index 0
               ^~~~~~~~
/home/ruessel/Arduino/PackProbe_UNO_Edit/PackProbe_UNO_Edit/PackProbe_UNO_Edit.ino:169:15: note: suggested alternative: 'fread'
   num_bytes = i2c_read(false); // num of bytes; 1 byte will be index 0
               ^~~~~~~~
               fread
PackProbe_UNO_Edit:176:3: error: 'i2c_stop' was not declared in this scope
   i2c_stop();
   ^~~~~~~~
/home/ruessel/Arduino/PackProbe_UNO_Edit/PackProbe_UNO_Edit/PackProbe_UNO_Edit.ino: In function 'void scan()':
PackProbe_UNO_Edit:187:33: error: 'I2C_WRITE' was not declared in this scope
     bool ack = i2c_start(i<<1 | I2C_WRITE);
                                 ^~~~~~~~~
PackProbe_UNO_Edit:187:16: error: 'i2c_start' was not declared in this scope
     bool ack = i2c_start(i<<1 | I2C_WRITE);
                ^~~~~~~~~
/home/ruessel/Arduino/PackProbe_UNO_Edit/PackProbe_UNO_Edit/PackProbe_UNO_Edit.ino:187:16: note: suggested alternative: 'va_start'
     bool ack = i2c_start(i<<1 | I2C_WRITE);
                ^~~~~~~~~
                va_start
PackProbe_UNO_Edit:196:5: error: 'i2c_stop' was not declared in this scope
     i2c_stop();
     ^~~~~~~~
exit status 1
'i2c_init' was not declared in this scope

I’m kinda lost in this… Any Advice if this is even possible with a Uno-R3?
I Installed many different I2C Libraries, but I was not able to get this to
work.

Any Advice would be appreciated!

Best Regards, Technikerl

The changed Code:

PackProbe

by Erik Speckman
//
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 PORTB
#define SCL_PIN 4

#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.

*************************************************************************************/

// Yun Bridge Serial Access
#include <SoftwareSerial.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_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(9600);  // 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 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);
}

Wow...nobody?

Any Hints???

If you post your code as described in the Read this before posting a programming question, more people will read it.

@ SteveMann:

Thank you for the Hint, but I used “code” brackets to post my code…
Can you point out what else I did wrong?

Best Regards, Technikerl