Page 8 of 9
Re: Flying ADC 16 channel BMS 96S test
Posted: Wed May 28, 2025 12:58 pm
by johu
Thanks for checking on that.
I carried out an experiment of my own with 64 LFP cells all charged to the maximum of 3.4V or roughly 220V total.
With the 4 blocks not interconnected I observed nothing strange. So I connected them all with 500 Ohm in between and measured across one of them with the scope to be able to see any unwanted current spikes. Nothing.
It's a mix of 2.1 and 2.3 boards.
Became brave and connected them straight through without the resistors to see if anything happened. Again, nothing.
Will build more battery bricks (have about 120 cells)
Re: Flying ADC 16 channel BMS 96S test
Posted: Fri May 30, 2025 4:56 am
by Proton
Do you have any 3.7v batteries to go over 4v? Even some small 26500 batteries would work.
Re: Flying ADC 16 channel BMS 96S test
Posted: Fri May 30, 2025 2:42 pm
by johu
In fact I developed the BMS with a 16S NMC battery that I charged to 4.1V. Like said, I've never seen it blow up when only one module is used.
Re: Flying ADC 16 channel BMS 96S test
Posted: Sun Jun 01, 2025 9:35 am
by uhi22
johu wrote: ↑Wed May 28, 2025 12:58 pm
With the 4 blocks not interconnected I observed nothing strange. So I connected them all with 500 Ohm in between and measured across one of them with the scope to be able to see any unwanted current spikes. Nothing.
That's a good starting point. In theory, nothing should break even if we inject voltage between the separate battery modules, like 10V DC, 100V DC, 400V rectangle, etc, right? Because the we assume "perfect isolation". But thinking of this:

- 2025-06-01_C_iso_C_CE.jpg (23.98 KiB) Viewed 1710 times
1. There is capacitance across the opto. If we apply a rectangle between the battery and the chassis ground (which basically a isolation monitoring device could do), we can open the FETs, if the voltage divider, formed by C_iso and C_GS provides sufficient voltage on the gate.
2. Also a weak resistive connection (1 MOhm) over C_iso, together with higher voltage, has the same effect.
3. Also the C_CE needs to be checked, because it also forms a voltage divider with C_GS, in case the OUTP is jumping.
Re: Flying ADC 16 channel BMS 96S test
Posted: Sun Jun 01, 2025 10:04 am
by johu
Thanks!
So adding 22n in parallel to R6 should have a positive effect, right? Now the combined C_GS >> C_iso and C_CE which should pretty much rule out false triggers?
The dead time now needs to be increased to around 2 ms until R6 has sufficiently discharged C_22. With the given time constants (one switch every 25 ms) that should be no problem
Re: Flying ADC 16 channel BMS 96S test
Posted: Sun Jun 01, 2025 10:22 am
by uhi22
Hm, I'm not really convinced of my assumption anymore.
The C_iso is around 1pF (
https://www.mouser.com/datasheet/2/143/ ... M5gYtOZNbV) and the C_gs specified as "max 600pF" (
http://www.stansontech.com/download/Tre ... SRG_V1.pdf), so if we assume 300pF we would have a divider 1/300. So for reaching the treshold voltage (above 1V), we would need 300V. Of course, the layout may increase the C_iso, but I would not treat this as 100% proof to the the root cause.
Maybe multiple effects come together to reach the worst case, e.g. the above combined with some resistive leakage due to humidity. 1V threshold voltage together with 10k needs only 100µA. How much could the 10k be lowered?
Re: Flying ADC 16 channel BMS 96S test
Posted: Sun Jun 01, 2025 10:26 am
by johu
uhi22 wrote: ↑Sun Jun 01, 2025 10:22 am
How much could the 10k be lowered?
Probably a lot. Cell drain reduction was priority but maybe I overshot the target there?
Re: Flying ADC 16 channel BMS 96S test
Posted: Sun Jun 01, 2025 10:48 am
by uhi22
Maybe go with 2k2 || 10nF, and look what the statistics will say.
And reduce the cell drain by reducing the sampling frequency. Maybe introducing two modes with A: fast sampling, higher drain and B: slow sampling, low drain, so depending on the use case everybody can optimize just by setting a parameter.
Re: Flying ADC 16 channel BMS 96S test
Posted: Sun Jun 01, 2025 11:57 am
by uhi22
Another direction of thinking: At which voltage the flying ADC is flying, before any of the mux switches are activated? By which path is this voltage determined?
- into direction of chassis ground, it is isolated via the 5V/5V DCDC and the data isolators. Fine.
- into direction of battery, the voltage can rise or fall until a PFET will reach the avalanche voltage (>100V according to
http://www.stansontech.com/download/Tre ... SRG_V1.pdf). Not much current can flow, it is only like static electricity.
Using this situation as starting point, could this lead to an overload of U_GS or an unintended switching of other channels at the moment when the first MUX channel turns on?
Edit: One possible damage scenario is caused by the Miller capacity. It turns the FET on, even if the gate is grounded by resistor. We have 20pF Miller, and C_GS<600pF. Let's assume 400pF. This forms a voltage divider 20pF/400pF, which is factor 20. The threshold voltage of 1V is easily reached by applying a voltage jump on the drain of 20V. Having the ADC sitting around at +100V or -100V at the beginning, it is easy to get a jump of 100V U_DS, resulting in a gate voltage of 5V. Multiple FETS will start to draw current in this situation, until the 10k gets the gates discharged.
Also for this scenario, lowering the 10k and adding a parallel gate capacitance would help.
Edit 2: An other scenario is, that in such a situation the maximum allow U_GS of +/-20V is violated. Because the source can get a quickly rising voltage via the body diode of the other FET, and the gate is still sitting and hold by the C_CE of the optocoupler. If the C_CE is in the same order of magnitude as the C_GS (20pF) or even higher, the gate voltage is only following the source with delay.
Also for this case, an additional gate capacitance would help.
Re: Flying ADC 16 channel BMS 96S test
Posted: Sun Jun 01, 2025 12:23 pm
by johu
In favour of this theory FETs always failed at power-up. Once the system is up and running I have never seen or heard of failures.
At power-up all FETs are open, this should be ensured by the pull-down resistors. So the ADC floats *somewhere*
I have witnessed the Miller effect on a simple H-bridge. The low-side FETs V_GS can be clamped to 0 with a strong driver and about 1 Ohm gate resistor. Still when you turn on the upper FET you get a spurious turn-on of the lower FET as well. It is only very short and doesn't do much damage because of the strong pull-down. But with 10k this is a different story (despite the lower Miller cap of the smaller FET)
Re: Flying ADC 16 channel BMS 96S test
Posted: Sun Jun 01, 2025 5:52 pm
by skr
so my 36S MEB modules have been in use for <1 week, so far no issues. Balancing is slow, I am helping the weakest cells with PSU CV set below before it starts tripping the hell out of ADC measurements on the adjacent cells and OCP <300mA.
The gate resistor has 8200pF 100V cap across it, extra 3R on cell taps through the polyfuse. Same i2cdelay=5 as other 2.3HW. Pretty thick coat of conformal coat. I have it completely shutting off with ignition at the moment. Cell taps go in pretty close proximity to the phase cables, which see <300A phase current.
Current bmsio code looks like this:
Code: Select all
oid BmsIO::ReadCellVoltages()
{
const int totalBalanceCycles = 30;
static uint16_t balanceCycles[18] = {0};
static uint8_t chan = 0;
static float sum = 0, min, max, avg;
int balMode = Param::GetInt(Param::balmode);
bool balance = Param::GetInt(Param::opmode) == BmsFsm::IDLE && Param::GetFloat(Param::uavg) > Param::GetFloat(Param::ubalance) && BAL_OFF != balMode;
FlyingAdcBms::BalanceStatus bstt;
if (balance)
{
if (balanceCycles[chan] == 0)
{
balanceCycles[chan] = totalBalanceCycles; //this leads to switching to next channel below
}
else
{
balanceCycles[chan]--;
}
if (balanceCycles[chan] > 0 && balanceCycles[chan] < (totalBalanceCycles - 1))
{
float udc = Param::GetFloat((Param::PARAM_NUM)(Param::u0 + chan));
float balanceTarget = 0;
switch (balMode)
{
case BAL_ADD: //maximum cell voltage is target when only adding
balanceTarget = CorrectVoltage(Param::GetFloat(Param::umax));
break;
case BAL_DIS: //minimum cell voltage is target when only dissipating
balanceTarget = CorrectVoltage(Param::GetFloat(Param::umin));
break;
case BAL_BOTH: //average cell voltage is target when dissipating and adding
balanceTarget = CorrectVoltage(Param::GetFloat(Param::uavg));
break;
default: //not balancing
break;
}
// Calculate adaptive thresholds based on voltage delta and minimum values
float deltaV = CorrectVoltage(Param::GetFloat(Param::umax)) - CorrectVoltage(Param::GetFloat(Param::umin));
float chargeThreshold = MAX(3.0f, deltaV * 0.02f); // At least 3mV or 2% of delta
float dischargeThreshold = MAX(1.0f, deltaV * 0.01f); // At least 1mV or 1% of delta
// Static variables to cache global values for slave nodes
static float cachedUmin = 0.0f;
static float cachedUmax = 0.0f;
static float cachedUavg = 0.0f;
static uint32_t lastCacheUpdateTime = 0;
static uint32_t cacheCounter = 0;
cacheCounter++;
// Cache values with a timeout mechanism for slave nodes
if (!bmsFsm->IsFirst()) {
// Current cached values
float currentUmin = CorrectVoltage(Param::GetFloat(Param::umin));
float currentUmax = CorrectVoltage(Param::GetFloat(Param::umax));
float currentUavg = CorrectVoltage(Param::GetFloat(Param::uavg));
// Check if values changed significantly (received update from master)
bool valuesChanged = (ABS(currentUmin - cachedUmin) > 1.0f) ||
(ABS(currentUmax - cachedUmax) > 1.0f) ||
(ABS(currentUavg - cachedUavg) > 1.0f);
if (valuesChanged) {
// Update cached values
cachedUmin = currentUmin;
cachedUmax = currentUmax;
cachedUavg = currentUavg;
lastCacheUpdateTime = cacheCounter;
}
// Make balancing decisions more sensitive on slave nodes
chargeThreshold = MAX(1.5f, deltaV * 0.01f); // More aggressive (1.5mV minimum)
dischargeThreshold = MAX(0.5f, deltaV * 0.005f); // More aggressive (0.5mV minimum)
// If cache is stale (no updates for 200 cycles ~5 seconds), use even more aggressive thresholds
if ((cacheCounter - lastCacheUpdateTime) > 200) {
// Use more aggressive thresholds if not receiving regular updates
chargeThreshold = MAX(1.0f, deltaV * 0.005f); // Very aggressive
dischargeThreshold = MAX(0.3f, deltaV * 0.0025f); // Very aggressive
}
// Special case for significant outliers
float significantDeviation = 50.0f; // mV
if (ABS(udc - balanceTarget) > significantDeviation) {
// For big deviations, use even more aggressive thresholds
if (udc < balanceTarget)
chargeThreshold = 1.0f; // Extremely aggressive charge threshold for low cells
else
dischargeThreshold = 0.3f; // Extremely aggressive discharge threshold for high cells
}
}
if (udc < (balanceTarget - chargeThreshold) && (balMode & BAL_ADD))
{
bstt = FlyingAdcBms::SetBalancing(FlyingAdcBms::BAL_CHARGE);
}
else if (udc > (balanceTarget + dischargeThreshold) && (balMode & BAL_DIS))
{
bstt = FlyingAdcBms::SetBalancing(FlyingAdcBms::BAL_DISCHARGE);
}
else
{
bstt = FlyingAdcBms::SetBalancing(FlyingAdcBms::BAL_OFF);
balanceCycles[chan] = 0;
}
Param::SetInt((Param::PARAM_NUM)(Param::u0cmd + chan), bstt);
}
else
{
FlyingAdcBms::SetBalancing(FlyingAdcBms::BAL_OFF);
}
}
else
{
balanceCycles[chan] = totalBalanceCycles;
bstt = FlyingAdcBms::SetBalancing(FlyingAdcBms::BAL_OFF);
Param::SetInt((Param::PARAM_NUM)(Param::u0cmd + chan), bstt);
}
//Read cell voltage when balancing is turned off
if (balanceCycles[chan] >= totalBalanceCycles)
{
float gain = Param::GetFloat(Param::gain);
int numChan = Param::GetInt(Param::numchan);
bool even = (chan & 1) == 0;
if (chan == 0)
gain *= 1 + Param::GetFloat(Param::correction0) / 1000000.0f;
else if (chan == 1)
gain *= 1 + Param::GetFloat(Param::correction1) / 1000000.0f;
else if (chan == 15)
gain *= 1 + Param::GetFloat(Param::correction15) / 1000000.0f;
//Read ADC result before mux change
float udc = FlyingAdcBms::GetResult() * (gain / 1000.0f);
Param::SetFloat((Param::PARAM_NUM)(Param::u0 + chan), udc);
min = MIN(min, udc);
max = MAX(max, udc);
sum += udc;
//First we sweep across all even channels: 0, 2, 4,...
if (even && (chan + 2) < numChan)
chan += 2;
//After reaching the furthest even channel (say 12) we either change over to a higher odd channel
else if (even && (chan + 1) < numChan)
chan++;
//or lower odd channel
else if (even)
chan--;
//Now we sweep across all odd channels until we reach 1
else if (chan > 1)
chan -= 2;
//We have no reached chan 1. Accumulate values and restart at chan 0
else
{
chan = 0;
avg = sum / Param::GetInt(Param::numchan);
Accumulate(sum, min, max, avg);
min = 8000;
max = 0;
sum = 0;
}
FlyingAdcBms::SelectChannel(chan);
FlyingAdcBms::StartAdc();
}
}
Re: Flying ADC 16 channel BMS 96S test
Posted: Thu Jun 05, 2025 10:01 am
by maiks
I have a battery pack with 96 cells.
I'm trying to run them without interconnecting the BMS boards directly. According to the [Wiki], it says:
"The BMS will then keep running as long as current flows through the battery. Two hours after it has measured the last current flow, it will shut down."
Based on that, I have a few questions:
Current Sensing:
If I’m not interconnecting the BMSes, does that mean I need to connect all 6 BMS boards to the same current sensor to keep them alive and synchronized?
Enable_in (Ignition Pin):
Should I connect the enable_in pin of all 6 BMS boards to the same ignition signal?
Web Interface Access:
If the BMSes are not networked together (e.g., over CAN), how can I individually access each BMS via the web interface?
Re: Flying ADC 16 channel BMS 96S test
Posted: Thu Jun 05, 2025 10:16 am
by skr
You need to connect enable in to the first board, which in turn controls enable in on next board through its enable out. Without these nothing will work.
Re: Flying ADC 16 channel BMS 96S test
Posted: Thu Jun 05, 2025 10:25 am
by johu
maiks wrote: ↑Thu Jun 05, 2025 10:01 am
If I’m not interconnecting the BMSes, does that mean I need to connect all 6 BMS boards to the same current sensor to keep them alive and synchronized?
No, the current sensor must be connected to the first board and transmits its value to the other ones via CAN
maiks wrote: ↑Thu Jun 05, 2025 10:01 am
If the BMSes are not networked together (e.g., over CAN), how can I individually access each BMS via the web interface?
That's not how it is designed to work. E.g. for balancing each board needs to know the min/max/avg cell voltages of the others. Why are you not doing the CAN interconnection?
Re: Flying ADC 16 channel BMS 96S test
Posted: Thu Jun 05, 2025 10:58 am
by maiks
johu wrote: ↑Thu Jun 05, 2025 10:25 am
No, the current sensor must be connected to the first board and transmits its value to the other ones via CAN
That's not how it is designed to work. E.g. for balancing each board needs to know the min/max/avg cell voltages of the others. Why are you not doing the CAN interconnection?
The muxshort error that some users are experiencing is mainly caused by cascading multiple BMS units together. That's why I’m exploring the option of running the BMSes independently without interconnecting them.
Re: Flying ADC 16 channel BMS 96S test
Posted: Thu Jun 05, 2025 11:54 am
by uhi22
This would also need independent power supply, how do you plan these?
Re: Flying ADC 16 channel BMS 96S test
Posted: Thu Jun 05, 2025 12:22 pm
by johu
maiks wrote: ↑Thu Jun 05, 2025 10:58 am
The muxshort error that some users are experiencing is mainly caused by cascading multiple BMS units together. That's why I’m exploring the option of running the BMSes independently without interconnecting them.
Ok, interesting approach. Yes they'd need something along the lines of this
viewtopic.php?t=1351 or that
viewtopic.php?t=6361 to isolate them from each other and still have communication.
Or just wait and see if we found the culprit and you can patch the BMS to work normally (you'd basically need 96 10nf 0603 capacitors). I'll take reception of new boards any day now.
Re: Flying ADC 16 channel BMS 96S test
Posted: Thu Jun 05, 2025 12:44 pm
by maiks
johu wrote: ↑Thu Jun 05, 2025 12:22 pm
Ok, interesting approach. Yes they'd need something along the lines of this
viewtopic.php?t=1351 or that
viewtopic.php?t=6361 to isolate them from each other and still have communication.
Or just wait and see if we found the culprit and you can patch the BMS to work normally (you'd basically need 96 10nf 0603 capacitors). I'll take reception of new boards any day now.
ok lets wait for the solution . Thank you
Re: Flying ADC 16 channel BMS 96S test
Posted: Thu Jun 05, 2025 4:13 pm
by johu
maiks wrote: ↑Thu Jun 05, 2025 12:44 pm
ok lets wait for the solution . Thank you
On it!
Re: Flying ADC 16 channel BMS 96S test
Posted: Thu Jun 05, 2025 5:15 pm
by johu
This is the switching waveform with 5k1 || 22nF
The 2ms dead time is plenty even for the FETs that have 10k || 22nF.
Had to remove R10 (that is supposed to put some extra load across cell 16 to avoid passive disbalancing). I connected it behind the 10R inline where it caused too much voltage drop across these.
The new isolator (with DC/DC) seems to have a slow power up ramp and the balancer switch misses its I2C command to switch all pins to output. So they remain at input with pull-up and no control over the balancing circuit. Consequently the self test fails with BALANCER_FAIL. Fixed it for now by issuing the "pins to output" command before every balancer command. Quite redundant but works for now.
Apart from that everything works as expected.
EDIT: I also found that I had forgotten to set all 8 mux control pins to 0 before switching them to output. They have a reset value of 0 but who knows?
Re: Flying ADC 16 channel BMS 96S test
Posted: Thu Jun 05, 2025 6:02 pm
by johu
I swapped one module into my 64S pack and it works. Of course that doesn't say much as the old one worked as well.
To patch modules to this state first install this software:
https://github.com/jsphuebner/FlyingAdc ... 5473329191
Never run a v2.4 BMS or one patched to that state with an older version than 0.26.R!
Then put a 10 or 22nF capacitor in parallel to all gate pull-down/up resistors (R1, R2, R15, R27, R35, R7, R14, R26, R34, R42, R49, R55, R59, R6, R13, R25, R33)
Then start with 2 modules connected together. Power cycle multiple times to make sure it all goes well. Then add a 3rd, 4th and so on.
@maiks: if a module gets destroyed I'll replace it with a new one.
Re: Flying ADC 16 channel BMS 96S test
Posted: Thu Jun 05, 2025 7:03 pm
by Proton
Can I install the latest 0.26.R on my boards? I have a combination of v 2.1, v2.2 and v2.3 boards
Re: Flying ADC 16 channel BMS 96S test
Posted: Thu Jun 05, 2025 7:20 pm
by johu
I'm inclined to say never change a running system. The firmware doesn't add a new feature.
On the other hand it adds another verification point if you do install it.
Re: Flying ADC 16 channel BMS 96S test
Posted: Fri Jun 06, 2025 7:26 am
by maiks
johu wrote: ↑Thu Jun 05, 2025 6:02 pm
I swapped one module into my 64S pack and it works. Of course that doesn't say much as the old one worked as well.
To patch modules to this state first install this software:
https://github.com/jsphuebner/FlyingAdc ... 5473329191
Never run a v2.4 BMS or one patched to that state with an older version than 0.26.R!
Then put a 10 or 22nF capacitor in parallel to all gate pull-down/up resistors (R1, R2, R15, R27, R35, R7, R14, R26, R34, R42, R49, R55, R59, R6, R13, R25, R33)
Then start with 2 modules connected together. Power cycle multiple times to make sure it all goes well. Then add a 3rd, 4th and so on.
@maiks: if a module gets destroyed I'll replace it with a new one.
Thank you, @johu. I just want to confirm—should I remove the components marked in red (as shown in the attached image) and replace them with 10 nF or 22 nF 0603 SMD ceramic capacitors?
Re: Flying ADC 16 channel BMS 96S test
Posted: Fri Jun 06, 2025 8:34 am
by uhi22
No, the capacitors need to be added "on top" (literally).