1#!/usr/bin/env python 2# 3# BLE GATT configuration generator for use with BTstack, v0.1 4# Copyright 2011 Matthias Ringwald 5# 6# Format of input file: 7# PRIMARY_SERVICE, SERVICE_UUID 8# CHARACTERISTIC, ATTRIBUTE_TYPE_UUID, [READ | WRITE | DYNAMIC], VALUE 9 10import codecs 11import csv 12import io 13import os 14import re 15import string 16import sys 17 18header = ''' 19// {0} generated from {1} for BTstack 20 21// binary representation 22// attribute size in bytes (16), flags(16), handle (16), uuid (16/128), value(...) 23 24#include <stdint.h> 25 26const uint8_t profile_data[] = 27''' 28 29usage = ''' 30Usage: ./compile_gatt.py profile.gatt profile.h 31''' 32 33 34print(''' 35BLE configuration generator for use with BTstack, v0.1 36Copyright 2011 Matthias Ringwald 37''') 38 39assigned_uuids = { 40 'GAP_SERVICE' : 0x1800, 41 'GATT_SERVICE' : 0x1801, 42 'GAP_DEVICE_NAME' : 0x2a00, 43 'GAP_APPEARANCE' : 0x2a01, 44 'GAP_PERIPHERAL_PRIVACY_FLAG' : 0x2A02, 45 'GAP_RECONNECTION_ADDRESS' : 0x2A03, 46 'GAP_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS' : 0x2A04, 47 'GATT_SERVICE_CHANGED' : 0x2a05, 48} 49 50property_flags = { 51 # GATT Characteristic Properties 52 'BROADCAST' : 0x01, 53 'READ' : 0x02, 54 'WRITE_WITHOUT_RESPONSE' : 0x04, 55 'WRITE' : 0x08, 56 'NOTIFY': 0x10, 57 'INDICATE' : 0x20, 58 'AUTHENTICATED_SIGNED_WRITE' : 0x40, 59 'EXTENDED_PROPERTIES' : 0x80, 60 # custom BTstack extension 61 'DYNAMIC': 0x100, 62 'LONG_UUID': 0x200, 63 'AUTHENTICATION_REQUIRED': 0x400, 64 'AUTHORIZATION_REQUIRED': 0x800, 65 'ENCRYPTION_KEY_SIZE_7': 0x6000, 66 'ENCRYPTION_KEY_SIZE_8': 0x7000, 67 'ENCRYPTION_KEY_SIZE_9': 0x8000, 68 'ENCRYPTION_KEY_SIZE_10': 0x9000, 69 'ENCRYPTION_KEY_SIZE_11': 0xa000, 70 'ENCRYPTION_KEY_SIZE_12': 0xb000, 71 'ENCRYPTION_KEY_SIZE_13': 0xc000, 72 'ENCRYPTION_KEY_SIZE_14': 0xd000, 73 'ENCRYPTION_KEY_SIZE_15': 0xe000, 74 'ENCRYPTION_KEY_SIZE_16': 0xf000, 75 76 # only used by gatt compiler >= 0xffff 77 # Extended Properties 78 'RELIABLE_WRITE': 0x10000, 79 80 # Broadcast, Notify, Indicate, Extended Properties are only used to describe a GATT Characteristic, but are free to use with att_db 81 'READ_WITHOUT_AUTHENTICATION': 0x0001, 82 # 0x10 83 # 0x20 84 # 0x80 85} 86 87btstack_root = '' 88services = dict() 89characteristic_indices = dict() 90presentation_formats = dict() 91current_service_uuid_string = "" 92current_service_start_handle = 0 93current_characteristic_uuid_string = "" 94defines_for_characteristics = [] 95defines_for_services = [] 96 97handle = 1 98total_size = 0 99 100def read_defines(infile): 101 defines = dict() 102 with open (infile, 'rt') as fin: 103 for line in fin: 104 parts = re.match('#define\s+(\w+)\s+(\w+)',line) 105 if parts and len(parts.groups()) == 2: 106 (key, value) = parts.groups() 107 defines[key] = int(value, 16) 108 return defines 109 110def keyForUUID(uuid): 111 keyUUID = "" 112 for i in uuid: 113 keyUUID += "%02x" % i 114 return keyUUID 115 116def c_string_for_uuid(uuid): 117 return uuid.replace('-', '_') 118 119def twoByteLEFor(value): 120 return [ (value & 0xff), (value >> 8)] 121 122def is_128bit_uuid(text): 123 if re.match("[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}", text): 124 return True 125 return False 126 127def parseUUID128(uuid): 128 parts = re.match("([0-9A-Fa-f]{4})([0-9A-Fa-f]{4})-([0-9A-Fa-f]{4})-([0-9A-Fa-f]{4})-([0-9A-Fa-f]{4})-([0-9A-Fa-f]{4})([0-9A-Fa-f]{4})([0-9A-Fa-f]{4})", uuid) 129 uuid_bytes = [] 130 for i in range(8, 0, -1): 131 uuid_bytes = uuid_bytes + twoByteLEFor(int(parts.group(i),16)) 132 return uuid_bytes 133 134def parseUUID(uuid): 135 if uuid in assigned_uuids: 136 return twoByteLEFor(assigned_uuids[uuid]) 137 uuid_upper = uuid.upper().replace('.','_') 138 if uuid_upper in bluetooth_gatt: 139 return twoByteLEFor(bluetooth_gatt[uuid_upper]) 140 if is_128bit_uuid(uuid): 141 return parseUUID128(uuid) 142 uuidInt = int(uuid, 16) 143 return twoByteLEFor(uuidInt) 144 145def parseProperties(properties): 146 value = 0 147 parts = properties.split("|") 148 for property in parts: 149 property = property.strip() 150 if property in property_flags: 151 value |= property_flags[property] 152 else: 153 print("WARNING: property %s undefined" % (property)) 154 return value 155 156def write_8(fout, value): 157 fout.write( "0x%02x, " % (value & 0xff)) 158 159def write_16(fout, value): 160 fout.write('0x%02x, 0x%02x, ' % (value & 0xff, (value >> 8) & 0xff)) 161 162def write_uuid(uuid): 163 for byte in uuid: 164 fout.write( "0x%02x, " % byte) 165 166def write_string(fout, text): 167 for l in text.lstrip('"').rstrip('"'): 168 write_8(fout, ord(l)) 169 170def write_sequence(fout, text): 171 parts = text.split() 172 for part in parts: 173 fout.write("0x%s, " % (part.strip())) 174 175def write_indent(fout): 176 fout.write(" ") 177 178def is_string(text): 179 for item in text.split(" "): 180 if not all(c in string.hexdigits for c in item): 181 return True 182 return False 183 184def add_client_characteristic_configuration(properties): 185 return properties & (property_flags['NOTIFY'] | property_flags['INDICATE']) 186 187def serviceDefinitionComplete(fout): 188 global services 189 if current_service_uuid_string: 190 fout.write("\n") 191 # print("append service %s = [%d, %d]" % (current_characteristic_uuid_string, current_service_start_handle, handle-1)) 192 defines_for_services.append('#define ATT_SERVICE_%s_START_HANDLE 0x%04x' % (current_service_uuid_string, current_service_start_handle)) 193 defines_for_services.append('#define ATT_SERVICE_%s_END_HANDLE 0x%04x' % (current_service_uuid_string, handle-1)) 194 services[current_service_uuid_string] = [current_service_start_handle, handle-1] 195 196def parseService(fout, parts, service_type): 197 global handle 198 global total_size 199 global current_service_uuid_string 200 global current_service_start_handle 201 202 serviceDefinitionComplete(fout) 203 204 property = property_flags['READ']; 205 206 write_indent(fout) 207 fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts))) 208 209 uuid = parseUUID(parts[1]) 210 uuid_size = len(uuid) 211 212 size = 2 + 2 + 2 + uuid_size + 2 213 214 if service_type == 0x2802: 215 size += 4 216 217 write_indent(fout) 218 write_16(fout, size) 219 write_16(fout, property) 220 write_16(fout, handle) 221 write_16(fout, service_type) 222 write_uuid(uuid) 223 fout.write("\n") 224 225 current_service_uuid_string = c_string_for_uuid(parts[1]) 226 current_service_start_handle = handle 227 handle = handle + 1 228 total_size = total_size + size 229 230def parsePrimaryService(fout, parts): 231 parseService(fout, parts, 0x2800) 232 233def parseSecondaryService(fout, parts): 234 parseService(fout, parts, 0x2801) 235 236def parseIncludeService(fout, parts): 237 global handle 238 global total_size 239 240 property = property_flags['READ']; 241 242 write_indent(fout) 243 fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts))) 244 245 uuid = parseUUID(parts[1]) 246 uuid_size = len(uuid) 247 if uuid_size > 2: 248 uuid_size = 0 249 # print("Include Service ", c_string_for_uuid(uuid)) 250 251 size = 2 + 2 + 2 + 2 + 4 + uuid_size 252 253 keyUUID = c_string_for_uuid(parts[1]) 254 255 write_indent(fout) 256 write_16(fout, size) 257 write_16(fout, property) 258 write_16(fout, handle) 259 write_16(fout, 0x2802) 260 write_16(fout, services[keyUUID][0]) 261 write_16(fout, services[keyUUID][1]) 262 if uuid_size > 0: 263 write_uuid(uuid) 264 fout.write("\n") 265 266 handle = handle + 1 267 total_size = total_size + size 268 269 270def parseCharacteristic(fout, parts): 271 global handle 272 global total_size 273 global current_characteristic_uuid_string 274 global characteristic_indices 275 276 property_read = property_flags['READ']; 277 278 # enumerate characteristics with same UUID, using optional name tag if available 279 current_characteristic_uuid_string = c_string_for_uuid(parts[1]); 280 index = 1 281 if current_characteristic_uuid_string in characteristic_indices: 282 index = characteristic_indices[current_characteristic_uuid_string] + 1 283 characteristic_indices[current_characteristic_uuid_string] = index 284 if len(parts) > 4: 285 current_characteristic_uuid_string += '_' + parts[4].upper().replace(' ','_') 286 else: 287 current_characteristic_uuid_string += ('_%02x' % index) 288 289 uuid = parseUUID(parts[1]) 290 uuid_size = len(uuid) 291 properties = parseProperties(parts[2]) 292 value = ', '.join([str(x) for x in parts[3:]]) 293 294 # reliable writes is defined in an extended properties 295 if (properties & property_flags['RELIABLE_WRITE']): 296 properties = properties | property_flags['EXTENDED_PROPERTIES'] 297 298 write_indent(fout) 299 fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts[0:3]))) 300 301 size = 2 + 2 + 2 + 2 + (1+2+uuid_size) 302 write_indent(fout) 303 write_16(fout, size) 304 write_16(fout, property_read) 305 write_16(fout, handle) 306 write_16(fout, 0x2803) 307 write_8(fout, properties) 308 write_16(fout, handle+1) 309 write_uuid(uuid) 310 fout.write("\n") 311 handle = handle + 1 312 total_size = total_size + size 313 314 size = 2 + 2 + 2 + uuid_size 315 if is_string(value): 316 size = size + len(value) 317 else: 318 size = size + len(value.split()) 319 320 # drop Broadcast (0x01), Notify (0x10), Indicate (0x20)- not used for flags 321 # 322 value_properties = properties & 0x1ffce 323 324 # add UUID128 flag for value handle 325 if uuid_size == 16: 326 value_properties = value_properties | property_flags['LONG_UUID']; 327 328 write_indent(fout) 329 fout.write('// 0x%04x VALUE-%s-'"'%s'"'\n' % (handle, '-'.join(parts[1:3]),value)) 330 write_indent(fout) 331 write_16(fout, size) 332 write_16(fout, value_properties) 333 write_16(fout, handle) 334 write_uuid(uuid) 335 if is_string(value): 336 write_string(fout, value) 337 else: 338 write_sequence(fout,value) 339 340 fout.write("\n") 341 defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_VALUE_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 342 handle = handle + 1 343 344 if add_client_characteristic_configuration(properties): 345 # replace GATT Characterstic Properties with READ|WRITE|READ_WITHOUT_AUTHENTICATION|DYNAMIC 346 ccc_properties = (properties & 0x1fc00) | \ 347 property_flags['READ_WITHOUT_AUTHENTICATION'] | \ 348 property_flags['READ'] | \ 349 property_flags['WRITE'] | \ 350 property_flags['DYNAMIC']; 351 size = 2 + 2 + 2 + 2 + 2 352 write_indent(fout) 353 fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle)) 354 write_indent(fout) 355 write_16(fout, size) 356 write_16(fout, ccc_properties) 357 write_16(fout, handle) 358 write_16(fout, 0x2902) 359 write_16(fout, 0) 360 fout.write("\n") 361 defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 362 handle = handle + 1 363 364 if properties & property_flags['RELIABLE_WRITE']: 365 size = 2 + 2 + 2 + 2 + 2 366 write_indent(fout) 367 fout.write('// 0x%04x CHARACTERISTIC_EXTENDED_PROPERTIES\n' % (handle)) 368 write_indent(fout) 369 write_16(fout, size) 370 write_16(fout, property_flags['READ']) 371 write_16(fout, handle) 372 write_16(fout, 0x2900) 373 write_16(fout, 1) # Reliable Write 374 fout.write("\n") 375 handle = handle + 1 376 377def parseCharacteristicUserDescription(fout, parts): 378 global handle 379 global total_size 380 global current_characteristic_uuid_string 381 382 properties = parseProperties(parts[1]) 383 value = parts[2] 384 385 size = 2 + 2 + 2 + 2 386 if is_string(value): 387 size = size + len(value) - 2 388 else: 389 size = size + len(value.split()) 390 391 write_indent(fout) 392 fout.write('// 0x%04x CHARACTERISTIC_USER_DESCRIPTION-%s\n' % (handle, '-'.join(parts[1:]))) 393 write_indent(fout) 394 write_16(fout, size) 395 write_16(fout, properties) 396 write_16(fout, handle) 397 write_16(fout, 0x2901) 398 if is_string(value): 399 write_string(fout, value) 400 else: 401 write_sequence(fout,value) 402 fout.write("\n") 403 defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_USER_DESCRIPTION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 404 handle = handle + 1 405 406def parseServerCharacteristicConfiguration(fout, parts): 407 global handle 408 global total_size 409 global current_characteristic_uuid_string 410 411 properties = parseProperties(parts[1]) 412 properties = properties | property_flags['DYNAMIC'] 413 size = 2 + 2 + 2 + 2 414 415 write_indent(fout) 416 fout.write('// 0x%04x SERVER_CHARACTERISTIC_CONFIGURATION-%s\n' % (handle, '-'.join(parts[1:]))) 417 write_indent(fout) 418 write_16(fout, size) 419 write_16(fout, properties) 420 write_16(fout, handle) 421 write_16(fout, 0x2903) 422 fout.write("\n") 423 defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_SERVER_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 424 handle = handle + 1 425 426def parseCharacteristicFormat(fout, parts): 427 global handle 428 global total_size 429 430 property_read = property_flags['READ']; 431 432 identifier = parts[1] 433 presentation_formats[identifier] = handle 434 # print("format '%s' with handle %d\n" % (identifier, handle)) 435 436 format = parts[2] 437 exponent = parts[3] 438 unit = parseUUID(parts[4]) 439 name_space = parts[5] 440 description = parseUUID(parts[6]) 441 442 size = 2 + 2 + 2 + 2 + 7 443 444 write_indent(fout) 445 fout.write('// 0x%04x CHARACTERISTIC_FORMAT-%s\n' % (handle, '-'.join(parts[1:]))) 446 write_indent(fout) 447 write_16(fout, size) 448 write_16(fout, property_read) 449 write_16(fout, handle) 450 write_16(fout, 0x2904) 451 write_sequence(fout, format) 452 write_sequence(fout, exponent) 453 write_uuid(unit) 454 write_sequence(fout, name_space) 455 write_uuid(description) 456 fout.write("\n") 457 handle = handle + 1 458 459 460def parseCharacteristicAggregateFormat(fout, parts): 461 global handle 462 global total_size 463 464 property_read = property_flags['READ']; 465 size = 2 + 2 + 2 + 2 + (len(parts)-1) * 2 466 467 write_indent(fout) 468 fout.write('// 0x%04x CHARACTERISTIC_AGGREGATE_FORMAT-%s\n' % (handle, '-'.join(parts[1:]))) 469 write_indent(fout) 470 write_16(fout, size) 471 write_16(fout, property_read) 472 write_16(fout, handle) 473 write_16(fout, 0x2905) 474 for identifier in parts[1:]: 475 format_handle = presentation_formats[identifier] 476 if format == 0: 477 print("ERROR: identifier '%s' in CHARACTERISTIC_AGGREGATE_FORMAT undefined" % identifier) 478 sys.exit(1) 479 write_16(fout, format_handle) 480 fout.write("\n") 481 handle = handle + 1 482 483def parseReportReference(fout, parts): 484 global handle 485 global total_size 486 487 property_read = property_flags['READ']; 488 size = 2 + 2 + 2 + 2 + 1 + 1 489 490 properties = parseProperties(parts[1]) 491 492 report_id = parts[2] 493 report_type = parts[3] 494 495 write_indent(fout) 496 fout.write('// 0x%04x REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:]))) 497 write_indent(fout) 498 write_16(fout, size) 499 write_16(fout, property_read) 500 write_16(fout, handle) 501 write_16(fout, 0x2908) 502 write_sequence(fout, report_id) 503 write_sequence(fout, report_type) 504 fout.write("\n") 505 handle = handle + 1 506 507 508def parseNumberOfDigitals(fout, parts): 509 global handle 510 global total_size 511 512 property_read = property_flags['READ']; 513 size = 2 + 2 + 2 + 2 + 1 514 515 no_of_digitals = parts[1] 516 517 write_indent(fout) 518 fout.write('// 0x%04x NUMBER_OF_DIGITALS-%s\n' % (handle, '-'.join(parts[1:]))) 519 write_indent(fout) 520 write_16(fout, size) 521 write_16(fout, property_read) 522 write_16(fout, handle) 523 write_16(fout, 0x2909) 524 write_sequence(fout, no_of_digitals) 525 fout.write("\n") 526 handle = handle + 1 527 528def parseLines(fname_in, fin, fout): 529 global handle 530 global total_size 531 532 line_count = 0; 533 for line in fin: 534 line = line.strip("\n\r ") 535 line_count += 1 536 537 if line.startswith("//"): 538 fout.write(" //" + line.lstrip('/') + '\n') 539 continue 540 541 if line.startswith("#import"): 542 imported_file = '' 543 parts = re.match('#import\s+<(.*)>\w*',line) 544 if parts and len(parts.groups()) == 1: 545 imported_file = btstack_root+'/src/ble/gatt-service/' + parts.groups()[0] 546 parts = re.match('#import\s+"(.*)"\w*',line) 547 if parts and len(parts.groups()) == 1: 548 imported_file = os.path.abspath(os.path.dirname(fname_in) + '/'+parts.groups()[0]) 549 if len(imported_file) == 0: 550 print('ERROR: #import in file %s - line %u neither <name.gatt> nor "name.gatt" form', (fname_in, line_count)) 551 continue 552 553 print("Importing %s" % imported_file) 554 try: 555 imported_fin = codecs.open (imported_file, encoding='utf-8') 556 fout.write(' // ' + line + ' -- BEGIN\n') 557 parseLines(imported_file, imported_fin, fout) 558 fout.write(' // ' + line + ' -- END\n') 559 except IOError as e: 560 print('ERROR: Import failed. Please check path.') 561 562 continue 563 564 if line.startswith("#TODO"): 565 print ("WARNING: #TODO in file %s - line %u not handled, skipping declaration:" % (fname_in, line_count)) 566 print ("'%s'" % line) 567 fout.write("// " + line + '\n') 568 continue 569 570 if len(line) == 0: 571 continue 572 573 f = io.StringIO(line) 574 parts_list = csv.reader(f, delimiter=',', quotechar='"') 575 576 for parts in parts_list: 577 for index, object in enumerate(parts): 578 parts[index] = object.strip().lstrip('"').rstrip('"') 579 580 if parts[0] == 'PRIMARY_SERVICE': 581 parsePrimaryService(fout, parts) 582 continue 583 584 if parts[0] == 'SECONDARY_SERVICE': 585 parseSecondaryService(fout, parts) 586 continue 587 588 if parts[0] == 'INCLUDE_SERVICE': 589 parseIncludeService(fout, parts) 590 continue 591 592 # 2803 593 if parts[0] == 'CHARACTERISTIC': 594 parseCharacteristic(fout, parts) 595 continue 596 597 # 2900 Characteristic Extended Properties 598 599 # 2901 600 if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION': 601 parseCharacteristicUserDescription(fout, parts) 602 continue 603 604 605 # 2902 Client Characteristic Configuration - automatically included in Characteristic if 606 # notification / indication is supported 607 if parts[0] == 'CLIENT_CHARACTERISTIC_CONFIGURATION': 608 continue 609 610 # 2903 611 if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION': 612 parseServerCharacteristicConfiguration(fout, parts) 613 continue 614 615 # 2904 616 if parts[0] == 'CHARACTERISTIC_FORMAT': 617 parseCharacteristicFormat(fout, parts) 618 continue 619 620 # 2905 621 if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT': 622 parseCharacteristicAggregateFormat(fout, parts) 623 continue 624 625 # 2906 626 if parts[0] == 'VALID_RANGE': 627 print("WARNING: %s not implemented yet\n" % (parts[0])) 628 continue 629 630 # 2907 631 if parts[0] == 'EXTERNAL_REPORT_REFERENCE': 632 print("WARNING: %s not implemented yet\n" % (parts[0])) 633 continue 634 635 # 2908 636 if parts[0] == 'REPORT_REFERENCE': 637 parseReportReference(fout, parts) 638 continue 639 640 # 2909 641 if parts[0] == 'NUMBER_OF_DIGITALS': 642 parseNumberOfDigitals(fout, parts) 643 continue 644 645 # 290A 646 if parts[0] == 'VALUE_TRIGGER_SETTING': 647 print("WARNING: %s not implemented yet\n" % (parts[0])) 648 continue 649 650 # 290B 651 if parts[0] == 'ENVIRONMENTAL_SENSING_CONFIGURATION': 652 print("WARNING: %s not implemented yet\n" % (parts[0])) 653 continue 654 655 # 290C 656 if parts[0] == 'ENVIRONMENTAL_SENSING_MEASUREMENT': 657 print("WARNING: %s not implemented yet\n" % (parts[0])) 658 continue 659 660 # 290D 661 if parts[0] == 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING': 662 print("WARNING: %s not implemented yet\n" % (parts[0])) 663 continue 664 665 # 2906 666 if parts[0] == 'VALID_RANGE': 667 print("WARNING: %s not implemented yet\n" % (parts[0])) 668 continue 669 670 print("WARNING: unknown token: %s\n" % (parts[0])) 671 672def parse(fname_in, fin, fname_out, fout): 673 global handle 674 global total_size 675 676 fout.write(header.format(fname_out, fname_in)) 677 fout.write('{\n') 678 679 parseLines(fname_in, fin, fout) 680 681 serviceDefinitionComplete(fout) 682 write_indent(fout) 683 fout.write("// END\n"); 684 write_indent(fout) 685 write_16(fout,0) 686 fout.write("\n") 687 total_size = total_size + 2 688 689 fout.write("}; // total size %u bytes \n" % total_size); 690 691def listHandles(fout): 692 fout.write('\n\n') 693 fout.write('//\n') 694 fout.write('// list service handle ranges\n') 695 fout.write('//\n') 696 for define in defines_for_services: 697 fout.write(define) 698 fout.write('\n') 699 fout.write('\n') 700 fout.write('//\n') 701 fout.write('// list mapping between characteristics and handles\n') 702 fout.write('//\n') 703 for define in defines_for_characteristics: 704 fout.write(define) 705 fout.write('\n') 706 707if (len(sys.argv) < 3): 708 print(usage) 709 sys.exit(1) 710try: 711 # read defines from bluetooth_gatt.h 712 btstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..') 713 gen_path = btstack_root + '/src/bluetooth_gatt.h' 714 bluetooth_gatt = read_defines(gen_path) 715 716 filename = sys.argv[2] 717 fin = codecs.open (sys.argv[1], encoding='utf-8') 718 fout = open (filename, 'w') 719 parse(sys.argv[1], fin, filename, fout) 720 listHandles(fout) 721 fout.close() 722 print('Created %s' % filename) 723 724except IOError as e: 725 print(usage) 726 sys.exit(1) 727 728print('Compilation successful!\n') 729