Stupid simple multi cell battery monitoring

Hi, I am not that great with programming but I know just enough to do the basics and understand most of the code I use as an example and modify it to fit my needs.

I am working on a super easy and simple solution to be able to read out the individual voltages of a multi-cell Lion battery pack. I have been looking around and searching for different solutions on the googler and found a few interesting things but it usually ends up with the project being abandoned or into a more complex board/design/function that I am looking for.

Basically I have lots of battery packs with 28 18650 individual cells in a 7s4p configuration and want to monitor and read out the voltage level of the 7s cells for each pack. Each pack already has a BMS and cell balancing in an off the shelf module.

With that in mind I was able to find many off the shelf modules or AFE (Analog Front End) that have this function built and have been developed into some very neat projects to completion but most if not all of these are all overkill with additional features I do not desire like Cell balancing and High/Low voltage cuttoffs.

Some of the examples are:

MAX14921/MAX14920
MC33771
LTC6802/LTC6803/LTC6806...
AD7284
bq293x0 series

All of the above I have ordered and have now to do prototyping and proof of concept. I started with the AD7284 and got stuck with trying to work out the code for that. The MC33771 and the LTC devices are waiting for additional components to wire up. I have not started with the bq293x0 series of AFE but that will be next and currently I am working on the MAX1491 AFE and did find someone (felix) that did some work with arduino and the device but was also incomplete code but at least it is a start.

The repository is located: GitHub - felixekman/MAX14921-Arduino: MAX14921 Library and example for Arduino

Everything seems to be complete with the code but will never compile correctly and unlike most of the time when I can figure it out with the googler.... this one needs some special attention... I will post the code here in its complete original form (split up) if someone can look it over and help me out with simplifying it to remove the unneeded functions and most of all figuring out how to make it work. The only basic function of the project is to read the individual 4p cell voltages out via SPI or i2c to another application or device like an external LCD via Arduino or Raspberry pi.

I thank you for your time and your support and appreciate all the help I can get.

sorry, I was going to follow up with additional posts with the code and the errors but I can only post every 5 minutes.

/*
  MAX14921.h - Library for reading from a MAX14921.
  Created by Felix
*/

#include "max14921.h"
#include "Arduino.h"
#include <SPI.h>

long ticks = 0;

/**-----------------------------------------------------------------------------
 *
 * Default delay timeouts
 *
 *------------------------------------------------------------------------------
 */
#define   MD_AK35_LEVEL_SHIFTING_DELAY_MAX   50   // us
#define   MD_AK35_T_SETTLING_TIME_MAX        10   // us

/**-----------------------------------------------------------------------------
 *
 * SPI bit defines
 *
 *------------------------------------------------------------------------------
 */
#define   MD_AK35_SPI_SAMPLB_ENABLE     0x04
#define   MD_AK35_SPI_SAMPLB_DISABLE    0x00
#define   MD_AK35_SPI_DIAG_ENABLE       0x02
#define   MD_AK35_SPI_DIAG_DISABLE      0x00
#define   MD_AK35_SPI_LOPW_ENABLE       0x01
#define   MD_AK35_SPI_LOPW_DISABLE      0x00
#define   MD_AK35_SPI_TXSEL_DIRECT      0x08
#define   MD_AK35_SPI_TXSEL_BUFFERED    0x18

#define   MD_AK35_SPI_TXSEL_1           0x40
#define   MD_AK35_SPI_TXSEL_2           0x20
#define   MD_AK35_SPI_TXSEL_3           0x60

#define   MD_AK35_SPI_CELLSEL_00   0x80
#define   MD_AK35_SPI_CELLSEL_01   0xC0
#define   MD_AK35_SPI_CELLSEL_02   0xA0
#define   MD_AK35_SPI_CELLSEL_03   0xE0
#define   MD_AK35_SPI_CELLSEL_04   0x90
#define   MD_AK35_SPI_CELLSEL_05   0xD0
#define   MD_AK35_SPI_CELLSEL_06   0xB0
#define   MD_AK35_SPI_CELLSEL_07   0xF0
#define   MD_AK35_SPI_CELLSEL_08   0x88
#define   MD_AK35_SPI_CELLSEL_09   0xC8
#define   MD_AK35_SPI_CELLSEL_10   0xA8
#define   MD_AK35_SPI_CELLSEL_11   0xE8
#define   MD_AK35_SPI_CELLSEL_12   0x98
#define   MD_AK35_SPI_CELLSEL_13   0xD8
#define   MD_AK35_SPI_CELLSEL_14   0xB8
#define   MD_AK35_SPI_CELLSEL_15   0xF8

