Xiaomi M365 compatible, open source firmware for ATMega328p+BQ769x0 BMS
Branch: master
Clone or download
Type Name Latest commit message Commit time
Failed to load latest commit information.
lib initial commit Jul 11, 2018
src Update README. Jan 27, 2019
README.md Add some material links to README. Feb 6, 2019
configtool.py 76800 baud without calibration but m365 esc firmware patch Dec 8, 2018
platformio.ini Finally made a README. Nov 28, 2018


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:

The caveat

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!


This project uses PlatformIO, please check out their Documentation to get started.

ISP programming

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 use PlatformIO to program the BMS.
You will probably have to adjust the upload_port (COM port) in the platformio.ini file.
Please do check out the Documentation from PlatformIO.

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.

Run the 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 debug_print(). You can also use any serial terminal and send this string in HEX 55aa0322fa0500dbfe instead of debug_print().


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.

Final words


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.