KT motor controllers -- Flexible OpenSource firmware for BMSBattery S/Kunteng KT motor controllers (0.25kW up to 5kW)

@stancecoke @Xnyle it brings tears to my eyes to see the both of you back here! The og creaters of this amazing firmware + Bluetooth app!

I would like to convince you that this firmware is not dead and that it provides unique value to the ebike community that other controllers simply cannot offer. People keep coming back to try and further optimize the firmware. The combination of a small form factor of the controller, high power (15 kW+ from the 18 fets), low weight (1 kg), flexible firmware that is reasonably easy to read and edit, native PAS support, (simplified) FOC, flexibility in screen options (lcd3, 8, app or none) + still active community is simply unmatched by any other controller I have encountered so far.

There a still things to be polished and added in (preferably) a user interface (as far as I am up to date/aware):
- display motor temp in kt-lcd 3, with the same logic (10k thermistor) as in the bluosec app. An active customizable upper limit would be highly needed to protect our motors from the large phase currents that the 18 fets can produce.
- easily accessible and customizable field weakening that obeys a certain max in phase amps.
- easily accessible and customizable ramp up time to set duty cycle. I always change this manually in the firmware from 2s to 0.5s. This greatly increases responsiveness/aggressiveness of the throttle response and enables things like instant wheelies.
- optimization of the zero amp finding routine: i have recently started editing this, with double the wait states and 1/10 the zero amp threshold (from 2amp to 0.2amp i believe). This has greatly reduced the power loss at idle, now showing some 50watts at idle on the screen instead of the original 250watts (real losses from 3w to 0.3W or so; much less hot to the touch). Maybe this influences drag when coasting; i did not test that yet.
- implementing the motor constant as a settable value in the java script.
- checking the stability of recent updates to e.g. regen braking (i tried Andrea’s updates and instantly fried 2 controllers so i went back to the original stancecoke, xnyle and torque from x4 master branch, that has always worked flawlessly with powerful regen braking up to skidding the rear wheel). This checking is up to us as a community of course.
- cleaning up all tested modifications in a new master branch + optimally an updated java tool + bluosec (but that is wishful thinking;) )

Of course, we can keep editing the firmware directly by hand, but that would probably lead to chaos in the long run. So a skilled owner would be highly appreciated as this firmware is really a piece of art! And so are the controllers, simple and crappy as some may call them.

See some pictures below as a tribute to the work that you did on the firmware: a 35kg 15kW carbon mtb that has been running your amazing firmware on a modified 18fet kt controller (200batt amps, 300phase amps, oil filled and actively cooled) very reliably for years now. This performance in such a small form factor and for such a low price is still unmatched by all modern controllers in my opinion.

So let’s keep the kt firmware development alive!
I agree 100% to all the above!
One question to this @Iambuilderman :
"
ramp up time to set duty cycle. I always change this manually in the firmware from 2s to 0.5s. This greatly increases responsiveness/aggressiveness of the throttle response and enables things like instant wheelies.
"

Could you point me to where what needs to be edited?
I could also use a faster response to the throttle in my use case.
 
"ramp up time to set duty cycle. I always change this manually in the firmware from 2s to 0.5s. This greatly increases responsiveness/aggressiveness of the throttle response and enables things like instant wheelies."

Could you point me to where what needs to be edited?
I could also use a faster response to the throttle in my use case.
@stancecoke and / or @Xnyle:
can one of you please point me to where in the firmware the throttle response can be made more responsive (-> "ramp up time to set duty cycle")?
I tried already with PI controller parameter changes, but I would really like to try what is mentioned above by @Iambuilderman, but unfortunately, I did not get a response from him.
 
@stancecoke and / or @Xnyle:
can one of you please point me to where in the firmware the throttle response can be made more responsive (-> "ramp up time to set duty cycle")?
I tried already with PI controller parameter changes, but I would really like to try what is mentioned above by @Iambuilderman, but unfortunately, I did not get a response from him.
In the absence of any suggestions I thought I'd have a play with AI on this, the code for throttle control *appears* to be in ACAcommons.c - presenting this code to ChatGPT and asking your question got the following response:-

❓Follow-Up: Would you like help refactoring any specific part of this code, such as making the offroad mode logic more maintainable or improving the PI controller?​

4o

You said:​

make throttle ramp up faster?

ChatGPT said:​

To make the throttle ramp up faster in your ACAcommons.c code, you need to adjust how the throttle value is accumulated and filtered. Currently, the ramp-up behavior is controlled by a moving average mechanism using this block in updateRequestedTorque():
c
CopyEdit
ui16_throttle_accumulated -= ui16_throttle_accumulated >> 4;
ui16_throttle_accumulated += ui8_adc_read_throttle();
ui8_temp = ui16_throttle_accumulated >> 4;
This acts as a low-pass filter with a smoothing factor of 1/16, which slows down how quickly the actual throttle responds to fast user input.

🔧 To Make the Throttle Ramp Up Faster:​

Option 1: Reduce the filter time constant​

Decrease the right shift value from 4 (which divides by 16) to something smaller, such as 2 (divide by 4):
c
CopyEdit
ui16_throttle_accumulated -= ui16_throttle_accumulated >> 2;
ui16_throttle_accumulated += ui8_adc_read_throttle();
ui8_temp = ui16_throttle_accumulated >> 2;
⚠️ Lower values = faster response but more noise.

Option 2: Remove filtering entirely (instant throttle response)​

This would make it respond immediately, but could be jerky:
c
CopyEdit
ui8_temp = ui8_adc_read_throttle();

Option 3: Use an adaptive ramp-up​

Add a fast path when increasing throttle and a slower one when decreasing, for smoother feel:
c
CopyEdit
uint8_t raw_throttle = ui8_adc_read_throttle();
if (raw_throttle > (ui16_throttle_accumulated >> 4)) {
ui16_throttle_accumulated += (raw_throttle << 2); // ramp up fast
} else {
ui16_throttle_accumulated -= ui16_throttle_accumulated >> 3; // ramp down slow
}
ui8_temp = ui16_throttle_accumulated >> 4;

Would you like me to modify the code directly with one of these options?
______________________________________________________________________________________________________________________________________


.......so possibly some ideas to try there? Bear in mind that both myself and ChatGPT are known to be error prone, so obviously all at your own risk.. :)
 
Last edited:
In the absence of any suggestions I thought I'd have a play with AI on this, the code for throttle control *appears* to be in ACAcommons.c - presenting this code to ChatGPT and asking your question got the following response:-
..........

ChatGPT said:​

To make the throttle ramp up faster in your ACAcommons.c code, you need to adjust how the throttle value is accumulated and filtered. Currently, the ramp-up behavior is controlled by a moving average mechanism using this block in updateRequestedTorque():
c
CopyEdit
ui16_throttle_accumulated -= ui16_throttle_accumulated >> 4;
ui16_throttle_accumulated += ui8_adc_read_throttle();
ui8_temp = ui16_throttle_accumulated >> 4;
This acts as a low-pass filter with a smoothing factor of 1/16, which slows down how quickly the actual throttle responds to fast user input.

🔧 To Make the Throttle Ramp Up Faster:​

Option 1: Reduce the filter time constant​

Decrease the right shift value from 4 (which divides by 16) to something smaller, such as 2 (divide by 4):
c
CopyEdit
ui16_throttle_accumulated -= ui16_throttle_accumulated >> 2;
ui16_throttle_accumulated += ui8_adc_read_throttle();
ui8_temp = ui16_throttle_accumulated >> 2;

.......so possibly some ideas to try there? Bear in mind that both myself and ChatGPT are both proven to be error strewn, so obviously all at your own risk.. :)
Thank you, @geofft !
I used ChatGPT also on other projects to clean up my code.
Did not think about it in this case - great idea!
I will try the filter constant reduction - the suggested 4-fold reduction would even match what @Iambuilderman stated (2 sec to 0.5 sec), so maybe he went the same path.

I am not at my controller these days, but will try asap when I am back.
 
Hey guys,
I just came across this wonderful firmware that you build, and I want to ask you a few newbie things, I hope you won't mind.
I have a kids e-motorcycle with the controller from the attached picture.
1 - is it compatible with your firmware?
2 - can I make a backup of the existing firmware?
3 - the reason I want to upgrade is that I have knob to limit the max speed, and when I use it the throttle control is not using the full range of movement to adjust the speed, but it just limits the top end, basically like a low pass filter. I want the new max speed to be reached when throttle is in the max position.
Here is a quick video which shows how it works when max speed is limited to about 30%

Thanks!
 

Attachments

  • IMG_8983.jpeg
    IMG_8983.jpeg
    919.6 KB · Views: 11
Hey guys,
I just came across this wonderful firmware that you build, and I want to ask you a few newbie things, I hope you won't mind.
I have a kids e-motorcycle with the controller from the attached picture.
1 - is it compatible with your firmware?
2 - can I make a backup of the existing firmware?
3 - the reason I want to upgrade is that I have knob to limit the max speed, and when I use it the throttle control is not using the full range of movement to adjust the speed, but it just limits the top end, basically like a low pass filter. I want the new max speed to be reached when throttle is in the max position.
Here is a quick video which shows how it works when max speed is limited to about 30%

Thanks!
From your picture it looks like you do not have a KT controller, so the firmware will most likely not be compatible w/ your controller.
Also - 2) the KT controllers block readout of the original f/w, so there is no way for creating a backup
 
Guys, I will respond to your questions in detail (with screenshots) this weekend; time is always too limited. But I am thrilled that you are still interested in further developing these firmware + controller modifications. I was also thinking of AI to help with suggestions for coding. And also with writing an extrnsive ‘how to’ for preparing the 18 fet controllers for more power + voltage, as I promised a long time ago.

One of the things I discovered is the importance of filtering the power line + 12/15v line; it really helps to keep temperatures in check when you are operating at (or even over) the absolute maximum ratings and safe operating area of the mosfets.

First things first, I will start with some proper answers to your questions this weekend :)
 
One of the things I discovered is the importance of filtering the power line + 12/15v line; it really helps to keep temperatures in check when you are operating at (or even over) the absolute maximum ratings and safe operating area of the mosfets.
A while back I made the mistake of tapping the controller internal 15v line to power my new torquesensor - several blown mosfets later I realised what was happening and powered the tqsr from an alternative source. No blown mosfets since then, so I can concur with what you're saying here..:)
 
A while back I made the mistake of tapping the controller internal 15v line to power my new torquesensor - several blown mosfets later I realised what was happening and powered the tqsr from an alternative source. No blown mosfets since then, so I can concur with what you're saying here..:)
Keep in mind they probably only design that supply with enough juice to barely power the gate drivers and feed the 5v regulator...if you drop the voltage enough from another load it could cause the FETs to be insufficiently driven, so they don't turn on all the way, and operate as resistors instead of switches..... :(

It could also affect the turn on/off timing, which probably wouldn't cause shoot thru, but might cause other similar issues.
 
@stancecoke and / or @Xnyle:
can one of you please point me to where in the firmware the throttle response can be made more responsive (-> "ramp up time to set duty cycle")?
I tried already with PI controller parameter changes, but I would really like to try what is mentioned above by @Iambuilderman, but unfortunately, I did not get a response from him.

Great to see you and Geofft are trying to make further optimisations using AI! I just read through all code in notepad to see what could be useful to change. My approach to increasing responsiveness is different, but maybe yours works as well.

In main.h I just lowered the time it takes to get to maximum pwm duty cycle, from the original 127 (2s) to 60 (1s) or 30 (0.5s). I dare not go lower than 30 to protect the fets but maybe it is fine. Please note: in the java configurator I set my P value to 0.7 and my I value to 0.2, that should also help with responsiveness (and I dare not go higher).

Just experiment with the pwm rampup timer; it should make a big difference from a standstill!

This is what it looks like:
 

Attachments

  • IMG_3612.jpeg
    IMG_3612.jpeg
    8.6 MB · Views: 9
See the reference manual:
View attachment 362912

to be honest, I don't know, how the Standard Peripheral Library handles the values in the TIM1_BDTRConfig command. :)

regards
stancecoke
Hi Stancecoke,

Thanks for your geat help and detailed info + suggestions. The stm dead time specification looks complicated indeed, but I think Casainho implemented a much simpler method in the code, see pwm.c in the screenshot below:IMG_3614.jpeg
I tried different values: 16 (1 us), 48 (3 us) and as you suggested 160 (10 us). This made no difference to the power loss and heat production during idle, so i changed it back to 16 (1 us).

I also tried increasing the gate voltage of my buck converter from 12v to 16v, but this did not make any significant difference so i changed it back to 12v later on (to reduce switching losses and prevent ringing/overshoot by slowing down the switching a bit).

In the end, I did solve the idle losses problem by changing the ‘zero current routine’ in the code and by improving the filtering of the power line and buck converter. I suspect my fets were avalanching a bit due to excesive voltage peaks (switching noise) and/or false triggering due to noise in the 12/15v line.

This is what I changed to reduce idle losses:

In main.h I decreased the ‘maximum current’ by a factor of 10, and also halved the max positive and max negative values. The result is a visible iteration at startup/after regen braking to the lowest possible idle current:

IMG_3616.jpeg

Please note that I already slowed down the idle current iteration proces at bootup by doubling all initialisation wait states in adc.c but this change did not help without the other one above):
IMG_3609.jpeg

IMG_3610.jpeg

These code changes reduced the idle losses by a factor 2 - 3. The filtering helped another factor 5 or so, so in total a factor 10.
 
Last edited:
With respect to the filtering: that is a big topic on itself. I found that by reducing input voltage to my (customized; 120v fets) controller from 110v to 106v also solved the heating problem, so that’s where i started suspecting an avalanching problem and tried to improve filtering. This is what I did, it has also helped to reduce idle losses for a 20s controller (100v 4110 fets; 84v battery voltage):

- improve filtering on the main power line by adding 4x1000=4000 uF lossy ‘low esr’ electrolytic bulk capacitance outside the controller (no space inside) + adding 4.7uF 400v cbb film capacitor at the power entry point on the pcb.
- reduce startup inrush current to buck converter by adding another 5ohm in addition to the 5ohm ntc that was already there.
- as a buck converter is not able to filter very high frequency (switching! Di/Dt…) noise on the input line, i added both a ferrite bead to the input as well as the output line of the dc-dc converter. I also added a 0.1uF 104j cbb to both the input and output of the converter. Note that a ferrite bead and a fast cbb capacitor can lead to ringing, but fortunately there is already an ntc resistor at the input + a lossy electrolytic at both the in and output, so they will dampen the fast ringing behaviour.

These changes have helped a lot; there is no significant heating during idle anymore, and the controller also remains much cooler during (heavy) use. I now run the 4110 18fet at 330 phase amps. I am planning to run it at 250 battery amps (need an additional battery + fuse for that…) and 350 phase amps. I have never blown a controller during active duty (only during testing/water ingress); fingers crossed that 250a/350a phase is also fine with your very accurate firmware & control loop!

Please find attached some of the literature i read on filtering buck converters.

Long story short: it is best to slow down inrush current with an ntc (e.g. 10 ohm), you can add ferrite beads to input and ouput lines, you can add 104j cbb’s to input and output for additional highfrequency filtering, a 12v line is a high enough voltage for the fets (up to totally crazy phase amps such as 110a per fet without taking into account any derating factor for parallelled fets!).

My current idea for even flatter driver voltage is to modify a 120v->24v buck converter to a 17v buck converter, and to feed this 17v to the lm317 linear voltage regulator (2v losses). That should result in the flattest and most robust driver voltage for the system i think. I will report back once i have built a new controller with this setup.

A lot of words; I hope it is understandable and of use to anyone
 

Attachments

  • slta055.pdf
    219.5 KB · Views: 0
  • AN-1368.pdf
    443.2 KB · Views: 0
  • slup409c.pdf
    2 MB · Views: 0
  • slyt670.pdf
    1 MB · Views: 2
Some more literature on filtering & ferrite beads. I suspect proper EMI filtering might also help users who lose connection with the Bluosec app. The Bluosec app demands a lot from your phone (power, calculations, at least from my old phone ;) ). I found that keeping the phone connected to usb power (battery pack for phone or use a buck converter + usb outlet) helps to keep The Bluetooth connection stable.

Some background on the pro’s and cons of using ferrite beads and fast capacitors such as ceramics or metal film cbbs is in the files below:
 

Attachments

  • Ferrite Beads Demystified | Analog Devices.pdf
    1.1 MB · Views: 2
  • an88f.pdf
    107 KB · Views: 2
Keep in mind they probably only design that supply with enough juice to barely power the gate drivers and feed the 5v regulator...if you drop the voltage enough from another load it could cause the FETs to be insufficiently driven, so they don't turn on all the way, and operate as resistors instead of switches..... :(

It could also affect the turn on/off timing, which probably wouldn't cause shoot thru, but might cause other similar issues.
Just a word of thanks to you too Amberwolf: you are a hero for following all these conversations and providing valuable feedback. Highly appreciated by us as a (sub)community!
 
Casainho implemented a much simpler method in the code
No, he just used the formula from the reference manual for small values.
In main.h I decreased the ‘maximum current’ by a factor of 10, and also halved the max positive and max negative values.
All this parameters aren't used in the rest of the code, so they can't have any influence on the behaviour.
The only appearance is in the main.h:
1746959729555.png

I can't remember, but I think this definitions are an artefact from Casainhos early code, that are not used in the recent code from me or @Xnyle

regards
stancecoke.
 
No, he just used the formula from the reference manual for small values.

All this parameters aren't used in the rest of the code, so they can't have any influence on the behaviour.
The only appearance is in the main.h:
View attachment 370067

I can't remember, but I think this definitions are an artefact from Casainhos early code, that are not used in the recent code from me or @Xnyle

regards
stancecoke.
With respect to dead time: oh ok, well the problem turned out to be caused by something else anyway, so in the end I did not need a longer dead time.

With respect to ‘the zero amps routine’: ok, that is strange, I noticed better/faster iteration to 50w (on lcd3 screen) from 250 (initially), so it seemed to work for me. But maybe it was due to the other changes then. I did notice a change in real losses from something like 3-5W to 0.5W or so (measured as voltage drop over an 8 ohm resistor), but maybe it was due to the other changes. It does not seem to do any harm, so I guess I will leave it this way then :)
 
Just a word of thanks to you too Amberwolf: you are a hero for following all these conversations and providing valuable feedback. Highly appreciated by us as a (sub)community!
You're welcome.

I wish I could get an enthusiastic community like this built around my wolfy-bot project (because I am still trying to learn enough coding to do much more than "hello world"...decades after I started learning).
 
Replacing the fried Ananda (MC5-B0) controller on my 20" foldable with a KT36/48SVPR-ffB10L (rated 7A and max 15±1A) paired with an Ananda M129VD 250W front-wheel hub, a KT-LCD5, a 36V 10.44Ah/374.4Wh battery and a (unknown, presumably Ananda too) PAS sensor on the left side of the crank. No torque-sensor and no throttle are installed though.

After many hours of researching (for some options finding the required information really was though lmao) I've put together a set of options and succesfully managed to flash it back to the controller, but I'm not completely sure I did everything right or not. I'll attach my generated config.h and experimental .ini-file to this post, please take a look and tell me if I did indeed manage to ferk it up before I blow up the third controller on this set-up x-X.
 

Attachments

  • config.h
    1.5 KB · Views: 2
  • 36V-20in-Ananda-Kunteng-15a.ini
    239 bytes · Views: 2
Replacing the fried Ananda (MC5-B0) controller on my 20" foldable with a KT36/48SVPR-ffB10L (rated 7A and max 15±1A) paired with an Ananda M129VD 250W front-wheel hub, a KT-LCD5, a 36V 10.44Ah/374.4Wh battery and a (unknown, presumably Ananda too) PAS sensor on the left side of the crank. No torque-sensor and no throttle are installed though.

After many hours of researching (for some options finding the required information really was though lmao) I've put together a set of options and succesfully managed to flash it back to the controller, but I'm not completely sure I did everything right or not. I'll attach my generated config.h and experimental .ini-file to this post, please take a look and tell me if I did indeed manage to ferk it up before I blow up the third controller on this set-up x-X.
My inexpert eye can't see any obvious horrors, but don't take that as gospel....
Does the P factor look a bit low at 0.1? Default is normally 0.5, not sure how much difference that actually makes, maybe you've lowered this intentionally?
 
My inexpert eye can't see any obvious horrors, but don't take that as gospel....
Does the P factor look a bit low at 0.1? Default is normally 0.5, not sure how much difference that actually makes, maybe you've lowered this intentionally?
Those two factors I used because (supposedly) these are confirmed to be safe and working.

Anywho, I got kinda impatient, hooked everything up and tried to power on the bike. The Display came on for a splitsecond one time, that's all it did. If I press the power-button again nothing else seems to happen. I'm hoping I've just missed or misunderstood a crucial setting and not accidentally fried controller #3 😅

Edit: so, the display came on for a second and immediately went off again. I have no clue where to go from here.
 
Last edited:
Edit: so, the display came on for a second and immediately went off again. I have no clue where to go from here.
This looks to me to be more likely to be a display/wiring/battery issue rather than the controller - with a long press on the power button (>3secs) the display powers up and sends the battery voltage down the Vin line to power up the controller. That's pretty much it, I don't *think* it waits for any confirmation from the controller, but I could be wrong.
Are you getting battery voltage on pin 1 (batt +ve) of the lcd connector? Is this then fed out to pin 2 (Vin) after a long press on the power button?
 
Back
Top