2018-11-19 00:16:19 +00:00
#!python2-32
2018-12-01 22:24:21 +00:00
from __future__ import print_function
2018-11-19 00:16:19 +00:00
from sys import argv , exit
2018-12-01 22:24:21 +00:00
import os
import argparse
from progressbar import ProgressBar
2018-11-19 00:16:19 +00:00
from py9b . link . base import LinkOpenException , LinkTimeoutException
from py9b . transport . base import BaseTransport as BT
from py9b . transport . xiaomi import XiaomiTransport
2018-12-07 00:38:16 +00:00
from py9b . transport . ninebot import NinebotTransport
2018-11-19 00:16:19 +00:00
from py9b . command . regio import ReadRegs , WriteRegs
from py9b . command . update import *
2018-12-01 22:24:21 +00:00
PING_RETRIES = 20
2019-10-20 06:49:22 +00:00
2018-11-19 00:16:19 +00:00
def checksum ( s , data ) :
2019-10-20 06:49:22 +00:00
for c in data :
s + = ord ( c )
return s & 0xFFFFFFFF
2018-11-19 00:16:19 +00:00
2018-12-01 22:24:21 +00:00
def UpdateFirmware ( link , tran , dev , fwfile ) :
2019-10-20 06:49:22 +00:00
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
2019-03-16 18:02:12 +00:00
2018-12-01 22:24:21 +00:00
2019-10-20 06:49:22 +00:00
##########################################################################################
2018-12-01 22:24:21 +00:00
2019-10-20 06:49:22 +00:00
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 "
" \n Example 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 "
" \n Example 3: %(prog)s -i serial -a COM2 esc CFW.bin - flash CFW.bin to ESC via COM2 "
" \n Example 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 " ) ,
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 ( )
2018-12-01 22:24:21 +00:00
args = parser . parse_args ( )
2019-10-20 06:49:22 +00:00
if args . device == " extbms " and args . protocol != " ninebot " :
exit ( " Only Ninebot supports External BMS ! " )
2018-12-07 00:56:11 +00:00
2018-12-01 22:24:21 +00:00
dev = devices . get ( args . device )
2019-10-20 06:49:22 +00:00
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 ( )
2018-12-07 00:38:16 +00:00
else :
2019-10-20 06:49:22 +00:00
exit ( " !!! BUG !!! Unknown interface selected: " + args . interface )
2019-03-16 18:02:12 +00:00
2018-12-01 22:24:21 +00:00
with link :
2019-10-20 06:49:22 +00:00
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