Piotr Dobrowolski
bdeb675bf3
Sadly bleak link implementation doesn't seem to work good enough to flash yet, but fwupd of ESC and BLE over serial has been confirmed to work fine.
180 lines
5.3 KiB
Python
180 lines
5.3 KiB
Python
#!python2-32
|
|
from __future__ import print_function
|
|
from sys import argv, exit
|
|
import os
|
|
import argparse
|
|
from progressbar import ProgressBar
|
|
|
|
from py9b.link.base import LinkOpenException, LinkTimeoutException
|
|
from py9b.transport.base import BaseTransport as BT
|
|
from py9b.transport.xiaomi import XiaomiTransport
|
|
from py9b.transport.ninebot import NinebotTransport
|
|
from py9b.command.regio import ReadRegs, WriteRegs
|
|
from py9b.command.update import *
|
|
|
|
PING_RETRIES = 20
|
|
|
|
|
|
def checksum(s, data):
|
|
for c in data:
|
|
s += c
|
|
return s & 0xFFFFFFFF
|
|
|
|
|
|
def UpdateFirmware(link, tran, dev, fwfile):
|
|
fwfile.seek(0, os.SEEK_END)
|
|
fw_size = fwfile.tell()
|
|
fwfile.seek(0)
|
|
fw_page_size = 0x80
|
|
|
|
print("Pinging...", end="")
|
|
for retry in range(PING_RETRIES):
|
|
print(".", end="")
|
|
try:
|
|
if dev == BT.BLE:
|
|
tran.execute(ReadRegs(dev, 0, "13s"))
|
|
else:
|
|
tran.execute(ReadRegs(dev, 0x10, "14s"))
|
|
except LinkTimeoutException:
|
|
continue
|
|
break
|
|
else:
|
|
print("Timed out !")
|
|
return False
|
|
print("OK")
|
|
|
|
if args.interface != "blefleet":
|
|
print("Locking...")
|
|
tran.execute(WriteRegs(BT.ESC, 0x70, "<H", 0x0001))
|
|
else:
|
|
print("Not Locking...")
|
|
|
|
print("Starting...")
|
|
tran.execute(StartUpdate(dev, fw_size))
|
|
|
|
print("Writing...")
|
|
pb = ProgressBar(maxval=fw_size // fw_page_size + 1).start()
|
|
page = 0
|
|
chk = 0
|
|
while fw_size:
|
|
pb.update(page)
|
|
chunk_sz = min(fw_size, fw_page_size)
|
|
data = fwfile.read(chunk_sz)
|
|
chk = checksum(chk, data)
|
|
# tran.execute(WriteUpdate(dev, page, data))
|
|
tran.execute(
|
|
WriteUpdate(dev, page, data + b"\x00" * (fw_page_size - chunk_sz))
|
|
) # TODO: Ninebot wants this padding. Will it work on M365 too?
|
|
page += 1
|
|
fw_size -= chunk_sz
|
|
pb.finish()
|
|
|
|
print("Finalizing...")
|
|
tran.execute(FinishUpdate(dev, chk ^ 0xFFFFFFFF))
|
|
|
|
print("Reboot")
|
|
tran.execute(RebootUpdate(dev))
|
|
print("Done")
|
|
return True
|
|
|
|
|
|
##########################################################################################
|
|
|
|
parser = argparse.ArgumentParser(
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
description="Xiaomi/Ninebot firmware flasher",
|
|
epilog="Example 1: %(prog)s ble ble_patched.bin - flash ble_patched.bin to BLE using default communication parameters"
|
|
"\nExample 2: %(prog)s -i tcp -a 127.0.1.10:6000 bms bms115.bin - flash bms115.bin to BMS over TCP-BLE bridge at 127.0.1.10:6000"
|
|
"\nExample 3: %(prog)s -i serial -a COM2 esc CFW.bin - flash CFW.bin to ESC via COM2"
|
|
"\nExample 4: %(prog)s -i ble -a 12:34:56:78:9A:BC -p ninebot extbms bms107.bin - flash bms107.bin to Ninebot's external BMS via BLE, use specified BLE address",
|
|
)
|
|
|
|
devices = {"ble": BT.BLE, "esc": BT.ESC, "bms": BT.BMS, "extbms": BT.EXTBMS}
|
|
parser.add_argument("device", help="target device", type=str.lower, choices=devices)
|
|
|
|
parser.add_argument("file", type=argparse.FileType("rb"), help="firmware file")
|
|
|
|
parser.add_argument(
|
|
"-i",
|
|
"--interface",
|
|
help="communication interface, default: %(default)s",
|
|
type=str.lower,
|
|
choices=("ble", "serial", "tcp", "blefleet", "bleak"),
|
|
default="ble",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"-a",
|
|
"--address",
|
|
help="communication address (ble: BDADDR, serial: port, tcp: host:port), default: first available",
|
|
)
|
|
|
|
protocols = {"xiaomi": XiaomiTransport, "ninebot": NinebotTransport}
|
|
parser.add_argument(
|
|
"-p",
|
|
"--protocol",
|
|
help="communication protocol, default: %(default)s",
|
|
type=str.lower,
|
|
choices=protocols,
|
|
default="xiaomi",
|
|
)
|
|
|
|
if len(argv) == 1:
|
|
parser.print_usage()
|
|
exit()
|
|
args = parser.parse_args()
|
|
|
|
if args.device == "extbms" and args.protocol != "ninebot":
|
|
exit("Only Ninebot supports External BMS !")
|
|
|
|
dev = devices.get(args.device)
|
|
|
|
if args.interface == "ble":
|
|
try:
|
|
from py9b.link.ble import BLELink
|
|
except:
|
|
exit("BLE is not supported on your system !")
|
|
link = BLELink()
|
|
elif args.interface == "tcp":
|
|
from py9b.link.tcp import TCPLink
|
|
|
|
link = TCPLink()
|
|
elif args.interface == "serial":
|
|
from py9b.link.serial import SerialLink
|
|
|
|
link = SerialLink()
|
|
elif args.interface == "blefleet":
|
|
try:
|
|
from py9b.link.blefleet import BLELink
|
|
except:
|
|
exit("BLE is not supported on your system !")
|
|
link = BLELink()
|
|
elif args.interface == "bleak":
|
|
try:
|
|
from py9b.link.bleak import BleakLink
|
|
except:
|
|
exit("BLE is not supported on your system !")
|
|
link = BleakLink()
|
|
else:
|
|
exit("!!! BUG !!! Unknown interface selected: " + args.interface)
|
|
|
|
with link:
|
|
tran = protocols.get(args.protocol)(link)
|
|
|
|
if args.address:
|
|
addr = args.address
|
|
else:
|
|
print("Scanning...")
|
|
ports = link.scan()
|
|
if not ports:
|
|
exit("No interfaces found !")
|
|
print("Connecting to", ports[0][0])
|
|
addr = ports[0][1]
|
|
|
|
link.open(addr)
|
|
print("Connected")
|
|
try:
|
|
UpdateFirmware(link, tran, dev, args.file)
|
|
except Exception as e:
|
|
print("Error:", e)
|
|
raise
|