From 80431efe3cac531978e1ff1ff3b43cdf7a1216fe Mon Sep 17 00:00:00 2001 From: lakshmi_b06 Date: Fri, 12 Jun 2020 17:49:36 +0530 Subject: [PATCH 1/2] files compatible with python3 --- ble_legacy_dfu_controller.py | 2 +- ble_secure_dfu_controller.py | 23 ++++++++-------- dfu.py | 23 +++++++++------- nrf_ble_dfu_controller.py | 52 +++++++++++++++++++++++++----------- scan.py | 19 ++++++++++--- unpacker.py | 4 +-- util.py | 9 +++---- 7 files changed, 84 insertions(+), 48 deletions(-) diff --git a/ble_legacy_dfu_controller.py b/ble_legacy_dfu_controller.py index e315c10..edfc403 100644 --- a/ble_legacy_dfu_controller.py +++ b/ble_legacy_dfu_controller.py @@ -197,7 +197,7 @@ def check_DFU_mode(self): try: res = self.ble_conn.expect('handle:.*', timeout=10) # res = self.ble_conn.expect('handle:', timeout=10) - except pexpect.TIMEOUT, e: + except pexpect.TIMEOUT as e: print("State timeout") except: pass diff --git a/ble_secure_dfu_controller.py b/ble_secure_dfu_controller.py index e2aed62..8eeb5fa 100644 --- a/ble_secure_dfu_controller.py +++ b/ble_secure_dfu_controller.py @@ -1,6 +1,7 @@ import math import pexpect import time +import binascii from array import array from util import * @@ -71,9 +72,9 @@ def from_string(res_str): class BleDfuControllerSecure(NrfBleDfuController): # Class constants - UUID_BUTTONLESS = '8e400001-f315-4f60-9fb8-838830daea50' UUID_CONTROL_POINT = '8ec90001-f315-4f60-9fb8-838830daea50' UUID_PACKET = '8ec90002-f315-4f60-9fb8-838830daea50' + UUID_BUTTONLESS = '8ec90003-f315-4f60-9fb8-838830daea50' # Constructor inherited from abstract base class @@ -83,7 +84,6 @@ class BleDfuControllerSecure(NrfBleDfuController): def start(self): (_, self.ctrlpt_handle, self.ctrlpt_cccd_handle) = self._get_handles(self.UUID_CONTROL_POINT) (_, self.data_handle, _) = self._get_handles(self.UUID_PACKET) - if verbose: print('Control Point Handle: 0x%04x, CCCD: 0x%04x' % (self.ctrlpt_handle, self.ctrlpt_cccd_handle)) print('Packet handle: 0x%04x' % (self.data_handle)) @@ -111,8 +111,8 @@ def check_DFU_mode(self): dfu_mode = False try: - self.ble_conn.expect([self.UUID_BUTTONLESS], timeout=2) - except (pexpect.TIMEOUT , e): + self.ble_conn.expect([self.UUID_BUTTONLESS], timeout=5) + except pexpect.TIMEOUT as e: dfu_mode = True return dfu_mode @@ -120,14 +120,15 @@ def check_DFU_mode(self): def switch_to_dfu_mode(self): (_, bl_value_handle, bl_cccd_handle) = self._get_handles(self.UUID_BUTTONLESS) - self._enable_notifications(bl_cccd_handle) + self._enable_indications(bl_cccd_handle) # Reset the board in DFU mode. After reset the board will be disconnected cmd = 'char-write-req 0x%04x 01' % (bl_value_handle) self.ble_conn.sendline(cmd) # Wait some time for board to reboot - time.sleep(0.5) + time.sleep(1) + print("Switched to DFU mode Successfully") # Increase the mac address by one and reconnect self.target_mac_increase(1) @@ -189,7 +190,7 @@ def _wait_and_parse_notify(self): if result[1] != Results.SUCCESS: raise Exception("Error in {} procedure, reason: {}".format( Procedures.to_string(result[0]), - Results.to_string(result[1]))) + Results.to_string(result[1]))) return result @@ -274,14 +275,14 @@ def _dfu_send_object(self, offset, obj_max_size): if offset == 0 or offset >= obj_max_size or crc32 != crc32_unsigned(self.bin_array[0:offset]): # Create Data Object size = min(obj_max_size, self.image_size - offset) - self._dfu_send_command(Procedures.CREATE, [Procedures.PARAM_DATA] + uint32_to_bytes_le(size)) + self._dfu_send_command(Procedures.CREATE, [Procedures.PARAM_DATA] + uint32_to_bytes_le(int(size))) self._wait_and_parse_notify() segment_count = 0 segment_total = int(math.ceil(min(obj_max_size, self.image_size-offset)/float(self.pkt_payload_size))) - segment_begin = offset - segment_end = min(offset+obj_max_size, self.image_size) + segment_begin = int(offset) + segment_end = int(min(offset+obj_max_size, self.image_size)) for i in range(segment_begin, segment_end, self.pkt_payload_size): num_bytes = min(self.pkt_payload_size, segment_end - i) @@ -301,7 +302,7 @@ def _dfu_send_object(self, offset, obj_max_size): if res != Results.SUCCESS: raise Exception("bad notification status: {}".format(Results.to_string(res))) - + if crc32 != crc32_unsigned(self.bin_array[0:offset]): # Something went wrong, need to re-transmit this object return 0 diff --git a/dfu.py b/dfu.py index b0be5dd..7c67589 100755 --- a/dfu.py +++ b/dfu.py @@ -19,7 +19,7 @@ def main(): - init_msg = """ + init_msg = """ ================================ == == == DFU Server == @@ -81,7 +81,7 @@ def main(): options, args = parser.parse_args() - except (Exception , e): + except Exception as e: print(e) print("For help use --help") sys.exit(2) @@ -108,7 +108,7 @@ def main(): #print(options.zipfile) try: hexfile, datfile = unpacker.unpack_zipfile(options.zipfile) - except (Exception, e): + except Exception as e: print("ERR") print(e) pass @@ -137,6 +137,7 @@ def main(): else: ble_dfu = BleDfuControllerLegacy(options.address.upper(), hexfile, datfile) + # Initialize inputs ble_dfu.input_setup() @@ -147,21 +148,25 @@ def main(): success = ble_dfu.switch_to_dfu_mode() if not success: print("Couldn't reconnect") + else: + ble_dfu.start() + else: + ble_dfu.start() else: + print("Couldn't connect, will try DFU MAC") # The device might already be in DFU mode (MAC + 1) ble_dfu.target_mac_increase(1) # Try connection with new address - print("Couldn't connect, will try DFU MAC") - if not ble_dfu.scan_and_connect(): + if ble_dfu.scan_and_connect(): + ble_dfu.start() + else: raise Exception("Can't connect to device") - ble_dfu.start() - # Disconnect from peer device if not done already and clean up. ble_dfu.disconnect() - except (Exception, e): + except Exception as e: # print(traceback.format_exc()) print("Exception at line {}: {}".format(sys.exc_info()[2].tb_lineno, e)) pass @@ -171,7 +176,7 @@ def main(): # If Unpacker for zipfile used then delete Unpacker if unpacker != None: - unpacker.delete() + unpacker.delete() print("DFU Server done") diff --git a/nrf_ble_dfu_controller.py b/nrf_ble_dfu_controller.py index b1b2671..d0fade8 100644 --- a/nrf_ble_dfu_controller.py +++ b/nrf_ble_dfu_controller.py @@ -1,10 +1,12 @@ import os import pexpect import re +import zlib from abc import ABCMeta, abstractmethod from array import array from util import * +from scan import Scan verbose = False @@ -60,9 +62,9 @@ def __init__(self, target_mac, firmware_path, datfile_path): self.firmware_path = firmware_path self.datfile_path = datfile_path - - self.ble_conn = pexpect.spawn("sudo hcitool lescan") - self.ble_conn = pexpect.spawn("gatttool -b '%s' --interactive" % target_mac) + self.scan_obj = Scan(None) + scan_list = self.scan_obj.scan() + self.ble_conn = pexpect.spawn("gatttool -b '%s' -t random --interactive" % target_mac) self.ble_conn.delaybeforesend = 0 # -------------------------------------------------------------------------- @@ -105,7 +107,6 @@ def input_setup(self): self.image_size = len(self.bin_array) print("Binary imge size: %d" % self.image_size) - print("Binary CRC32: %d" % crc32_unsigned(array_to_hex_string(self.bin_array))) return @@ -122,21 +123,21 @@ def input_setup(self): # Perform a scan and connect via gatttool. # Will return True if a connection was established, False otherwise # -------------------------------------------------------------------------- - def scan_and_connect(self, timeout=2): + def scan_and_connect(self, timeout=30): if verbose: print("scan_and_connect") print("Connecting to %s" % (self.target_mac)) try: self.ble_conn.expect('\[LE\]>', timeout=timeout) - except (pexpect.TIMEOUT, e): + except pexpect.TIMEOUT as e: return False self.ble_conn.sendline('connect') try: res = self.ble_conn.expect('.*Connection successful.*', timeout=timeout) - except (pexpect.TIMEOUT, e): + except pexpect.TIMEOUT as e: return False return True @@ -153,8 +154,7 @@ def target_mac_increase(self, inc): # Re-start gatttool with the new address self.disconnect() - self.ble_conn = pexpect.spawn("sudo hcitool lescan") - self.ble_conn = pexpect.spawn("gatttool -b '%s' --interactive" % self.target_mac) + self.ble_conn = pexpect.spawn("gatttool -b '%s' -t random --interactive" % self.target_mac) self.ble_conn.delaybeforesend = 0 # -------------------------------------------------------------------------- @@ -167,10 +167,11 @@ def _get_handles(self, uuid): self.ble_conn.sendline('characteristics') try: - self.ble_conn.expect([uuid], timeout=2) - handles = re.findall('.*handle: (0x....),.*char value handle: (0x....)', self.ble_conn.before) + self.ble_conn.expect([uuid], timeout=10) + handles = re.findall('.*handle: (0x....),.*char value handle: (0x....)', self.ble_conn.before.decode('utf-8')) (handle, value_handle) = handles[-1] - except (pexpect.TIMEOUT, e): + + except pexpect.TIMEOUT as e: raise Exception("UUID not found: {}".format(uuid)) return (int(handle, 16), int(value_handle, 16), int(value_handle, 16)+1) @@ -202,7 +203,7 @@ def _dfu_wait_for_notify(self): # continue to wait. # self.ble_conn.sendline('') - string = self.ble_conn.before + string = self.ble_conn.before.decode('utf-8') if '[ ]' in string: print('Connection lost! ') raise Exception('Connection Lost') @@ -211,7 +212,7 @@ def _dfu_wait_for_notify(self): if index == 0: after = self.ble_conn.after hxstr = after.split()[3:] - handle = long(float.fromhex(hxstr[0])) + handle = int(float.fromhex(hxstr[0].decode('utf-8'))) return hxstr[2:] else: @@ -234,7 +235,8 @@ def _dfu_send_command(self, procedure, params=[]): # Verify that command was successfully written try: res = self.ble_conn.expect('Characteristic value was written successfully.*', timeout=10) - except (pexpect.TIMEOUT, e): + except pexpect.TIMEOUT as e: + print("State timeout") # -------------------------------------------------------------------------- @@ -264,5 +266,23 @@ def _enable_notifications(self, cccd_handle): # Verify that command was successfully written try: res = self.ble_conn.expect('Characteristic value was written successfully.*', timeout=10) - except (pexpect.TIMEOUT, e): + except pexpect.TIMEOUT as e: + print("State timeout") + + # -------------------------------------------------------------------------- + # Enable notifications from the Control Point Handle + # -------------------------------------------------------------------------- + def _enable_indications(self, cccd_handle): + if verbose: print('_enable_notifications') + + cmd = 'char-write-req 0x%04x %s' % (cccd_handle, '0200') + + if verbose: print(cmd) + + self.ble_conn.sendline(cmd) + + # Verify that command was successfully written + try: + res = self.ble_conn.expect('Characteristic value was written successfully.*', timeout=10) + except pexpect.TIMEOUT as e: print("State timeout") diff --git a/scan.py b/scan.py index 12f576b..17d3aab 100755 --- a/scan.py +++ b/scan.py @@ -8,6 +8,8 @@ import pexpect import signal +import sys +import time #------------------------------------------------------------------------------ # Bluetooth LE scan for advertising peripheral devices @@ -21,10 +23,15 @@ def __init__( self, advert_name ): def scan( self ): try: - self.hcitool = pexpect.spawn('hcitool lescan') + self.hcitool = pexpect.spawn('hciconfig hci0 down') + self.hcitool = pexpect.spawn('hciconfig hci0 up') + self.hcitool = pexpect.spawn('hcitool lescan') #self.hcitool.logfile = sys.stdout - index = self.hcitool.expect(['LE Scan ...'], 1) + index = self.hcitool.expect(['LE Scan ...']) + time.sleep(2) + except pexpect.EOF: + self.hcitool.terminate(force=True) return [] except Exception as err: @@ -38,10 +45,13 @@ def scan( self ): return [] list = [] - for dummy in range(0, 2): + for dummy in range(0, 4): + #print("for loop:",self.hcitool.readline()) try: list.append(self.hcitool.readline()) + #print("list:",list) except pexpect.TIMEOUT: + return list break if list == []: @@ -60,7 +70,7 @@ def scan( self ): # Strip newline from items in list list = [item.strip() for item in list] - list = list[0:2] + #list = list # Close pexpect (release device for subsequent use) self.hcitool.terminate(force=True) @@ -91,6 +101,7 @@ def scan(self): print("scan: pexpect.TIMEOUT") pass except Exception as e: + print(e) print("scan: exception: {0} ".format(sys.exc_info()[0])) pass diff --git a/unpacker.py b/unpacker.py index 1df03fd..f0a34df 100755 --- a/unpacker.py +++ b/unpacker.py @@ -15,7 +15,7 @@ class Unpacker(object): # #-------------------------------------------------------------------------- def entropy(self, length): - return ''.join(random.choice(string.lowercase) for i in range (length)) + return ''.join(random.choice(string.ascii_lowercase) for i in range (length)) #-------------------------------------------------------------------------- # @@ -26,7 +26,7 @@ def unpack_zipfile(self, file): raise Exception("Error: file, not found!") # Create unique working direction into which the zip file is expanded - self.unzip_dir = "{0}/{1}_{2}".format(tempfile.gettempdir(), os.path.splitext(basename(file))[0], self.entropy(6)) + self.unzip_dir = ("{0}/{1}_{2}".format(tempfile.gettempdir(), os.path.splitext(basename(file))[0], self.entropy(6))) datfilename = "" binfilename = "" diff --git a/util.py b/util.py index b67da7f..34fd669 100644 --- a/util.py +++ b/util.py @@ -29,15 +29,14 @@ def array_to_hex_string(arr): return hex_str def crc32_unsigned(bytestring): - return binascii.crc32(bytestring) % (1 << 32) + return binascii.crc32(bytestring) % (1 << 32) def mac_string_to_uint(mac): parts = list(re.match('(..):(..):(..):(..):(..):(..)', mac).groups()) - ints = map(lambda x: int(x, 16), parts) - + ints = list(map(lambda x: int(x, 16), parts)) res = 0 - for i in range(0, len(ints)): - res += (ints[len(ints)-1 - i] << 8*i) + for i in range(0, len((ints))): + res += (ints[len((ints))-1 - i] << 8*i) return res From 57d55d0e14abddd090bd42fbd7130c9f2b006c6c Mon Sep 17 00:00:00 2001 From: lakshmi_b06 Date: Thu, 25 Jun 2020 10:02:10 +0530 Subject: [PATCH 2/2] scan file updated --- app_vsens.zip | Bin 0 -> 38216 bytes application.json | 0 dfu.py | 1 + manifest.json | 8 ++++++++ nrf_ble_dfu_controller.py | 9 ++++++--- scan.py | 3 ++- 6 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 app_vsens.zip create mode 100644 application.json create mode 100644 manifest.json diff --git a/app_vsens.zip b/app_vsens.zip new file mode 100644 index 0000000000000000000000000000000000000000..8974bc8a27928b1c639d16552948cb2b71871f83 GIT binary patch literal 38216 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', timeout=timeout) except pexpect.TIMEOUT as e: + print("scan error:",e) return False self.ble_conn.sendline('connect') try: res = self.ble_conn.expect('.*Connection successful.*', timeout=timeout) - except pexpect.TIMEOUT as e: + print("Connection successfull") + except pexpect.TIMEOUT as e: return False return True diff --git a/scan.py b/scan.py index 17d3aab..9e67b7a 100755 --- a/scan.py +++ b/scan.py @@ -28,7 +28,8 @@ def scan( self ): self.hcitool = pexpect.spawn('hcitool lescan') #self.hcitool.logfile = sys.stdout index = self.hcitool.expect(['LE Scan ...']) - time.sleep(2) + time.sleep(10) + print("scan after:",self.hcitool.after) except pexpect.EOF: