Come "leggere" batterie DJI

Intanto bentrovati a tutti. Ultimamente sto bazzicando più con l'elettronica che con Arduino. Torno nel forum per l'ennesima "sfida". Ho delle batterie per drone DJI ed avendo sostituito gli elementi interni vorrei resettare alcuni paramteri. Sul web ho trovato qualcosa con Arduino ma il codice è in vendita. Non è per la irrisoria cifra ma come al solito per l'ennesima "sfida" dell'hobbysta. Ora ho trovato anche un codice che allego e mi accingo a "studiare" e chiedo a voi esperti di aiutarmi nella comprendione del codice. Le batterie DJI hanno una board interna in cui vengono salvate le informazioni e sono costituite da due celle da 3,85V da 2250mAh polimeri di litio

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

 * SMBus FOR 3DR SOLO
 * STAVROPOULOS
 * Code Version 0.01 beta
 *
 * MUCH OF THIS CODE WAS COPIED FROM
 * https://github.com/PowerCartel/PackProbe/blob/master/PackProbe/PackProbe.ino
 * https://github.com/ArduPilot/PX4Firmware/blob/master/src/drivers/batt_smbus/batt_smbus.cpp
 *
 **********************************/

/**********************************
 * CONFIGURE I2C/SERIAL ON ARDUINO
 **********************************/
 
//DEFINE SDA AND SCL PINS
  #define SCL_PIN 5                 //COMMUNICATION PIN 20 ON MEGA PIN5 on R3 UNO
  #define SCL_PORT PORTC

  #define SDA_PIN 4                 //COMMUNICATION PIN 21 ON MEGA PIN 4 on R3 UNO
  #define SDA_PORT PORTC

//CONFIGURE I2C MODES
  //#define I2C_TIMEOUT 100           //PREVENT SLAVE DEVICES FROM STRETCHING LOW PERIOD OF THE CLOCK INDEFINITELY AND LOCKING UP MCU BY DEFINING TIMEOUT
  //#define I2C_NOINTERRUPT 1       //SET TO 1 IF SMBus DEVICE CAN TIMEOUT
  //#define I2C_FASTMODE 1          //THE STANDARD I2C FREQ IS 100kHz.  USE THIS TO PERMIT FASTER UP TO 400kHz.
  //#define I2C_SLOWMODE 1            //THE STANDARD I2C FREQ IS 100kHz.  USE THIS TO PERMIT SLOWER, DOWN TO 25kHz.
  #define BAUD_RATE 115200
  #include <SoftI2CMaster.h>

/**********************************
 * CONFIGURE SERIAL LIBRARY
 **********************************/
  //#include <SoftwareSerial.h>
  //#include <Serial.h>
  #include <Wire.h>
  #include <LiquidCrystal_I2C.h> //i2c library for my LCD display that works.
 
 
/**********************************
 * DEFINE VARIABLES AND SMBus MAPPINGS
 **********************************/

  #define BATT_SMBUS_ADDR                     0x0B                ///< I2C address
  #define BATT_SMBUS_ADDR_MIN                 0x08                ///< lowest possible address
  #define BATT_SMBUS_ADDR_MAX                 0x7F                ///< highest possible address
//BUS MAPPINGS FROM DEV.3DR
  #define BATT_SMBUS_TEMP                     0x08                ///< temperature register
  #define BATT_SMBUS_VOLTAGE                  0x09                ///< voltage register
  #define BATT_SMBUS_REMAINING_CAPACITY       0x0f                ///< predicted remaining battery capacity as a percentage
  #define BATT_SMBUS_FULL_CHARGE_CAPACITY     0x10                ///< capacity when fully charged
  #define BATT_SMBUS_DESIGN_CAPACITY          0x18                ///< design capacity register
  #define BATT_SMBUS_DESIGN_VOLTAGE           0x19                ///< design voltage register
  #define BATT_SMBUS_SERIALNUM                0x1c                ///< serial number register
  #define BATT_SMBUS_MANUFACTURE_NAME         0x20                ///< manufacturer name
  #define BATT_SMBUS_MANUFACTURE_DATA         0x23                ///< manufacturer data
  #define BATT_SMBUS_MANUFACTURE_INFO         0x25                ///< cell voltage register
  #define BATT_SMBUS_CURRENT                  0x2a                ///< current register
  #define BATT_SMBUS_MEASUREMENT_INTERVAL_US  (1000000 / 10)      ///< time in microseconds, measure at 10hz
  #define BATT_SMBUS_TIMEOUT_US               10000000            ///< timeout looking for battery 10seconds after startup
  #define BATT_SMBUS_BUTTON_DEBOUNCE_MS       300                 ///< button holds longer than this time will cause a power off event
 
  #define BATT_SMBUS_PEC_POLYNOMIAL           0x07                ///< Polynomial for calculating PEC
  #define BATT_SMBUS_I2C_BUS                  PX4_I2C_BUS_EXPANSION
//BUS MAPPINGS FROM SMBus PROTOCOL DOCUMENTATION
#define BATTERY_MODE             0x03
#define CURRENT                  0x0A
#define RELATIVE_SOC             0x0D
#define ABSOLUTE_SOC             0x0E
#define TIME_TO_FULL             0x13
#define CHARGING_CURRENT         0x14
#define CHARGING_VOLTAGE         0x15
#define BATTERY_STATUS           0x16
#define CYCLE_COUNT              0x17
#define SPEC_INFO                0x1A
#define MFG_DATE                 0x1B
#define DEV_NAME                 0x21   // String
#define CELL_CHEM                0x22   // 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
//END BUS MAPPINGS
 
  #define bufferLen 32
  uint8_t i2cBuffer[bufferLen];

// standard I2C address for Smart Battery packs
  byte deviceAddress = BATT_SMBUS_ADDR;
 
  LiquidCrystal_I2C LCD(0x3B,16,2);
 
 
//int i2c;



           

void setup()
{

  //INITIATE SERIAL CONSOLE
    Serial.begin(BAUD_RATE);
  /*
    Serial.println(i2c_init());
   
   
  //SETUP I2C INPUT PINS
   // pinMode(5,INPUT_PULLUP);  // commented this out becauase it cant be used on R3 UNO
   // pinMode(4,INPUT_PULLUP);
    Serial.flush();

    while (!Serial) {
    ;                                                       //wait for Console port to connect.
    }

    Serial.println("Console Initialized");
 */
    i2c_init();                                             //i2c_start initialized the I2C system.  will return false if bus is locked.
    Serial.println("I2C Inialized");

 
 
}    

int fetchWord(byte func)
{
    i2c_start(deviceAddress<<1 | I2C_WRITE);                //Initiates a transfer to the slave device with the (8-bit) I2C address addr.
                                                            //Alternatively, use i2c_start_wait which tries repeatedly to start transfer until acknowledgment received
    //i2c_start_wait(deviceAddress<<1 | I2C_WRITE);
    i2c_write(func);                                        //Sends a byte to the previously addressed device. Returns true if the device replies with an ACK.
    i2c_rep_start(deviceAddress<<1 | I2C_READ);             //Sends a repeated start condition, i.e., it starts a new transfer without sending first a stop condition.
    byte b1 = i2c_read(false);                              //i2c_read Requests to receive a byte from the slave device. If last is true,
                                                            //then a NAK is sent after receiving the byte finishing the read transfer sequence.
    byte b2 = i2c_read(true);
    i2c_stop();                                             //Sends a stop condition and thereby releases the bus.
    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 loop()
{


    uint8_t length_read = 0;
 
    //delay (750);
    Serial.write(12);
    Serial.print("Manufacturer Name: ");
    length_read = i2c_smbus_read_block(BATT_SMBUS_MANUFACTURE_NAME, i2cBuffer, bufferLen);
    Serial.write(i2cBuffer, length_read);
    Serial.println("");
 
    //Serial.print("Manufacturer Data: ");
   // length_read = i2c_smbus_read_block(BATT_SMBUS_MANUFACTURE_DATA, i2cBuffer, bufferLen);
    //Serial.write(i2cBuffer, length_read);
   // Serial.println("");
 
   // Serial.print("Manufacturer Info: ");
    //length_read = i2c_smbus_read_block(BATT_SMBUS_MANUFACTURE_INFO, i2cBuffer, bufferLen);
    //Serial.write(i2cBuffer, length_read);
   // Serial.println("");
 
    Serial.print("Design Capacity: " );
    Serial.println(fetchWord(BATT_SMBUS_DESIGN_CAPACITY));
 
    Serial.print("Design Voltage: " );
    Serial.println(fetchWord(BATT_SMBUS_DESIGN_VOLTAGE));
 
    Serial.print("Serial Number: ");
    Serial.println(fetchWord(BATT_SMBUS_SERIALNUM));
 
    Serial.print("Voltage: ");
    Serial.println((float)fetchWord(BATT_SMBUS_VOLTAGE)/1000);
 
    Serial.print("Full Charge Capacity mAh: " );
    Serial.println(fetchWord(BATT_SMBUS_FULL_CHARGE_CAPACITY));
 
    Serial.print("Remaining Capacity mAh: " );
    Serial.println(fetchWord(BATT_SMBUS_REMAINING_CAPACITY));
 
    Serial.print("Temp: ");
    unsigned int tempk = fetchWord(BATT_SMBUS_TEMP);
    Serial.println((float)tempk/10.0-273.15);
 
    Serial.print("Current (mA): " );
    Serial.println(fetchWord(BATT_SMBUS_CURRENT));

    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("");

    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("Specification Info: ");
   // Serial.println(fetchWord(SPEC_INFO));
 
    Serial.print("Cycle Count: " );
    Serial.println(fetchWord(CYCLE_COUNT));
 
    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("Charging Current (mA): " );
    Serial.println(fetchWord(CURRENT));

    Serial.println(".");


 
//-----------------------------------------------------
//Attempt to fetch data again and store it for LCD
//-----------------------------------------------------


  float cellV1(fetchWord(CELL1_VOLTAGE));
  cellV1 = cellV1 / 1000;
  float cellV2(fetchWord(CELL2_VOLTAGE));
  cellV2 = cellV2 / 1000;
  float cellV3(fetchWord(CELL3_VOLTAGE));
  cellV3 = cellV3 / 1000;
  float cellV4(fetchWord(CELL4_VOLTAGE));
  cellV4 = cellV4 / 1000;

    LCD.init(); //This initilasies the LCD Screen  This also currently stops the SMBus Battery data reading
    LCD.backlight();//this turns the backlight on
   

    //LCD.setCursor(0,0);
    //LCD.print("Charge V:");
    //LCD.println(fetchWord(CHARGING_VOLTAGE));
    //LCD.setCursor(0,1);
    //LCD.print("Charge mA:");
    //LCD.print(fetchWord(CHARGING_CURRENT));
   


    LCD.setCursor(0,0);
    LCD.print("C1:");
    LCD.print(cellV1);
    LCD.print(" ");
    LCD.print("C2:");
    LCD.print(cellV2);
 

    LCD.setCursor(0,1);
    LCD.print("C3:");
    LCD.print(cellV4);
    LCD.print(" ");
    LCD.print("C4:");
    LCD.print(cellV4);
   



    delay(100);


 
}

Il codice che hai allegato implementa una I2C software, cosa che con la maggior parte dei microcontrollori odierni non è necessaria.

Non hai specificato quale scheda andrai ad usare, ma al 99% avrà l'I2C integrato quindi per quanto possa comunque funzionare implementarlo via software, sarebbe una cosa con poco senso.

A questo link trovi un progetto molto simile dove però viene usato un Arduino Nano (quindi I2C hardware) ed un display TFT.

Intanto grazie per la utilissima risposta. Usero Arduino Uno. Studio un pò il codice e vediamo fin dove riesco ad arrivare prima nella comprensione di ciò che il codice va a fare e poi nelle relative istruzioni...ne vedo molte mai viste. Immagino a questo punto che i circuiti di conotrollo delle batterie utilizzino una serie di comandi "standard" o sbaglio? Dovrò poi capire come resettare il valore che mi interessa. Grazie di nuovo. Se utile allego il codice dle progetto da te linkato.

/*
thanks to
PowerCartel for smart battery routines - https://github.com/PowerCartel/PackProbe
Bodmer for fast TFT library - https://github.com/Bodmer/TFT_ST7735
Alain Aeropic - for BatMan inspiration - https://www.thingiverse.com/thing:4235767 
*/

#define VERSION   "v1.0"

#include <TFT_ST7735.h> // Graphics and font library for ST7735 driver chip
#include <SPI.h>

#include <Wire.h>
byte deviceAddress = 11;

// 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
#define CELL3_VOLTAGE            0x3D
#define CELL2_VOLTAGE            0x3E
#define CELL1_VOLTAGE            0x3F
#define STATE_OF_HEALTH          0x4F
#define DJI_SERIAL               0xD8  // String

#define bufferLen 32
uint8_t i2cBuffer[bufferLen];

TFT_ST7735 tft = TFT_ST7735();
#include "dji_logo-48x48.h"

#define TFT_BACKGROUND  0xD6BB

void setup()
{
  Serial.begin(115200);

  Wire.begin();
  Wire.setClock(100000);
    
  tft.init();
  tft.setRotation(0);	// portrait

  tft.fillScreen(TFT_BLACK);

  drawIcon(dji_logo, (tft.width() - Width)/2,      (tft.height() - Height)/2-40, Width, Height);
  tft.setTextSize(1);
  tft.setTextColor(TFT_BACKGROUND, TFT_BLACK);
  tft.drawCentreString("Mavic Mini", 64, 68, 2);
  tft.drawCentreString("battery info", 64, 90, 1);
  tft.drawCentreString(VERSION, 64, 102, 1);
  tft.drawCentreString("github.com/czipis", 64, 120, 1);

  delay(3000);
  tft.fillScreen(TFT_BACKGROUND);
}

uint8_t read_byte()
{
  while (1)
  {
    if (Wire.available())
    {
      return Wire.read();
    }
  }
}

int fetchWord(byte func)
{
  Wire.beginTransmission(deviceAddress);
  Wire.write(func);
  Wire.endTransmission(false);
  delay(1);// FIX wire bug
  Wire.requestFrom(deviceAddress, 2, true);
  
  uint8_t b1 = read_byte();
  uint8_t b2 = read_byte();
  Wire.endTransmission();
  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;
  Wire.beginTransmission(deviceAddress);
  Wire.write(command);
  Wire.endTransmission(false);
  delay(1);
  Wire.requestFrom(deviceAddress, blockBufferLen, true);
  
  num_bytes = read_byte();
  num_bytes = constrain(num_bytes, 0, blockBufferLen - 2);
  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] = read_byte();
  }
  blockBuffer[x++] = read_byte(); // 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
  Wire.endTransmission();
  return num_bytes;
}

#define BUFF_SIZE 64
void drawIcon(const unsigned short* icon, int16_t x, int16_t y, int8_t width, int8_t height) {

  uint16_t  pix_buffer[BUFF_SIZE];   // Pixel buffer (16 bits per pixel)

  // Set up a window the right size to stream pixels into
  tft.setAddrWindow(x, y, x + width - 1, y + height - 1);

  // Work out the number whole buffers to send
  uint16_t nb = ((uint16_t)height * width) / BUFF_SIZE;

  // Fill and send "nb" buffers to TFT
  for (int i = 0; i < nb; i++) {
    for (int j = 0; j < BUFF_SIZE; j++) {
      pix_buffer[j] = pgm_read_word(&icon[i * BUFF_SIZE + j]);
    }
    tft.pushColors(pix_buffer, BUFF_SIZE);
  }

  // Work out number of pixels not yet sent
  uint16_t np = ((uint16_t)height * width) % BUFF_SIZE;

  // Send any partial buffer left over
  if (np) {
    for (int i = 0; i < np; i++) pix_buffer[i] = pgm_read_word(&icon[nb * BUFF_SIZE + i]);
    tft.pushColors(pix_buffer, np);
  }
}


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));

  Serial.print("Manufacture Date (D.M.Y): " );

  String formatted_date;
  int mdate = fetchWord(MFG_DATE);
  int mday = B00011111 & mdate;
  int mmonth = mdate >> 5 & B00001111;
  int myear = 1980 + (mdate >> 9 & B01111111);
  formatted_date += mday;
  formatted_date += ".";
  formatted_date += mmonth;
  formatted_date += ".";
  formatted_date += myear;
  Serial.println(formatted_date);
  int str_len = formatted_date.length() + 1; 
  char mfg_date[str_len];
  formatted_date.toCharArray(mfg_date, str_len);


  Serial.print("Serial Number: ");
  length_read = i2c_smbus_read_block(DJI_SERIAL, i2cBuffer, bufferLen);
  char *djiserial = i2cBuffer;
  Serial.println(djiserial); 

  Serial.print("Specification Info: ");
  Serial.println(fetchWord(SPEC_INFO));

  Serial.print("Cycle Count: " );
  uint8_t cycles = fetchWord(CYCLE_COUNT);
  Serial.println(cycles);

  Serial.print("Voltage: ");
  float voltage = (float)fetchWord(VOLTAGE) / 1000;
  Serial.println(voltage);

  Serial.print("Full Charge Capacity: " );
  Serial.println(fetchWord(FULL_CHARGE_CAPACITY));

  Serial.print("Remaining Capacity: " );
  Serial.println(fetchWord(REMAINING_CAPACITY));

  Serial.print("Relative Charge(%): ");
  uint8_t charge = fetchWord(RELATIVE_SOC);
  Serial.println(charge);


  Serial.print("Absolute Charge(%): ");
  Serial.println(fetchWord(ABSOLUTE_SOC));

  Serial.print("Minutes remaining for full charge: ");
  Serial.println(fetchWord(TIME_TO_FULL));

  Serial.print("Cell 1 Voltage: ");
  float cell1 = (float)fetchWord(CELL1_VOLTAGE)/1000;
  Serial.println(cell1);
  Serial.print("Cell 2 Voltage: ");
  float cell2 = (float)fetchWord(CELL2_VOLTAGE)/1000;
  Serial.println(cell2);
  char buffer[5];
  String v = dtostrf(voltage, 4, 2, buffer);
  String c1 = dtostrf(cell1, 4, 2, buffer);
  String c2 = dtostrf(cell2, 4, 2, buffer);
  String cells = v + " " + c1 + '/' + c2;
  str_len = cells.length() + 1; 
  char cellsV[str_len];
  cells.toCharArray(cellsV, str_len);


  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);
  float temp = tempk / 10.0 - 273.15;
  Serial.println(temp);

  Serial.print("Current (mA): " );
  Serial.println(fetchWord(CURRENT));

  Serial.println(".");

  
  drawIcon(dji_logo, (tft.width() - Width)/2,      0, Width, Height);

  tft.setTextSize(1);
  tft.setTextColor(TFT_BLACK, TFT_BLACK);

  tft.drawString("SERIAL", 3, 54, 1);
  tft.drawRightString(djiserial, 126, 54, 1);
  
  tft.drawString("PRODUCED", 3, 54+15, 1);
  tft.drawRightString(mfg_date, 126, 54+15, 1);
  
  tft.drawString("CYCLES", 3, 54+30, 1);b
  tft.drawNumber(cycles, 45, 54+30, 1);
  tft.drawString("TEMP", 70, 54+30, 1);
  tft.fillRect(102, 54+30 , 25, 10, TFT_BACKGROUND);
  tft.drawFloat(temp, 1, 102, 54+30, 1);
  
  tft.drawString("VOLTS", 3, 54+45, 1);
  tft.fillRect(40, 54+45 , 87, 10, TFT_BACKGROUND);
  tft.drawRightString(cellsV, 126, 54+45, 1);

  unsigned int batt_width = 110;
  unsigned int batt_height = 20;
  tft.drawRect(tft.width()/2 - batt_width/2, 130 , batt_width, batt_height, TFT_BLACK);
  tft.drawRect(tft.width()/2 + batt_width/2 - 1 , 130+batt_height/4 , 5, batt_height/2, TFT_BLACK);
  uint16_t color = TFT_RED;
  if (charge > 25) color = TFT_ORANGE;
  if (charge > 50) color = TFT_YELLOW;
  if (charge > 75) color = TFT_DARKGREEN;
  tft.fillRect(tft.width()/2 - batt_width/2 + 5, 130 + 2 , charge, batt_height-4, color);
  String pct = String(charge) + '%';
  str_len = pct.length() + 1; 
  char p[str_len];
  pct.toCharArray(p, str_len);
  tft.drawString(p,54,132, 2);

  delay(1000);
}

