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