/**-----------------------------------------------------------------------------
 *
 * Lookup table for quick access to cell select bits needed for SPI command
 *
 *------------------------------------------------------------------------------
 */
uint8_t gMD_AK35_CellSelect_Table[ 16 ] =
{
  MD_AK35_SPI_CELLSEL_00,
  MD_AK35_SPI_CELLSEL_01,
  MD_AK35_SPI_CELLSEL_02,
  MD_AK35_SPI_CELLSEL_03,
  MD_AK35_SPI_CELLSEL_04,
  MD_AK35_SPI_CELLSEL_05,
  MD_AK35_SPI_CELLSEL_06,
  MD_AK35_SPI_CELLSEL_07,
  MD_AK35_SPI_CELLSEL_08,
  MD_AK35_SPI_CELLSEL_09,
  MD_AK35_SPI_CELLSEL_10,
  MD_AK35_SPI_CELLSEL_11,
  MD_AK35_SPI_CELLSEL_12,
  MD_AK35_SPI_CELLSEL_13,
  MD_AK35_SPI_CELLSEL_14,
  MD_AK35_SPI_CELLSEL_15
};

/**-----------------------------------------------------------------------------
 *
 * Current object parameters
 *
 *------------------------------------------------------------------------------
 */
typedef struct {
  uint8_t              tmrId;

  //MF_AFWRK_QUEUENODE_T     thdQNodes[ MD_AK35_THREAD_Q_SIZE ];    /**< Queue nodes  */
  //MF_AFWRK_QUEUE_T         thdQ;                                  /**< Thread queue */
  //MD_AK35_STATE_T          curState;

  uint8_t          acqCellNumber;
  int         acqCellSampleTmrValueMsec;
  int         acqCellRepeatTmrValueMsec;
  int         acqCellSettlingTimeUsec;
  bool        acqScanContinuous;
  bool        acqT1IsEnabled;
  bool        acqT2IsEnabled;
  bool        acqT3IsEnabled;
  int         acqT1SettlingTimeUsec;
  int         acqT2SettlingTimeUsec;
  bool        acqT3SettlingTimeUsec;
  //MI_CLI_REASON_T   acqInterface;

  uint8_t   spiBalanceC01_C08;
  uint8_t   spiBalanceC09_C16;
  uint8_t   spiSamplB;
  uint8_t   spiDiag;
  uint8_t   spiLoPw;

  long   calParErrTmrValueMsec;

  //MD_AK35_USERCFG_T   usrCfg;

} MD_AK35_INSTANCE_T;

/**-----------------------------------------------------------------------------
 *
 * Used to manage the device object.
 *
 *------------------------------------------------------------------------------
 */
MD_AK35_INSTANCE_T   MD_AK35_obj;

int readvalue;

max14921::max14921(int ADC_CS_pin, int CS_pin, int SAMPLPIN_pin)
{
    pinMode(ADC_CS_pin, OUTPUT);
    pinMode(CS_pin, OUTPUT);
    pinMode(SAMPLPIN_pin, OUTPUT);

    // disable device to start with
    digitalWrite(ADC_CS_pin, HIGH);
    digitalWrite(CS_pin, HIGH);
    digitalWrite(SAMPLPIN_pin, HIGH);

    SPI.setClockDivider( SPI_CLOCK_DIV8 ); // slow the SPI bus down
    SPI.setBitOrder(MSBFIRST);
    SPI.setDataMode(SPI_MODE0);    // SPI 0,0 as per MCP330x data sheet
    SPI.begin();

    _ADC_CS_pin = ADC_CS_pin;
    _CS_pin = CS_pin;
    _SAMPLPIN_pin = SAMPLPIN_pin;

    //
    // Setup initial parameters of the AL38 object
    //
    MD_AK35_obj.acqCellNumber             = 16;
    MD_AK35_obj.acqCellSampleTmrValueMsec = 4;   // ms
    MD_AK35_obj.acqCellRepeatTmrValueMsec = 6;   // ms
    MD_AK35_obj.acqCellSettlingTimeUsec   = MD_AK35_T_SETTLING_TIME_MAX;   // us

    MD_AK35_obj.acqScanContinuous = false;

    MD_AK35_obj.acqT1IsEnabled        = false;
    MD_AK35_obj.acqT2IsEnabled        = false;
    MD_AK35_obj.acqT3IsEnabled        = false;
    MD_AK35_obj.acqT1SettlingTimeUsec = MD_AK35_T_SETTLING_TIME_MAX;   // us
    MD_AK35_obj.acqT2SettlingTimeUsec = MD_AK35_T_SETTLING_TIME_MAX;   // us
    MD_AK35_obj.acqT3SettlingTimeUsec = MD_AK35_T_SETTLING_TIME_MAX;   // us

  //  MD_AK35_obj.acqInterface    = MI_CLI_REASON_EXECUTE_USBHID;

    MD_AK35_obj.spiBalanceC01_C08 = 0x00;
    MD_AK35_obj.spiBalanceC09_C16 = 0x00;
    MD_AK35_obj.spiSamplB         = MD_AK35_SPI_SAMPLB_DISABLE;
    MD_AK35_obj.spiDiag           = MD_AK35_SPI_DIAG_DISABLE;
    MD_AK35_obj.spiLoPw           = MD_AK35_SPI_LOPW_DISABLE;

    MD_AK35_obj.calParErrTmrValueMsec = 40;
}

// initialize the library with the numbers of the interface pins: (SPI on Arduino UNO as standard)
// set up the number of cells used: Choice between 3 and 16 cells total. This is used to calculate pack voltage
void max14921::SetCellNumber(uint8_t cellNum)
{
  MD_AK35_obj.acqCellNumber = cellNum;
}
// set up the battery chemestry used: LiPo is standard sets parimeters for vMax/vMin per cell.
//LiPo - vMin per cell = 3.600v vMax per cell = 4.200v

// Enable balancing of cells - Option - ON/OFF
void max14921::SetBalancing(bool bal)
{
}

// Enable open-wire detection - Option - ON/OFF
void max14921::SetOpenWireDetection(bool openwire)
{
}

// Set Sample time: Option in ms. Standard 4 ms
void max14921::SetSampleTime(int sampletime)
{
  MD_AK35_obj.acqCellSampleTmrValueMsec = sampletime;
}

// Set Settling Time in us. Standard 50 us.
void max14921::SetSettlingTime(int settlingtime)
{
  MD_AK35_obj.acqCellSettlingTimeUsec = settlingtime;
}

// Set Repeat Time in ms. Standard 10 ms.
void max14921::SetRepeatTime(int repeattime)
{
  MD_AK35_obj.acqCellRepeatTmrValueMsec = repeattime;
}


/**-----------------------------------------------------------------------------
 *
 * Read 2 bytes from device using dummy data
 *
 * @return
 * int : 2 bytes of SPI Rx Data
 *
 * @constraints
 * Standard
 *
 *------------------------------------------------------------------------------
 */
int max14921::MAX11163_ReadData16( void )
{
  int adcData = 0;
  int spiData = 0;
  int hi = 0, lo = 0;

  digitalWrite(_ADC_CS_pin, LOW); // Enable device
  //digitalWrite (SELPIN, LOW); // Select adc

  // 100ns delay

  spiData = SPI.transfer(0x00);  // dummy read

  spiData = SPI.transfer(0x00);

  hi = spiData;

  spiData = SPI.transfer(0x00);
  lo = spiData;

  digitalWrite(_ADC_CS_pin, HIGH); // turn off device

  adcData= hi * 256 + lo;
  return( adcData );
}

/**-----------------------------------------------------------------------------
 *
 * Transfers 3 SPI data bytes and returns 3 bytes of read data
 *
 * @param byte1   in : SPI Data Tx Byte 1
 * @param byte2   in : SPI Data Tx Byte 2
 * @param byte3   in : SPI Data Tx Byte 3
 *
 * @return
 * MCS_U32_T : 24 bits of SPI Rx Data
 *
 * @constraints
 * Standard
 *
 *------------------------------------------------------------------------------
 */
long max14921::MD_AK35_SpiTransfer24(uint8_t byte1, uint8_t byte2, uint8_t byte3)
{
  long ak35Data = 0;
  byte spiData  = 0;

  spiData    = SPI.transfer(byte1);
  ak35Data   = spiData;
  ak35Data <<= 8;

  spiData    = SPI.transfer(byte2);
  ak35Data  |= spiData;
  ak35Data <<= 8;

  spiData    = SPI.transfer(byte3);
  ak35Data  |= spiData;

  return( ak35Data );
}


  }
}

“ I am working on a super easy and simple solution to be able to read out the individual voltages of a multi-cell Lion battery pack. ”

You can use DPDT relays to do this. About $3 each.

One relay on at a time.

rest of the code:

/**-----------------------------------------------------------------------------
 *
 * Performs readings of cell data including -
 *   - Disable SAMPL with SAMPL settle timing
 *   - Send cell selection SPI command to AK35
 *   - Delay AOUT settle time
 *   - Read ADC with timing constraints
 *
 * @param cellNum            in : Number of cells to read (1-16)
 * @param pAdcCellVoltage   out : Pointer to array of cell ADC readings
 * @param pSpiRxData        out : Pointer to array of SPI Rx data for AK35
 *                                  cell selection requests
 *
 * @constraints
 * Standard
 *
 *------------------------------------------------------------------------------
 */
void max14921::MD_AK35_Cmd_AcquireCell(uint8_t cellNum,
                              int *pAdcCellVoltage,
                              long *pSpiRxData )
{
  byte cellIndex = 0;


  //
  // Disable SAMPL and delay for data to be stored in AK35
  //
  digitalWrite (_SAMPLPIN_pin, LOW); // Sampl_Disable
  delayMicroseconds(MD_AK35_LEVEL_SHIFTING_DELAY_MAX);

  if ( cellNum != 0 )
  {
    //
    // Read x cell voltages
    //
    for ( cellIndex = (cellNum-1) ; cellIndex >= 0 ; cellIndex-- )
    {
      uint8_t spiCmd=0;

      spiCmd  = gMD_AK35_CellSelect_Table[ cellIndex ];
      spiCmd |= ( MD_AK35_obj.spiSamplB | MD_AK35_obj.spiDiag | MD_AK35_obj.spiLoPw );

      //
      // Send command to AK35
      //
      digitalWrite (_SAMPLPIN_pin, LOW); // chip-select MAX14921

      pSpiRxData[ cellIndex ] = MD_AK35_SpiTransfer24( MD_AK35_obj.spiBalanceC01_C08,
                                                       MD_AK35_obj.spiBalanceC09_C16,
                                                       spiCmd );
      digitalWrite (_SAMPLPIN_pin, HIGH); // chip-select MAX14921

      //
      // HW bug fix for 1st silicon
      //   Need to write balance bits twice for C01 to work correctly!
      //
      digitalWrite (_CS_pin, LOW); // chip-select MAX14921
      pSpiRxData[ cellIndex ] = MD_AK35_SpiTransfer24( MD_AK35_obj.spiBalanceC01_C08,
                                                       MD_AK35_obj.spiBalanceC09_C16,
                                                       spiCmd );
      digitalWrite (_CS_pin, HIGH); // chip-select MAX14921


      //
      // Delay required for command to be processed and data valid on AOUT of AK35
      //
      //MD_AK35_Delay_uSec( MD_AK35_obj.acqCellSettlingTimeUsec );
      delayMicroseconds(MD_AK35_obj.acqCellSettlingTimeUsec);

      //
      // Read AOUT Data with ADC
      //
      pAdcCellVoltage[ cellIndex ] = MAX11163_ReadData16();
    }
  }
}

/**-----------------------------------------------------------------------------
 *
 * Scans thru requested cells and reads ADC data and returns data to PC
 *
 * @param calibrationData   in : TRUE  - Data read is Parasitic Error Cal data
 *                               FALSE - Data is normal cell voltage data
 *
 * @constraints
 * Standard
 *
 *------------------------------------------------------------------------------
 */
void max14921::MD_AK35_ScanCell(bool calibrationData)
{
  int adcVal[ 16 ]    = { 0 };
  long spiRxData[ 16 ] = { 0 };

  //
  // If Cell Voltages requested, read and return that data
  //   0xFF = not requested
  //
  if ( MD_AK35_obj.acqCellNumber != 0 )
  {
    MD_AK35_Cmd_AcquireCell( MD_AK35_obj.acqCellNumber,
                             &adcVal[0],
                             &spiRxData[0] );

      //
      // Send Cell voltage data back along UART Pipe
      //
      long index          =   0;
      char      respStr[ 160 ] = { 0 };


      sprintf( respStr, "ak35Resp scan cell %02X %06X %04X",
                        MD_AK35_obj.acqCellNumber,
                        spiRxData[0],
                        adcVal[0] );

      for ( index = 1 ; index < MD_AK35_obj.acqCellNumber ; index++ )
      {
        sprintf( respStr, "%s %04X",
                          respStr,
                          adcVal[ index ] );
        Serial.println(respStr);
      }

      //MI_CLI_WriteTxt( respStr );
  }
}

larryd:
“ I am working on a super easy and simple solution to be able to read out the individual voltages of a multi-cell Lion battery pack. ”

You can use DPDT relays to do this. About $3 each.

One relay on at a time.

I would need about 420 relays...

libraries\MAX14921-Arduino-master\max14921.cpp.o (symbol from plugin): In function `max14921::max14921(int, int, int)':

(.text+0x0): multiple definition of `max14921::max14921(int, int, int)'

sketch\sketch_dec12a.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\MAX14921-Arduino-master\max14921.cpp.o (symbol from plugin): In function `max14921::max14921(int, int, int)':

(.text+0x0): multiple definition of `MD_AK35_obj'

sketch\sketch_dec12a.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\MAX14921-Arduino-master\max14921.cpp.o (symbol from plugin): In function `max14921::max14921(int, int, int)':

(.text+0x0): multiple definition of `max14921::max14921(int, int, int)'

sketch\sketch_dec12a.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\MAX14921-Arduino-master\max14921.cpp.o (symbol from plugin): In function `max14921::max14921(int, int, int)':

(.text+0x0): multiple definition of `max14921::SetCellNumber(unsigned char)'

sketch\sketch_dec12a.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\MAX14921-Arduino-master\max14921.cpp.o (symbol from plugin): In function `max14921::max14921(int, int, int)':

(.text+0x0): multiple definition of `max14921::SetBalancing(bool)'

sketch\sketch_dec12a.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\MAX14921-Arduino-master\max14921.cpp.o (symbol from plugin): In function `max14921::max14921(int, int, int)':

(.text+0x0): multiple definition of `max14921::SetOpenWireDetection(bool)'

sketch\sketch_dec12a.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\MAX14921-Arduino-master\max14921.cpp.o (symbol from plugin): In function `max14921::max14921(int, int, int)':

(.text+0x0): multiple definition of `max14921::SetSampleTime(int)'

sketch\sketch_dec12a.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\MAX14921-Arduino-master\max14921.cpp.o (symbol from plugin): In function `max14921::max14921(int, int, int)':

(.text+0x0): multiple definition of `max14921::SetSettlingTime(int)'

sketch\sketch_dec12a.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\MAX14921-Arduino-master\max14921.cpp.o (symbol from plugin): In function `max14921::max14921(int, int, int)':

(.text+0x0): multiple definition of `max14921::SetRepeatTime(int)'

sketch\sketch_dec12a.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\MAX14921-Arduino-master\max14921.cpp.o (symbol from plugin): In function `max14921::max14921(int, int, int)':

(.text+0x0): multiple definition of `max14921::MAX11163_ReadData16()'

sketch\sketch_dec12a.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\MAX14921-Arduino-master\max14921.cpp.o (symbol from plugin): In function `max14921::max14921(int, int, int)':

(.text+0x0): multiple definition of `max14921::MD_AK35_SpiTransfer24(unsigned char, unsigned char, unsigned char)'

sketch\sketch_dec12a.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\MAX14921-Arduino-master\max14921.cpp.o (symbol from plugin): In function `max14921::max14921(int, int, int)':

(.text+0x0): multiple definition of `max14921::MD_AK35_Cmd_AcquireCell(unsigned char, int*, long*)'

sketch\sketch_dec12a.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\MAX14921-Arduino-master\max14921.cpp.o (symbol from plugin): In function `max14921::max14921(int, int, int)':

(.text+0x0): multiple definition of `gMD_AK35_CellSelect_Table'

sketch\sketch_dec12a.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\MAX14921-Arduino-master\max14921.cpp.o (symbol from plugin): In function `max14921::max14921(int, int, int)':

(.text+0x0): multiple definition of `max14921::MD_AK35_ScanCell(bool)'

sketch\sketch_dec12a.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\MAX14921-Arduino-master\max14921.cpp.o (symbol from plugin): In function `max14921::max14921(int, int, int)':

(.text+0x0): multiple definition of `readvalue'

sketch\sketch_dec12a.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