Buonasera, sto studiando. Una domanda...considerate che non sono preparatissimo. Visto che il valore che mi interessa leggere ed azzerare è nella locazione 0x17...qual'è il criterio per "correggerlo"? Spero di aver posto bene la domanda.
Curiosando sul web...ma capendoci poco...leggo che un tizio chiede se il cycle count della batteria possa essere resettato con 0x0041/0x0012...qualcuno può aiutarmi a capire?
Quello chw i capito è che...0x17 è una locazione di memoria che dovrei riuscire a leggere con un comando...ora come, invece, ci scrivo dentro?

Curiosando in rete, ho trovato questo documento che dovrebbe descrivere nel dettaglio il protocollo usato dalla tua batteria detto SBS (Smart Battery System).

L'indirizzo 0x17 però sembra essere di sola lettura, quindi non credo sia quello da usare per ottenere il tuo scopo, a meno che la tua batteria non segua alla lettera il protocollo e sia consentito anche di azzerare il "CycleCount"?

Ho trovato anche questa libreria, ma ancora una volta l'indirizzo 0x17 è indicato come di sola lettura e comunque non ci sono definiti metodi che consentirebbero di scrivere.

Intanto sempre grazie per l'attenzione. Avevo trovato anche io il documento e stavo cercando di capire nei limiti delle mie conoscenze. Ero partito dal semplice presupposto che riuscendo a leggere una locazione fosse possibile anche scrivere. Un pò come ho fatto in alcuni casi con gli HEX editor nei file in cui smanetto con successo. Ovviamente stavo in quel caso operando su un file estratto dal chip per modificarlo e succesivamente flasharlo nuovamente. Tutto ciò immaginavo fosse possibile con un codice Arduino che leggesse e quindi sovrascrivesse il valore. Ora sono...nel vicolo cieco. La possibilità di azzerare il cycle count esiste e vorrei capire come. Intanto proverò appena possibile questi programmini che interrogano il chip del BMS.

Se interessa ho trovato il seuente codice. Devo ancora testarlo. Ho bisogno solo di un informazione sul collegamento di Arduino. E' sufficente collegare SDA/SCL e GND per stabilire la comunicazione?

