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
2018-11-19 00:16:19 +00:00
def checksum ( s , data ) :
for c in data :
s + = ord ( c )
return ( s & 0xFFFFFFFF )
2018-12-01 22:24:21 +00:00
def UpdateFirmware ( link , tran , dev , fwfile ) :
fwfile . seek ( 0 , os . SEEK_END )
fw_size = fwfile . tell ( )
fwfile . seek ( 0 )
fw_page_size = 0x80
2018-11-19 00:16:19 +00:00
2018-12-01 22:24:21 +00:00
print ( ' Pinging... ' , end = ' ' )
for retry in range ( PING_RETRIES ) :
print ( ' . ' , end = ' ' )
2018-11-19 00:16:19 +00:00
try :
2018-12-01 22:24:21 +00:00
if dev == BT . BLE :
tran . execute ( ReadRegs ( dev , 0 , ' 13s ' ) )
else :
tran . execute ( ReadRegs ( dev , 0x10 , ' 14s ' ) )
2018-11-19 00:16:19 +00:00
except LinkTimeoutException :
continue
break
else :
2018-12-01 22:24:21 +00:00
print ( ' Timed out ! ' )
return False
print ( ' OK ' )
2018-11-19 00:16:19 +00:00
2018-12-01 22:24:21 +00:00
print ( ' Locking... ' )
tran . execute ( WriteRegs ( BT . ESC , 0x70 , ' <H ' , 0x0001 ) )
print ( ' Starting... ' )
tran . execute ( StartUpdate ( dev , fw_size ) )
2018-11-19 00:16:19 +00:00
2018-12-01 22:24:21 +00:00
print ( ' Writing... ' )
pb = ProgressBar ( maxval = fw_size / / fw_page_size + 1 ) . start ( )
2018-11-19 00:16:19 +00:00
page = 0
chk = 0
while fw_size :
2018-12-01 22:24:21 +00:00
pb . update ( page )
2018-11-19 00:16:19 +00:00
chunk_sz = min ( fw_size , fw_page_size )
2018-12-01 22:24:21 +00:00
data = fwfile . read ( chunk_sz )
2018-11-19 00:16:19 +00:00
chk = checksum ( chk , data )
2018-12-07 00:38:16 +00:00
#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?
2018-11-19 00:16:19 +00:00
page + = 1
fw_size - = chunk_sz
2018-12-01 22:24:21 +00:00
pb . finish ( )
print ( ' Finalizing... ' )
tran . execute ( FinishUpdate ( dev , chk ^ 0xFFFFFFFF ) )
print ( ' Reboot ' )
tran . execute ( RebootUpdate ( dev ) )
print ( ' Done ' )
return True
##########################################################################################
2018-11-19 00:16:19 +00:00
2018-12-01 22:24:21 +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 192.168.1.10:6000 bms bms115.bin - flash bms115.bin to BMS over TCP-BLE bridge at 192.168.1.10:6000 '
' \n Example 3: %(prog)s -i serial -a COM2 esc CFW.bin - flash CFW.bin to ESC via COM2 '
2018-12-07 00:56:11 +00:00
' \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 ' )
2018-12-01 22:24:21 +00:00
2018-12-07 00:56:11 +00:00
devices = { ' ble ' : BT . BLE , ' esc ' : BT . ESC , ' bms ' : BT . BMS , ' extbms ' : BT . EXTBMS }
2018-12-01 22:24:21 +00:00
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 ,
2018-12-07 00:38:16 +00:00
choices = ( ' ble ' , ' serial ' , ' tcp ' ) , default = ' ble ' )
2018-12-01 22:24:21 +00:00
parser . add_argument ( ' -a ' , ' --address ' , help = ' communication address (ble: BDADDR, serial: port, tcp: host:port), default: first available ' )
2018-12-07 00:38:16 +00:00
protocols = { ' xiaomi ' : XiaomiTransport , ' ninebot ' : NinebotTransport }
2018-12-01 22:24:21 +00:00
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 ( )
2018-12-07 00:56:11 +00:00
if args . device == ' extbms ' and args . protocol != ' ninebot ' :
exit ( ' Only Ninebot supports External BMS ! ' )
2018-12-01 22:24:21 +00:00
dev = devices . get ( args . device )
2018-12-07 00:38:16 +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 ( )
else :
exit ( ' !!! BUG !!! Unknown interface selected: ' + args . interface )
2018-12-01 22:24:21 +00:00
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 ]
2018-11-19 00:16:19 +00:00
2018-12-01 22:24:21 +00:00
link . open ( addr )
print ( ' Connected ' )
try :
UpdateFirmware ( link , tran , dev , args . file )
except Exception as e :
print ( ' Error: ' , e )
2018-12-07 00:38:16 +00:00
raise