libraries\MAX14921-Arduino-master\max14921.cpp.o (symbol from plugin): In function `max14921::max14921(int, int, int)':

(.text+0x0): multiple definition of `ticks'

sketch\sketch_dec12a.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

“ I would need about 420 relays...”

Not a good idea then :wink:

what is the correct definition `max14921::max14921(int, int, int)'?

Not 7 relays?

As you have found, this is not a trivial problem. Big battery packs carry dangerous amounts of energy.

But the error messages you posted are trivial. Look at the first 3 lines. It gives the name of a function in the library, says you defined it multiple times and then says it was first defined in your sketch code.

The definition is the actual code that makes the function work. The declaration is just the headline which gives the name of the function and the types of its parameters and return value. Arduino creates declarations automatically so you don't see as many of those.

That looks like you copied the library code into your sketch so it is trying to compile two copies of the code. Delete the second copy in your code. Your sketch should only have definitions for loop(), setup() and maybe a few other functions.

MorganS:
Not 7 relays?

As you have found, this is not a trivial problem. Big battery packs carry dangerous amounts of energy.

But the error messages you posted are trivial. Look at the first 3 lines. It gives the name of a function in the library, says you defined it multiple times and then says it was first defined in your sketch code.

The definition is the actual code that makes the function work. The declaration is just the headline which gives the name of the function and the types of its parameters and return value. Arduino creates declarations automatically so you don’t see as many of those.

That looks like you copied the library code into your sketch so it is trying to compile two copies of the code. Delete the second copy in your code. Your sketch should only have definitions for loop(), setup() and maybe a few other functions.

It would not be just 7 relays, in total it would be over 400+ relays.

I am tracking I think.

wait… duh… what am I thinking…

Looking back at the repository I just see the source and a header but I am not sure where or how I came up with that example sketch. I just answered most of my questions.

I just need to come up with my own sketch. so the question is how do I start.

So I would start by including the header files right?

#include "max14921.h"
#include "Arduino.h"
#include <SPI.h>

For the setup()
then use a function to set up the AFE and the MCU like setting the pins for SPI?

I assume I will need to define some variables to store values in to call them out later right?

Then specify and send values to the AFE to it up to read values from the registers?

then for the loop()

I would read the values from the registers of the AFE into the variables for which I can then manipulate and read out or send over serial?


how do you think I should start? I usually have some kind of example code to work from but never from scratch.

You said 28 cells. How can that require more than 28 DPST relays? Since the BMS probably doesn't have 28 wires inside the pack, it is likely wired with "balance" connections on every 4P group. So only 7 wires (plus ground) to the BMS. 7 relays.

There is an "examples" folder in the github repository. Start with one of those. The folder structure is not quite right. You may need to copy the .ino files to your main Arduino sketch folder before the IDE will open them.

7 relays per pack and around 50-60 packs total…

This has been such a great learning opportunity, I starting to get more and more familiar with programming for arduino by the minute.

yes, now its coming back to me. For some reason I knew there was an example but forgot where I saw it and well it was right under my nose. I remember when I found it originally there was still a problem with it still and needed to dig more into it but I am just so close but just dont know how to proceed with the example.

The example has 2 sketches, MAX14921 and MAX14921_Setup. I have seen setup files before but not exactly sure why or what it is used for. When I load up the MAX14921 sketch it also loads the setup file with it and looking at it they both contain setup() and loop() sections and also contain most of the same code which when compiled gives me a redefinition error. That I do understand now why the error but why have the functions in both? Thats problem #1 I dont understand. I looks like to me that they are not needed and in fact the MAX14921 sketch is not needed as everything in that is also in the setup sketch right?

Next is the setup sketch, I already see a problem with the syntax of how the setup() is done. It looks like the Serial.print(function) are outside the {} brackets or is that supposed to be that way?

Next is the functions themselves not being declared correctly or used correctly? If thats what they are, I am still learning. For example :Serial.print(CellVoltage.1); is saying not declared in this scope and from what I understand and looking into the source or header file I dont see a function for (CellVoltage.1) right? or am I missing something?

here is the example:

MAX14921_Setup:

#include <max14921.h>
#include <HardwareSerial.h>
#include <SPI.h>

// for Uno
//#define SELPIN 10    // chip-select
//#define DATAOUT 11   // MOSI 
//#define DATAIN 12    // MISO 
//#define SPICLOCK 13  // Clock 

// for Nano
#define SELPIN 10    // chip-select MAX11163
#define DATAOUT 11   // MOSI 
#define DATAIN 12    // MISO 
#define SPICLOCK 13  // Clock 

#define SELMAXPIN 10 // chip-select MAX14921
#define SAMPLPIN 10 // SAMPLE pin MAX14921

#define CELLS 16 //Number of cells active
#define CHEMISTRY LiPo //LiPo, LIFe : LiPo: 4.2V full 3.0V minimum, LiFe: 3.6V full 2.8V minimum
#define BALANCING true // Balancing all cells
#define OPENWIRE false // Dectection of Open-Wire
#define SAMPLETIME 40




//max14921 _max14921(SELPIN, SELMAXPIN, SAMPLPIN);

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


Serial.print(packBalancing.status); // On, Battery pack balancing is active
Serial.print(packBalancing.status); // Off, Battery pack balancing is NOT active

Serial.print(OpenWire.status); // Yes, Warning if open-wire condtion is met
Serial.print(OpenWire.status); // No, Status if open wire is not met. I.e. no problem

Serial.print(packVoltage.procentage); // The procentage of the power left in the pack. Ex. 8S LiPo battery Vmax. 33,6v Vmin 28,8v

Serial.print(packVoltage.total); //The current Voltage value of the entire battery pack i.e. 33.251v cell 1-16

Serial.print(CellVoltage.1); // The current Voltage value of Cell 1 i.e. 3.151v
Serial.print(CellVoltage.2); // The current Voltage value of Cell 2 i.e. 3.151v
Serial.print(CellVoltage.3); // The current Voltage value of Cell 3 i.e. 3.151v
Serial.print(CellVoltage.4); // The current Voltage value of Cell 4 i.e. 3.151v
Serial.print(CellVoltage.5); // The current Voltage value of Cell 5 i.e. 3.151v
Serial.print(CellVoltage.6); // The current Voltage value of Cell 6 i.e. 3.151v
Serial.print(CellVoltage.7); // The current Voltage value of Cell 7 i.e. 3.151v
Serial.print(CellVoltage.8); // The current Voltage value of Cell 8 i.e. 3.151v
Serial.print(CellVoltage.9); // The current Voltage value of Cell 9 i.e. 3.151v
Serial.print(CellVoltage.10); // The current Voltage value of Cell 10 i.e. 3.151v
Serial.print(CellVoltage.11); // The current Voltage value of Cell 11 i.e. 3.151v
Serial.print(CellVoltage.12); // The current Voltage value of Cell 12 i.e. 3.151v
Serial.print(CellVoltage.13); // The current Voltage value of Cell 13 i.e. 3.151v
Serial.print(CellVoltage.14); // The current Voltage value of Cell 14 i.e. 3.151v
Serial.print(CellVoltage.15); // The current Voltage value of Cell 15 i.e. 3.151v
Serial.print(CellVoltage.16); // The current Voltage value of Cell 16 i.e. 3.151v


void loop() {
  _max14921.MD_AK35_ScanCell(false);
}

Then MAX14921

#include <max14921.h>
#include <HardwareSerial.h>
#include <SPI.h>

// for Uno
//#define SELPIN 10    // chip-select
//#define DATAOUT 11   // MOSI 
//#define DATAIN 12    // MISO 
//#define SPICLOCK 13  // Clock 

// for Nano
#define SELPIN 10    // chip-select MAX11163
#define DATAOUT 11   // MOSI 
#define DATAIN 12    // MISO 
#define SPICLOCK 13  // Clock 

#define SELMAXPIN 10 // chip-select MAX14921
#define SAMPLPIN 10 // SAMPLE pin MAX14921  

max14921 _max14921(SELPIN, SELMAXPIN, SAMPLPIN);

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

void loop() {
  _max14921.MD_AK35_ScanCell(false);
}

As you can see there are quit a few item redefined from one to the other. That I know is going to be a problem. I just cant seem to wrap my head around the rest of it, it kinda makes sense… I can follow along and figure out what it is doing but cant seem to grasp the functions.

Hey tedrobe, did you have any luck with the MAX14921 code? I've got a MAX14921 evaluation board and am looking to use it on my 16s LiFePo4 pack... Ill probably aim for running the code on an ESP8266 or ESP32, in place of the STM32 supplied on board (I don't have the source code for that anyway, so it looks easier to splice a new processor in)
Cheers