Kia Niro BMS

bigmotherwhale
Posts: 107
Joined: Tue Sep 22, 2020 6:48 pm
Has thanked: 15 times
Been thanked: 22 times

Re: Kia Niro BMS

Post by bigmotherwhale »

Does anyone have an idea if this code will work with the Hyundai / Kia hybrid packs?

Edit: looking deeper it seems that it will

could anyone tell me whether you can use the BMS master and the CAN messages alone instead of directly connecting to the slaves?
Thanks
maciek16c
Posts: 19
Joined: Mon May 01, 2023 7:40 pm
Location: Poland
Been thanked: 9 times

Re: Kia Niro BMS

Post by maciek16c »

I think it should be possible to use stock BMS. Looks like a lot of CAN messages are the same or similar between different Hyundai / Kia models and i have Hyundai Santa-Fe PHEV battery working using Savvycan script or zombieverter (there are still some obd-2 errors, but probably not critical)
bigmotherwhale
Posts: 107
Joined: Tue Sep 22, 2020 6:48 pm
Has thanked: 15 times
Been thanked: 22 times

Re: Kia Niro BMS

Post by bigmotherwhale »

I already went ahead and bought the pack its a Hyundai IONIQ PHEV, its nice to know that it can be done, would you mind sharing your code? Thanks
maciek16c
Posts: 19
Joined: Mon May 01, 2023 7:40 pm
Location: Poland
Been thanked: 9 times

Re: Kia Niro BMS

Post by maciek16c »

I added short description of my battery in my thread: viewtopic.php?p=62256
bigmotherwhale
Posts: 107
Joined: Tue Sep 22, 2020 6:48 pm
Has thanked: 15 times
Been thanked: 22 times

Re: Kia Niro BMS

Post by bigmotherwhale »

Hello, im trying to use your code and an Arduino to patch into a HEV BMS from a niro as i have been unable to find the CAN commands for the contactors, I have connected to the spi and i think im getting them to talk but the board design is different to that of the CMUs, It has 8X max17845 chips onboard.
I tried playing with the code but i dont know what im doing, and I keep getting "error byte cleared" in serial.
Is it possible to get this to work? could i get some help.
Thank you
Attachments
DSC05374.JPG
DSC05372.JPG
User avatar
bexander
Posts: 835
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Kia Niro BMS

Post by bexander »

Yes, this seem to be a integrated BMS, interesting.
So you have connected via SPI to the onborad MAX17841, correct?

My SW is hard coded to 12 slaves with 4+4 cells connected at pos 1-4 and 6-9. This will have to be changed to 8 slaves with 9 cells connected.
The number of slaves also determines the value of check byte so this have to match.

Please provide a serial print out of your results so far, helps a lot for me to determine your progress.
User avatar
bexander
Posts: 835
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Kia Niro BMS

Post by bexander »

bexander wrote: Thu Oct 26, 2023 4:17 am The number of slaves also determines the value of check byte so this have to match.
I ment the alive-counter. 0x0C with 12 slaves, 0x08 with 8 slaves. Will need to be changed in 3 places in the SW, daisyChainInit(), writeAllSlaves() and readAllSlaves().
That should get you through the setup.
bigmotherwhale
Posts: 107
Joined: Tue Sep 22, 2020 6:48 pm
Has thanked: 15 times
Been thanked: 22 times

Re: Kia Niro BMS

Post by bigmotherwhale »

Thankyou this is exactly what i needed to know.
Yes I have connected to the SPI of the MAX17841 and pulled up the respective pins.
So i have edited 0x0c to 0x08 in the code.
does this need to be edited too?
"* Reads data from slaves and stores in arrays *
**********************************************/
void readData()
{
for(int i=0; i<4; i++)
{
readAllSlaves((0x20 + i), true); // Read CELL 1-4 of all slaves
}
for(int i=0; i<4; i++)
{
readAllSlaves((0x25 + i), true); // Read CELL 5-8 of all slaves"

and this
* Stores measured cell voltage data in cellVoltage array *
*********************************************************/
void storeCellVoltage(uint8_t dataRegister, uint8_t readRegisterData[29])
{
if((dataRegister >= 0x20) && (dataRegister <= 0x23)) // Cell voltage registers 1-4
{
uint8_t cellNumberOffset = dataRegister - 0x20;

for(int i=0; i<12; i++)
{
uint16_t measVoltage = ((readRegisterData[25-i*2] << 8) + readRegisterData[24-i*2]);
measVoltage = (measVoltage >> 2) * (uint32_t)5000 / 0x3FFF;
cellVoltage[i*8 + cellNumberOffset] = measVoltage;
}
}

if((dataRegister >= 0x25) && (dataRegister <= 0x28)) // Cell voltage registers 5-8
{
uint8_t cellNumberOffset = dataRegister - 0x20 - 1;

for(int i=0; i<12; i++)
{
uint16_t measVoltage = ((readRegisterData[25-i*2] << 8) + readRegisterData[24-i*2]);
measVoltage = (measVoltage >> 2) * (uint32_t)5000 / 0x3FFF;
cellVoltage[i*8 + cellNumberOffset] = measVoltage;
}

Im not exactly sure how to edit this i just want the voltages from 1-8 on each chip, there are 72 channels even though my bms only uses 64 of them there are no thermistors connected to any of the slaves,
Im not very good at coding i can understand some of it but this is way to much for me, if i start editing this its going to be more of a guess than anything else.
User avatar
bexander
Posts: 835
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Kia Niro BMS

Post by bexander »

bigmotherwhale wrote: Thu Oct 26, 2023 1:46 pm void readData()
{
for(int i=0; i<4; i++)
{
readAllSlaves((0x20 + i), true); // Read CELL 1-4 of all slaves
}
for(int i=0; i<4; i++)
{
readAllSlaves((0x25 + i), true); // Read CELL 5-8 of all slaves"
Try this:

Code: Select all

void readData() 
{	
    for(int i=0; i<8; i++)
  {
    readAllSlaves((0x20 + i), true); // Read CELL 1-8 of all slaves
  }
  //readAllSlaves(0x2D, true); // Read AIN1 of all slaves (Cell temperature)
  readAllSlaves(0x50, true); // Read DIAG (Die temperature) of all slaves
}
User avatar
bexander
Posts: 835
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Kia Niro BMS

Post by bexander »

bigmotherwhale wrote: Thu Oct 26, 2023 1:46 pm void storeCellVoltage(uint8_t dataRegister, uint8_t readRegisterData[29])
{
if((dataRegister >= 0x20) && (dataRegister <= 0x23)) // Cell voltage registers 1-4
{
uint8_t cellNumberOffset = dataRegister - 0x20;

for(int i=0; i<12; i++)
{
uint16_t measVoltage = ((readRegisterData[25-i*2] << 8) + readRegisterData[24-i*2]);
measVoltage = (measVoltage >> 2) * (uint32_t)5000 / 0x3FFF;
cellVoltage[i*8 + cellNumberOffset] = measVoltage;
}
}

if((dataRegister >= 0x25) && (dataRegister <= 0x28)) // Cell voltage registers 5-8
{
uint8_t cellNumberOffset = dataRegister - 0x20 - 1;

for(int i=0; i<12; i++)
{
uint16_t measVoltage = ((readRegisterData[25-i*2] << 8) + readRegisterData[24-i*2]);
measVoltage = (measVoltage >> 2) * (uint32_t)5000 / 0x3FFF;
cellVoltage[i*8 + cellNumberOffset] = measVoltage;
}
Try this:

Code: Select all

void storeCellVoltage(uint8_t dataRegister, uint8_t readRegisterData[29]) 
{	
	if((dataRegister >= 0x20) && (dataRegister <= 0x27)) // Cell voltage registers 1-8
  {
    uint8_t cellNumberOffset = dataRegister - 0x20;
    
    for(int i=0; i<8; i++)
		{
			uint16_t measVoltage = ((readRegisterData[25-i*2] << 8) + readRegisterData[24-i*2]);
			measVoltage = (measVoltage >> 2) * (uint32_t)5000 / 0x3FFF;
			cellVoltage[i*8 + cellNumberOffset] = measVoltage;
		}
	}
}
User avatar
bexander
Posts: 835
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Kia Niro BMS

Post by bexander »

I would also comment out this part:

Code: Select all

if(dataRegister == 0x2D) // Aux voltage measurements (external temperature sensors)
		{
			storeCellTemperature(readRegisterData);
		}
Like this:

Code: Select all

/*if(dataRegister == 0x2D) // Aux voltage measurements (external temperature sensors)
		{
			storeCellTemperature(readRegisterData);
		}*/
User avatar
bexander
Posts: 835
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Kia Niro BMS

Post by bexander »

Change:
const uint8_t numberOfCells = 96;
to
const uint8_t numberOfCells = 64;
User avatar
bexander
Posts: 835
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Kia Niro BMS

Post by bexander »

Also this function needs to be changed

Code: Select all

boolean balanceCells(uint16_t lowestCellVoltage)
{
	static uint8_t counter = 1;
	static uint16_t cellToBalanceFilter = 0x0FFF & 0x5555; // Every odd cell
	boolean measureVoltages = false;

  //if(1 == counter) Serial.println("Balance cells");
  
	writeAllSlaves(0x18, 0x1500, true); // Set WATCHDOG timer to 5s

	if(0 == counter)
	{
		cellToBalanceFilter = 0x0FFF & ~cellToBalanceFilter; // Every other cell
		writeAllSlaves(0x1A, 0x0000, true); // Turn off all cell balancing for one cycle to avoid any switch overlap
	}
	
	if(1 == counter)
	{
		measureVoltages = true; // OK to measure cell voltages
	}
	
	for (int i=0; i < 12; i++) // Go through modules to set cells for balancing
	{
		if(2 == counter) // Only set cells once every counter cycle
		{
			uint16_t cellToBalance = 0x0000;
    
			for (int j=0; j < 8; j++) // Go through cells to set cells for balancing
			{
				uint8_t cellNumber = j+i*8;
			
				if ((cellVoltage[cellNumber] > startShuntVoltage) && ((int16_t)(cellVoltage[cellNumber] - lowestCellVoltage) > voltageAllowance)) // Ok to balance
				{
					if (j < 4)
					{
						cellToBalance |= (0x01 << j);
					}
					else if (j >= 4 && j <8)
					{
						cellToBalance |= (0x01 << (j+1));
					}
					else //Error
					{
						cellToBalance = 0x0000;
					}
				}
			}
		
			cellToBalance &= cellToBalanceFilter; // To avoid enabling adjacent balance switches
			writeAddressedSlave(0x1A, cellToBalance, i, true); // Send request to set cell balance switches
		
			Serial.print(i+1);
			Serial.print(": ");
			//Serial.println(cellToBalance, BIN);
			
			for(int i=0; i < 8; i++)
			{
				if(4 == i) Serial.print(" ");
				
				if(i < 4)
				{
					Serial.print((uint8_t)((cellToBalance >> i) & 0x01));
				}
				else
				{
					Serial.print((uint8_t)((cellToBalance >> (i + 1)) & 0x01));
				}
			}
			Serial.println();
			
		}
		
		if(dieTemperature[i] > 100) // Check die overtemp
		{
			writeAddressedSlave(0x1A, 0x0000, i, true); // Turn off cell balancing for the module with die overtemp
			Serial.print("Die overtemp, module ");
      Serial.println(i+1);
		}
	}
	
	counter++;
	
	if(counter > balanceCellCounterTurnAround)
	{
		counter = 0;
	}
	
	return measureVoltages;
}
To:

Code: Select all

boolean balanceCells(uint16_t lowestCellVoltage)
{
	static uint8_t counter = 1;
	static uint16_t cellToBalanceFilter = 0x00FF & 0x5555; // Every odd cell
	boolean measureVoltages = false;

  //if(1 == counter) Serial.println("Balance cells");
  
	writeAllSlaves(0x18, 0x1500, true); // Set WATCHDOG timer to 5s

	if(0 == counter)
	{
		cellToBalanceFilter = 0x0FFF & ~cellToBalanceFilter; // Every other cell
		writeAllSlaves(0x1A, 0x0000, true); // Turn off all cell balancing for one cycle to avoid any switch overlap
	}
	
	if(1 == counter)
	{
		measureVoltages = true; // OK to measure cell voltages
	}
	
	for (int i=0; i < 8; i++) // Go through modules to set cells for balancing
	{
		if(2 == counter) // Only set cells once every counter cycle
		{
			uint16_t cellToBalance = 0x0000;
    
			for (int j=0; j < 8; j++) // Go through cells to set cells for balancing
			{
				uint8_t cellNumber = j+i*8;
			
				if ((cellVoltage[cellNumber] > startShuntVoltage) && ((int16_t)(cellVoltage[cellNumber] - lowestCellVoltage) > voltageAllowance)) // Ok to balance
				{
					if (j < 8)
					{
						cellToBalance |= (0x01 << j);
					}
					else //Error
					{
						cellToBalance = 0x0000;
					}
				}
			}
		
			cellToBalance &= cellToBalanceFilter; // To avoid enabling adjacent balance switches
			writeAddressedSlave(0x1A, cellToBalance, i, true); // Send request to set cell balance switches
		
			Serial.print(i+1);
			Serial.print(": ");
			//Serial.println(cellToBalance, BIN);
			
			for(int i=0; i < 8; i++)
			{
				Serial.print((uint8_t)((cellToBalance >> i) & 0x01));
			}
			Serial.println();
			
		}
		
		if(dieTemperature[i] > 100) // Check die overtemp
		{
			writeAddressedSlave(0x1A, 0x0000, i, true); // Turn off cell balancing for the module with die overtemp
			Serial.print("Die overtemp, module ");
      Serial.println(i+1);
		}
	}
	
	counter++;
	
	if(counter > balanceCellCounterTurnAround)
	{
		counter = 0;
	}
	
	return measureVoltages;
}
bigmotherwhale
Posts: 107
Joined: Tue Sep 22, 2020 6:48 pm
Has thanked: 15 times
Been thanked: 22 times

Re: Kia Niro BMS

Post by bigmotherwhale »

Thankyou for helping, I tried the above i think i may have got something wrong as an extra bracket stopped me from compiling, I removed what I thought was the right one and uploaded it and this is what i got. I have included the log and the modified .ino
could you have a look at see i haven't missed something? thanks
Attachments
BMS2_7_3_modified.ino
(47.06 KiB) Downloaded 34 times
error.txt
(1.2 KiB) Downloaded 34 times
User avatar
bexander
Posts: 835
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Kia Niro BMS

Post by bexander »

Replace the entire readAllSlaves() with this:

Code: Select all

void readAllSlaves(uint8_t dataRegister, boolean setupDone) // Read all slaves
{
	uint8_t command = 0x03; // READALL
	uint8_t byteList[3] = {command, dataRegister, 0x00};
	uint8_t PEC = calculatePEC(byteList, 3);
	uint8_t readRegisterData[21];
	uint8_t errorByte = 0x00;
	static uint8_t resendCounter = 0;
	//static uint8_t failCounter = 0;
	
	/*if(failCounter >= 4)
	{
		//clearBuffers();
		failCounter = 0; // Reset counter
		Serial.println("Buffers cleared");
	}*/
	
	SPI.beginTransaction(MAX17841);
	
	// Load the READALL command sequence into the load queue
	digitalWrite(SPI_MAX_CS_PIN, LOW);
  SPI.transfer(0xC0); // WR_LD_Q SPI command byte (write the load queue)
  SPI.transfer(0x15); // Message length (5 + 2 x n = 21)
  SPI.transfer(command); // READALL command byte
  SPI.transfer(dataRegister); // Register address
  SPI.transfer(0x00); // Data-check byte (seed value = 0x00)
  SPI.transfer(PEC); // PEC byte
  SPI.transfer(0x00); // Alive-counter byte (seed value = 0x00)
  digitalWrite(SPI_MAX_CS_PIN, HIGH); 
  
  transmitQueue();
	
	// Read the receive buffer
	digitalWrite(SPI_MAX_CS_PIN, LOW);
	SPI.transfer(0x93); // RD_NXT_MSG SPI command byte
	
	for(int i=0; i<21; i++)
  {
		readRegisterData[i] = SPI.transfer(0x93);
	}
	
	errorByte |= receiveBufferError();
	
	SPI.endTransaction();
	
	// Verify that the device register data is received correctly during the READALL sequence
	if(!((readRegisterData[0] == command) && (readRegisterData[1] == dataRegister)))
  {
		errorByte |= mismatchBefore; 
	}
	if(setupDone)
	{
		uint8_t checkPEC = calculatePEC(readRegisterData, 19);
		// Check check-byte, PEC and alive-counter	
		if(!((readRegisterData[18] == 0x00) && (readRegisterData[19] == checkPEC) && (readRegisterData[20] == 0x08)))
		{
			errorByte |= mismatchAfter;
			if(readRegisterData[18] != 0x00)
			{
				readAllSlaves(0x02, false); // Read STATUS of all slaves with checks ignored
				writeAllSlaves(0x02, 0x0000, true); // Clear STATUS register
				Serial.println("STATUS cleared");
			}
		}
	}
	
	/*// Print data received
	for(int i=0; i<21; i++)
  {
		Serial.print(readRegisterData[i], HEX);
		Serial.print(" ");
	}
	Serial.println();*/

	if(errorByte) // Error
	{
    resendCounter++;
    Serial.println(errorByte, HEX);
    errorByte &= 0x00; // Clear errors
    Serial.println("errorByte cleared");
    
    if(resendCounter > 2)
    {
			Serial.println("READALL fail");
			resendCounter = 0;
			//failCounter++;
			return;
		}
    
    delay(1);
    readAllSlaves(dataRegister, setupDone); // Resend READALL
  }
  else // No errors, clear counters and store data received
  {
		resendCounter = 0; // Reset counter
		//failCounter = 0; // Reset counter
		
		if((dataRegister >= 0x20) && (dataRegister <= 0x2B)) // Cell voltage measurements
		{
			storeCellVoltage(dataRegister, readRegisterData);
		}
  
		/*if(dataRegister == 0x2D) // Aux voltage measurements (external temperature sensors)
		{
			storeCellTemperature(readRegisterData);
		}*/
		
		
		if(dataRegister == 0x50) // Die temperature measurements
		{
			storeDieTemperature(readRegisterData);
		}
		
		if(dataRegister == 0x13) // Read SCANCTRL to check if data is ready to be read
		{
			checkDataReady(readRegisterData);
		}
		
		if(dataRegister == 0x02) // Read STATUS and print content
		{
			// Print data received
			for(int i=0; i<21; i++)
			{
				Serial.print(readRegisterData[i], HEX);
				Serial.print(" ");
			}
			Serial.println();
		}
	}  
}
Adjusted for 8 slave modules.
bigmotherwhale
Posts: 107
Joined: Tue Sep 22, 2020 6:48 pm
Has thanked: 15 times
Been thanked: 22 times

Re: Kia Niro BMS

Post by bigmotherwhale »

Okay thanks again, I will give that a go and report back.
bigmotherwhale
Posts: 107
Joined: Tue Sep 22, 2020 6:48 pm
Has thanked: 15 times
Been thanked: 22 times

Re: Kia Niro BMS

Post by bigmotherwhale »

Seems to be getting closer, still seems to have 12 groups in the readout though?
Here is what im getting.
Attachments
error.txt
(3.78 KiB) Downloaded 46 times
User avatar
bexander
Posts: 835
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Kia Niro BMS

Post by bexander »

In writeAllSlaves() there is an error in your code:

Code: Select all

// Check alive-counter
		if(!(readRegisterData[5] == 0x0))
		{
			errorByte |= mismatchAfter;
		}
Should be:

Code: Select all

// Check alive-counter
		if(!(readRegisterData[5] == 0x08))
		{
			errorByte |= mismatchAfter;
		}
User avatar
bexander
Posts: 835
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Kia Niro BMS

Post by bexander »

There is also a lot of other places that need to be changed, I realize.
Post your latest code and I will make some changes to it and post it back.
bigmotherwhale
Posts: 107
Joined: Tue Sep 22, 2020 6:48 pm
Has thanked: 15 times
Been thanked: 22 times

Re: Kia Niro BMS

Post by bigmotherwhale »

Here is the code its been modified just by your suggestions.
Cheers
Attachments
BMS2_7_3_EDITED.ino
(46.68 KiB) Downloaded 36 times
User avatar
bexander
Posts: 835
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Kia Niro BMS

Post by bexander »

Try with this:
BMS2_7_3_EDITED_2.ino
(46.67 KiB) Downloaded 41 times
bigmotherwhale
Posts: 107
Joined: Tue Sep 22, 2020 6:48 pm
Has thanked: 15 times
Been thanked: 22 times

Re: Kia Niro BMS

Post by bigmotherwhale »

i get errorbyte cleared 1
Would it be a good ide to enable the HEX output in the code to see what the slaves are spitting out?
Attachments
error.txt
(1.31 KiB) Downloaded 31 times
User avatar
bexander
Posts: 835
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Kia Niro BMS

Post by bexander »

Check now
BMS2_7_3_EDITED_2.ino
(46.67 KiB) Downloaded 45 times
bigmotherwhale
Posts: 107
Joined: Tue Sep 22, 2020 6:48 pm
Has thanked: 15 times
Been thanked: 22 times

Re: Kia Niro BMS

Post by bigmotherwhale »

It has the correct number of cells but im getting measurecelldata wd timeout
Attachments
error.txt
(861 Bytes) Downloaded 33 times
User avatar
bexander
Posts: 835
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Kia Niro BMS

Post by bexander »

Found a typo...
BMS2_7_3_EDITED_2.ino
(46.67 KiB) Downloaded 50 times
Post Reply