/*
**********************************
 * SMBus FOR 3DR SOLO with Arduino UNO R3
 * STAVROPOULOS
 * Code Version 0.02 beta
 * MUCH OF THIS CODE WAS COPIED FROM
 * https://github.com/PowerCartel/PackProbe/blob/master/PackProbe/PackProbe.ino
 * https://github.com/ArduPilot/PX4Firmware/blob/master/src/drivers/batt_smbus/batt_smbus.cpp
 * 
 **********************************
 **********************************
 * Configured for Arduino UNO R3
 * you will need to use external pull up resistors of 
 * 4.7k-ohm to pull the SDA and SCL lines up to 3.3v
 **********************************
 **********************************
 * CONFIGURE I2C/SERIAL ON ARDUINO
 **********************************
 */
//DEFINE SDA AND SCL PINS
  #define SCL_PIN 5                 //COMMUNICATION PIN 5 ON MEGA
  #define SCL_PORT PORTC
  #define SDA_PIN 4                 //COMMUNICATION PIN 6 ON MEGA
  #define SDA_PORT PORTC

//CONFIGURE I2C MODES
  #define I2C_TIMEOUT 100           //PREVENT SLAVE DEVICES FROM STRETCHING LOW PERIOD OF THE CLOCK INDEFINITELY AND LOCKING UP MCU BY DEFINING TIMEOUT
  //#define I2C_NOINTERRUPT 1       //SET TO 1 IF SMBus DEVICE CAN TIMEOUT
  //#define I2C_FASTMODE 1          //THE STANDARD I2C FREQ IS 100kHz.  USE THIS TO PERMIT FASTER UP TO 400kHz.
  //#define I2C_SLOWMODE 1            //THE STANDARD I2C FREQ IS 100kHz.  USE THIS TO PERMIT SLOWER, DOWN TO 25kHz.
  #define BAUD_RATE 9600
  #include <SoftI2CMaster.h>

/**********************************
 * CONFIGURE SERIAL LIBRARY
 **********************************/
  //#include <SoftwareSerial.h>
  //#include <Serial.h>
  #include <Wire.h>

/**********************************
 * DEFINE VARIABLES AND SMBus MAPPINGS
 **********************************/
  #define BATT_SMBUS_ADDR                     0x0B                ///< I2C address
  #define BATT_SMBUS_ADDR_MIN                 0x08                ///< lowest possible address
  #define BATT_SMBUS_ADDR_MAX                 0x7F                ///< highest possible address
//BUS MAPPINGS FROM DEV.3DR
  #define BATT_SMBUS_TEMP                     0x08                ///< temperature register
  #define BATT_SMBUS_VOLTAGE                  0x09                ///< voltage register
  #define BATT_SMBUS_REMAINING_CAPACITY       0x0f                ///< predicted remaining battery capacity as a percentage
  #define BATT_SMBUS_FULL_CHARGE_CAPACITY     0x10                ///< capacity when fully charged
  #define BATT_SMBUS_DESIGN_CAPACITY          0x18                ///< design capacity register
  #define BATT_SMBUS_DESIGN_VOLTAGE           0x19                ///< design voltage register
  #define BATT_SMBUS_SERIALNUM                0x1c                ///< serial number register
  #define BATT_SMBUS_MANUFACTURE_NAME         0x20                ///< manufacturer name
  #define BATT_SMBUS_MANUFACTURE_DATA         0x23                ///< manufacturer data
  #define BATT_SMBUS_MANUFACTURE_INFO         0x25                ///< cell voltage register
  #define BATT_SMBUS_CURRENT                  0x2a                ///< current register
  #define BATT_SMBUS_MEASUREMENT_INTERVAL_US  (1000000 / 10)      ///< time in microseconds, measure at 10hz
  #define BATT_SMBUS_TIMEOUT_US               10000000            ///< timeout looking for battery 10seconds after startup
  #define BATT_SMBUS_BUTTON_DEBOUNCE_MS       300                 ///< button holds longer than this time will cause a power off event
  
  #define BATT_SMBUS_PEC_POLYNOMIAL           0x07                ///< Polynomial for calculating PEC
  #define BATT_SMBUS_I2C_BUS                  PX4_I2C_BUS_EXPANSION
//BUS MAPPINGS FROM SMBus PROTOCOL DOCUMENTATION
#define BATTERY_MODE             0x03
#define CURRENT                  0x0A
#define RELATIVE_SOC             0x0D
#define ABSOLUTE_SOC             0x0E
#define TIME_TO_FULL             0x13
#define CHARGING_CURRENT         0x14
#define CHARGING_VOLTAGE         0x15
#define BATTERY_STATUS           0x16
#define CYCLE_COUNT              0x17
#define SPEC_INFO                0x1A
#define MFG_DATE                 0x1B
#define DEV_NAME                 0x21   // String
#define CELL_CHEM                0x22   // 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
//END BUS MAPPINGS
  
  #define bufferLen 32
  uint8_t i2cBuffer[bufferLen];

