Xiaomi M365 compatible BMS
Warning: This project is meant for people with electronics and microcontroller knowledge!
This repository contains alternative firmware for the following BMS hardware: SP15SV0001-LLT.
It's programmed with the Arduino platform and is built with PlatformIO.
It runs on an ATMega328p MCU and controls a TI BQ769x0 battery monitoring IC over I²C.
This is a fully fledged replacement BMS for the Xiaomi M365 that implements their proprietary BMS protocol and supports all of the features you'd expect, like:
- Battery SOC (State Of Charge: mAh, %) using Coulomb Counting
- Pack Voltage, Cell Voltages, Current, Temperature
- Discharge and Charge cycles
All the battery information can be viewed in the native apps or 3rd party apps like m365 Tools.
Here's some pr0n of my 12S4P NCR18650B battery: Gallery.
Materials I used:
- Malectrics DIY Arduino Battery Spot Welder
- 8mm*0.2mm pure nickel strip (0.15mm is easier to weld)
- 18650 positive insulation pad important
- 125mm/80mm Battery PVC shrink wrap
- 50mm kapton tape
- I used automotive windshield glue that I had lying around, anything that sticks well to PVC should work.
- 18650 Cells:
- I used NCR18650B because I got them cheap.
- Good price/performance: Samsung INR18650-29E
- Charger: Mean Well HLG-240H-48A
- I paid 45€ for it at getgoods.com but pricing seems to vary a lot by country.
- Look for another CC/CV power supply if this one is too expensive.
The BMS uses an ATMega328p MCU without an external crystal and thus has to use the internal 8 MHz resonator.
However at 8 MHz the hardware UART will not function at 115200 baud, which is the baudrate the M365 controller expects.
The frequency of the internal resonator can be calibrated (manually with an oscilloscope or logic analyzer) using OSCCAL to still make this work.
But instead we simply patch the M365 firmware to use 76800 baud which works just fine with the internal 8 MHz resonator and no calibration trouble.
Patch your firmware at m365beta.botox.bz and use the "Change ESC<->BMS baud rate to 76800" option.
Thanks to Oleg for the idea and help.
- The BMS itself: SP15SV0001-LLT
- 10S - 13S. 30A version is recommended, >30A versions have both sides of the BMS PCB populated with MOSFETs and will not fit in the limited space of the M365.
- An ISP programmer with a 6pin ISP cable/adapter, example: Aliexpress
- Serial UART adapter, sold by the BMS shop or on Aliexpress
Current Shunt resistors
By default the BMS comes with ten 4mOhm shunt resistors in parallel. This results in a shunt resistance of 0.4mOhm, this is too small for accurate coulomb counting. In addition the voltage coming from the shunt resistors is also cut in half.
A simple fix for this is to simply remove six of the ten 4mOhm resistors, this leave four 4mOhm resistors. Thus the shunt resistance will be 1mOhm.
And to fix the shunt voltage from getting cut in half two more small resistors are removed from the top layer, as in the pictures: Bottom and Top.
IMPORTANT: The M365 ESC - connects to P- of the BMS!
Otherwise the M365 will be damaged when you brake with a full battery due to overvoltage!
C- has to be used to charge the battery, otherwise the BMS offers no protection against faulty chargers/overvoltage!
Do not cut the big - trace on the M365 ESC or it will damage the BMS because of different GND potentials on UART!
Do not connect GND from the BMS to the M365 anywhere! P- is GND for the M365! The only extra wires going from the BMS to the M365 are RX and TX!
Depending on your battery you might want to configure some of the settings here: src/main.h
capacity: The actual total capacity in mAh of your battery pack, don't use the manufacturer stated capacity if you want to have accurate SOC but rather look at sites like lygte-info.dk, etc.
- Example: for 4 * NCR18650B I used a value of 12400mAh.
nominal_voltage: The nominal voltage in mV of your cells, this will be 3.6V for almost all cells.
full_voltage: The voltage in mV that you will charge your cells to. I only charge mine to 4.1V for cycle life.
ODP_current: Over current protection value in mA, if your scooter is shutting off on your crazy settings then make this higher.
UVP_voltage: The BMS will shut off P- when any cells voltage goes below this.
- Remember: During load the cell voltage will drop a lot.
OVP_voltage: The BMS will shut off C- when any cells voltage goes above this.
- So do not connect your M365 ESC to C- or it'll die when you brake with a full battery, use P- for the ESC and C- to charge!
Connect your ISP programmer to the BMS, you can (and should) keep the battery disconnected while programming.
Here's the pinout of the ISP header: Image
You can also use any other program to flash the .hex file generated by PlatformIO to your BMS.
** Make sure NOT to change the fuses on your BMS! **
If you program wrong fuses you can brick your BMS.
For example: The BMS doesn't have an external oscillator, if you program fuses that try to use the external oscillator your BMS will be bricked until you connect an external oscillator to it!
The default fuses are: Low = 0xE2, High = 0xDA, Extended = 0xFD, Lockbits = 0xFE
Have you patched your M365 firmware for 76800 baud?
Make sure the temperature sensors are plugged in and all wires are connected properly. B- needs to be connected to the battery -.
Reset the BMS by shorting GND with RST on the ISP header.
configtool.py in an interactive python shell (IDLE on windows) and configure your COM port in there correctly first.
You'll probably have to install these two dependencies: cstruct and pyserial.
Check the source code for commands you can use, though you'll probably only need
You can also use any serial terminal and send this string in HEX
55aa0322fa0500dbfe instead of
After you've checked that your BMS works (voltage between + and P-) and communicates via UART (it print's
BOOTED! when it boots) you can connect it with your M365.
The original BMS was connected to the top 3pin header on the M365 ESC: Image
You'll have to connect the ESC R pin to the BMS TX pin and the T pin to the RX pin.
The ESC L pin is + for the red/brake light on the back fender, you'll have to make your own cable for that.
A big part of the BQ769x0 code is taken from here: LibreSolar/bq769x0_mbed_lib.
If you've spent at least an hour with your issue you can ask about it nicely in my M365 Telegram group.
If you break anything it's your own fault.
Works for me™. is the only guarantee I can give you.
I am in no way affiliated with the company that makes the BMS. I just bought it, reversed some stuff and made this firmware.