// standard I2C address for Smart Battery packs
  byte deviceAddress = BATT_SMBUS_ADDR;

void setup()
{

  //INITIATE SERIAL CONSOLE
    Serial.begin(BAUD_RATE);
    Serial.println(i2c_init());

  //SETUP I2C INPUT PINS
    //pinMode(27,INPUT_PULLUP);                             //use external pull up resistor instead
    //pinMode(28,INPUT_PULLUP);                             //use external pull up resistor instead

    Serial.flush();

    while (!Serial) {    
    ;                                                       //wait for Console port to connect.
    }

    Serial.println("Console Initialized");
  
    i2c_init();                                             //i2c_start initialized the I2C system.  will return false if bus is locked.
    Serial.println("I2C Inialized");
    scan();
}

int fetchWord(byte func)
{
    i2c_start(deviceAddress<<1 | I2C_WRITE);                //Initiates a transfer to the slave device with the (8-bit) I2C address addr.
                                                            //Alternatively, use i2c_start_wait which tries repeatedly to start transfer until acknowledgment received
    //i2c_start_wait(deviceAddress<<1 | I2C_WRITE);
    i2c_write(func);                                        //Sends a byte to the previously addressed device. Returns true if the device replies with an ACK.
    i2c_rep_start(deviceAddress<<1 | I2C_READ);             //Sends a repeated start condition, i.e., it starts a new transfer without sending first a stop condition.
    byte b1 = i2c_read(false);                              //i2c_read Requests to receive a byte from the slave device. If last is true, 
                                                            //then a NAK is sent after receiving the byte finishing the read transfer sequence.
    byte b2 = i2c_read(true);
    i2c_stop();                                             //Sends a stop condition and thereby releases the bus.
    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(BATT_SMBUS_MANUFACTURE_NAME, i2cBuffer, bufferLen);
    Serial.write(i2cBuffer, length_read);
    Serial.println("");
  
    Serial.print("Manufacturer Data: ");
    length_read = i2c_smbus_read_block(BATT_SMBUS_MANUFACTURE_DATA, i2cBuffer, bufferLen);
    Serial.write(i2cBuffer, length_read);
    Serial.println("");
  
    Serial.print("Manufacturer Info: ");
    length_read = i2c_smbus_read_block(BATT_SMBUS_MANUFACTURE_INFO, i2cBuffer, bufferLen);
    Serial.write(i2cBuffer, length_read);
    Serial.println("");
    
    Serial.print("Design Capacity: " );
    Serial.println(fetchWord(BATT_SMBUS_DESIGN_CAPACITY));
    
    Serial.print("Design Voltage: " );
    Serial.println(fetchWord(BATT_SMBUS_DESIGN_VOLTAGE));
  
    Serial.print("Serial Number: ");
    Serial.println(fetchWord(BATT_SMBUS_SERIALNUM));
    
    Serial.print("Voltage: ");
    Serial.println((float)fetchWord(BATT_SMBUS_VOLTAGE)/1000);
  
    Serial.print("Full Charge Capacity: " );
    Serial.println(fetchWord(BATT_SMBUS_FULL_CHARGE_CAPACITY));
    
    Serial.print("Remaining Capacity: " );
    Serial.println(fetchWord(BATT_SMBUS_REMAINING_CAPACITY));
  
    Serial.print("Temp: ");
    unsigned int tempk = fetchWord(BATT_SMBUS_TEMP);
    Serial.println((float)tempk/10.0-273.15);
  
    Serial.print("Current (mA): " );
    Serial.println(fetchWord(BATT_SMBUS_CURRENT));

    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("");

    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("Specification Info: ");
    Serial.println(fetchWord(SPEC_INFO));
   
    Serial.print("Cycle Count: " );
    Serial.println(fetchWord(CYCLE_COUNT));
  
    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("Current (mA): " );
    Serial.println(fetchWord(CURRENT));
      
    Serial.println(".");
    delay(5000);
}


...se almeno mi aiutate a farmi capire cosa fa il void scan, il cui risultato sul monitor seriale è una lunga lista di address da 1 a 127 (ox00 - 0x7F)...grazie a chi avrà al bontà di farlo.

Così a naso direi che fa una scansione di tutti gli indirizzi i2c e ti scrive ok se riceve risposta su qualche indirizzo.

Grazie, si piano piano ci sono arrivato. In pratica dovrebbe "andare" a cercare l'indirizzo della batteria.
Una domanda più hardware che software. La batteria in questione ha, ovviamente, i due pin SDA ed SCL ed il suo BMS interno. Ora è sufficente collegare i piedini SDA ed SCL, più il GND o serve altro? Immagino che il BMS sia alimentato dalla stessa batteria.

Bene, almeno per me, sono riuscito a collegarmi e leggere la batteria incluso il dato che mi interessava alla locazione 0x17. Ora chiedo aiuto, qui sono completamente inerme, su come potrebbe essere possibile resettare quel valore. Grazie

Ho trovato questo. Leggendo ne capisco la logica, ma non ho la preparazione necessaria per tramutarlo in codice per Arduino. Potreste aiutarmi a capire? Grazie


To write to a smart battery, you'll need to use an I2C or SMBus interface and a compatible host device (like an Arduino or microcontroller) to send commands and data. The process involves setting up the I2C/SMBus connection, defining the target address, specifying the register address (or command), and then sending the data. You'll also need to account for the PEC (Packet Error Checking) byte if required by the specific protocol or device.
Here's a more detailed breakdown:

  1. Establish the I2C/SMBus connection:
    Hardware Setup: Connect the host device's SDA (Serial Data) and SCL (Serial Clock) lines to the smart battery's corresponding pins.

Software Setup: Configure the host device's I2C/SMBus library (like the Wire library on Arduino) to communicate at the appropriate speed and address.

  1. Define the Target Address:

Smart Battery's Address: Smart batteries typically have an 7-bit address, and you'll need to find the correct address for your specific battery. The address might be 0x58 or 0x59 in some cases.

Host Device Configuration: Set the target address in the I2C/SMBus library configuration on the host device.

  1. Specify the Register Address (or SMBus Command):

Registers: The smart battery has internal registers that store information like voltage, current, state of charge, etc. You'll need to find the specific register address you want to write to based on the smart battery's datasheet.

SMBus Commands: In SMBus, instead of register addresses, you might use SMBus commands to access the appropriate registers.

  1. Send the Data:

Data Format: The data you send will depend on what you're trying to write (e.g., a new charge current limit, a new voltage target, etc.).

I2C/SMBus Write Function: Use the I2C/SMBus library's write function to send the data to the specified address and register.
PEC Byte (if required): Some SMBus protocols require a PEC byte for error checking. Ensure you calculate and include the PEC byte in your write operation if necessary.

Example (using a hypothetical scenario):
Let's say you want to set a new charge current limit for a smart battery with the address 0x58. You need to write the new current value to register address 0x02.

  1. Setup:
    Configure your I2C library on your Arduino (or other host device) for a 7-bit address of 0x58.
  2. Write:
    Use the I2C write function to send the following:
    Target address: 0x58 (or 0x58 if you are using an 8-bit address and need to right-shift)
    Register address: 0x02 (to write to the current register)
    Data (new current value in the appropriate format)
    PEC Byte (if required by your protocol)

Key Considerations:

Datasheet:
Always consult the smart battery's datasheet for specific instructions on register addresses, SMBus commands, and data formats.

Anche gpb01 mi ha abbandonato... :expressionless_face:

Non conosco queste batterie SBS ma a questo link c'e' una libreria (che non ho provato)
"SmartBatteryHack_ESP8266/Arduino/SBS at master · simonchen/SmartBatteryHack_ESP8266 · GitHub"
In quella libreria c'e' anche il comando di scrittura. NON è detto che poi funzioni sul registro 0x17
Sempre quel tizio la usa in questo esempio:
"SmartBatteryHack_ESP8266/Arduino/SmartBatteryHack at master · simonchen/SmartBatteryHack_ESP8266 · GitHub"
che non ho capito che caspita fa (fa pure robba con usb, boh) ma puoi vedere come usa la libreria. I "comandi" li elenca nell'esempio, comunque sai già che per te è 0x17.
Cosa devi scrivere poi non lo so. Non so se basta scrivere un byte a 0.

Presumo un qualcosa del genere:

#define SDA_PIN 4
#define SCL_PIN 5
#define CycleCount 0x17
#define BATTERY_ADDRESS 0x0B     // qui indirizzo della tua batteria 

SBS battery = SBS(BATTERY_ADDRESS, SDA_PIN, SCL_PIN);

void setup()
{
  Serial.begin(9600);
  uint8_t ret = battery.sbsWriteByte(CycleCount, 0x00);   // scrivo 0 in registro
  Serial.print("scrittura valore ritorno=");
  Serial.println(ret);
}
void loop{}

il ret dovrebbe essere 0 se non riesce ad accedere al registro, altrimenti il numero di byte scritti (quindi 1)

Esiste anche la sbsWriteInt() per scrivere un intero (2 byte però)