xref: /btstack/tool/compile_gatt.py (revision 8ea3236c5e643359e0382131bd98c235d1f41570)
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, Notify, Indicate - not used for flags
321    value_properties = properties & 0x1ffce
322
323    # add UUID128 flag for value handle
324    if uuid_size == 16:
325        value_properties = value_properties | property_flags['LONG_UUID'];
326
327    write_indent(fout)
328    fout.write('// 0x%04x VALUE-%s-'"'%s'"'\n' % (handle, '-'.join(parts[1:3]),value))
329    write_indent(fout)
330    write_16(fout, size)
331    write_16(fout, value_properties)
332    write_16(fout, handle)
333    write_uuid(uuid)
334    if is_string(value):
335        write_string(fout, value)
336    else:
337        write_sequence(fout,value)
338
339    fout.write("\n")
340    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_VALUE_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
341    handle = handle + 1
342
343    if add_client_characteristic_configuration(properties):
344        # replace GATT Characterstic Properties with READ|WRITE|READ_WITHOUT_AUTHENTICATION|DYNAMIC
345        ccc_properties = (properties & 0x1fc00) | property_flags['READ_WITHOUT_AUTHENTICATION'] | property_flags['READ'] | property_flags['WRITE'] | property_flags['DYNAMIC'];
346        size = 2 + 2 + 2 + 2 + 2
347        write_indent(fout)
348        fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle))
349        write_indent(fout)
350        write_16(fout, size)
351        write_16(fout, ccc_properties)
352        write_16(fout, handle)
353        write_16(fout, 0x2902)
354        write_16(fout, 0)
355        fout.write("\n")
356        defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
357        handle = handle + 1
358
359    if properties & property_flags['RELIABLE_WRITE']:
360        size = 2 + 2 + 2 + 2 + 2
361        write_indent(fout)
362        fout.write('// 0x%04x CHARACTERISTIC_EXTENDED_PROPERTIES\n' % (handle))
363        write_indent(fout)
364        write_16(fout, size)
365        write_16(fout, property_flags['READ'])
366        write_16(fout, handle)
367        write_16(fout, 0x2900)
368        write_16(fout, 1)   # Reliable Write
369        fout.write("\n")
370        handle = handle + 1
371
372def parseCharacteristicUserDescription(fout, parts):
373    global handle
374    global total_size
375    global current_characteristic_uuid_string
376
377    properties = parseProperties(parts[1])
378    value      = parts[2]
379
380    size = 2 + 2 + 2 + 2
381    if is_string(value):
382        size = size + len(value) - 2
383    else:
384        size = size + len(value.split())
385
386    write_indent(fout)
387    fout.write('// 0x%04x CHARACTERISTIC_USER_DESCRIPTION-%s\n' % (handle, '-'.join(parts[1:])))
388    write_indent(fout)
389    write_16(fout, size)
390    write_16(fout, properties)
391    write_16(fout, handle)
392    write_16(fout, 0x2901)
393    if is_string(value):
394        write_string(fout, value)
395    else:
396        write_sequence(fout,value)
397    fout.write("\n")
398    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_USER_DESCRIPTION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
399    handle = handle + 1
400
401def parseServerCharacteristicConfiguration(fout, parts):
402    global handle
403    global total_size
404    global current_characteristic_uuid_string
405
406    properties = parseProperties(parts[1])
407    properties = properties | property_flags['DYNAMIC']
408    size = 2 + 2 + 2 + 2
409
410    write_indent(fout)
411    fout.write('// 0x%04x SERVER_CHARACTERISTIC_CONFIGURATION-%s\n' % (handle, '-'.join(parts[1:])))
412    write_indent(fout)
413    write_16(fout, size)
414    write_16(fout, properties)
415    write_16(fout, handle)
416    write_16(fout, 0x2903)
417    fout.write("\n")
418    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_SERVER_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
419    handle = handle + 1
420
421def parseCharacteristicFormat(fout, parts):
422    global handle
423    global total_size
424
425    property_read = property_flags['READ'];
426
427    identifier = parts[1]
428    presentation_formats[identifier] = handle
429    # print("format '%s' with handle %d\n" % (identifier, handle))
430
431    format     = parts[2]
432    exponent   = parts[3]
433    unit       = parseUUID(parts[4])
434    name_space = parts[5]
435    description = parseUUID(parts[6])
436
437    size = 2 + 2 + 2 + 2 + 7
438
439    write_indent(fout)
440    fout.write('// 0x%04x CHARACTERISTIC_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
441    write_indent(fout)
442    write_16(fout, size)
443    write_16(fout, property_read)
444    write_16(fout, handle)
445    write_16(fout, 0x2904)
446    write_sequence(fout, format)
447    write_sequence(fout, exponent)
448    write_uuid(unit)
449    write_sequence(fout, name_space)
450    write_uuid(description)
451    fout.write("\n")
452    handle = handle + 1
453
454
455def parseCharacteristicAggregateFormat(fout, parts):
456    global handle
457    global total_size
458
459    property_read = property_flags['READ'];
460    size = 2 + 2 + 2 + 2 + (len(parts)-1) * 2
461
462    write_indent(fout)
463    fout.write('// 0x%04x CHARACTERISTIC_AGGREGATE_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
464    write_indent(fout)
465    write_16(fout, size)
466    write_16(fout, property_read)
467    write_16(fout, handle)
468    write_16(fout, 0x2905)
469    for identifier in parts[1:]:
470        format_handle = presentation_formats[identifier]
471        if format == 0:
472            print("ERROR: identifier '%s' in CHARACTERISTIC_AGGREGATE_FORMAT undefined" % identifier)
473            sys.exit(1)
474        write_16(fout, format_handle)
475    fout.write("\n")
476    handle = handle + 1
477
478def parseReportReference(fout, parts):
479    global handle
480    global total_size
481
482    property_read = property_flags['READ'];
483    size = 2 + 2 + 2 + 2 + 1 + 1
484
485    properties = parseProperties(parts[1])
486
487    report_id = parts[2]
488    report_type = parts[3]
489
490    write_indent(fout)
491    fout.write('// 0x%04x REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:])))
492    write_indent(fout)
493    write_16(fout, size)
494    write_16(fout, property_read)
495    write_16(fout, handle)
496    write_16(fout, 0x2908)
497    write_sequence(fout, report_id)
498    write_sequence(fout, report_type)
499    fout.write("\n")
500    handle = handle + 1
501
502
503def parseNumberOfDigitals(fout, parts):
504    global handle
505    global total_size
506
507    property_read = property_flags['READ'];
508    size = 2 + 2 + 2 + 2 + 1
509
510    no_of_digitals = parts[1]
511
512    write_indent(fout)
513    fout.write('// 0x%04x NUMBER_OF_DIGITALS-%s\n' % (handle, '-'.join(parts[1:])))
514    write_indent(fout)
515    write_16(fout, size)
516    write_16(fout, property_read)
517    write_16(fout, handle)
518    write_16(fout, 0x2909)
519    write_sequence(fout, no_of_digitals)
520    fout.write("\n")
521    handle = handle + 1
522
523def parseLines(fname_in, fin, fout):
524    global handle
525    global total_size
526
527    line_count = 0;
528    for line in fin:
529        line = line.strip("\n\r ")
530        line_count += 1
531
532        if line.startswith("//"):
533            fout.write("    //" + line.lstrip('/') + '\n')
534            continue
535
536        if line.startswith("#import"):
537            imported_file = ''
538            parts = re.match('#import\s+<(.*)>\w*',line)
539            if parts and len(parts.groups()) == 1:
540                imported_file = btstack_root+'/src/ble/gatt-service/' + parts.groups()[0]
541            parts = re.match('#import\s+"(.*)"\w*',line)
542            if parts and len(parts.groups()) == 1:
543                imported_file = os.path.abspath(os.path.dirname(fname_in) + '/'+parts.groups()[0])
544            if len(imported_file) == 0:
545                print('ERROR: #import in file %s - line %u neither <name.gatt> nor "name.gatt" form', (fname_in, line_count))
546                continue
547
548            print("Importing %s" % imported_file)
549            try:
550                imported_fin = codecs.open (imported_file, encoding='utf-8')
551                fout.write('    // ' + line + ' -- BEGIN\n')
552                parseLines(imported_file, imported_fin, fout)
553                fout.write('    // ' + line + ' -- END\n')
554            except IOError as e:
555                print('ERROR: Import failed. Please check path.')
556
557            continue
558
559        if line.startswith("#TODO"):
560            print ("WARNING: #TODO in file %s - line %u not handled, skipping declaration:" % (fname_in, line_count))
561            print ("'%s'" % line)
562            fout.write("// " + line + '\n')
563            continue
564
565        if len(line) == 0:
566            continue
567
568        f = io.StringIO(line)
569        parts_list = csv.reader(f, delimiter=',', quotechar='"')
570
571        for parts in parts_list:
572            for index, object in enumerate(parts):
573                parts[index] = object.strip().lstrip('"').rstrip('"')
574
575            if parts[0] == 'PRIMARY_SERVICE':
576                parsePrimaryService(fout, parts)
577                continue
578
579            if parts[0] == 'SECONDARY_SERVICE':
580                parseSecondaryService(fout, parts)
581                continue
582
583            if parts[0] == 'INCLUDE_SERVICE':
584                parseIncludeService(fout, parts)
585                continue
586
587            # 2803
588            if parts[0] == 'CHARACTERISTIC':
589                parseCharacteristic(fout, parts)
590                continue
591
592            # 2900 Characteristic Extended Properties
593
594            # 2901
595            if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION':
596                parseCharacteristicUserDescription(fout, parts)
597                continue
598
599
600            # 2902 Client Characteristic Configuration - automatically included in Characteristic if
601            # notification / indication is supported
602            if parts[0] == 'CLIENT_CHARACTERISTIC_CONFIGURATION':
603                continue
604
605            # 2903
606            if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION':
607                parseServerCharacteristicConfiguration(fout, parts)
608                continue
609
610            # 2904
611            if parts[0] == 'CHARACTERISTIC_FORMAT':
612                parseCharacteristicFormat(fout, parts)
613                continue
614
615            # 2905
616            if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT':
617                parseCharacteristicAggregateFormat(fout, parts)
618                continue
619
620            # 2906
621            if parts[0] == 'VALID_RANGE':
622                print("WARNING: %s not implemented yet\n" % (parts[0]))
623                continue
624
625            # 2907
626            if parts[0] == 'EXTERNAL_REPORT_REFERENCE':
627                print("WARNING: %s not implemented yet\n" % (parts[0]))
628                continue
629
630            # 2908
631            if parts[0] == 'REPORT_REFERENCE':
632                parseReportReference(fout, parts)
633                continue
634
635            # 2909
636            if parts[0] == 'NUMBER_OF_DIGITALS':
637                parseNumberOfDigitals(fout, parts)
638                continue
639
640            # 290A
641            if parts[0] == 'VALUE_TRIGGER_SETTING':
642                print("WARNING: %s not implemented yet\n" % (parts[0]))
643                continue
644
645            # 290B
646            if parts[0] == 'ENVIRONMENTAL_SENSING_CONFIGURATION':
647                print("WARNING: %s not implemented yet\n" % (parts[0]))
648                continue
649
650            # 290C
651            if parts[0] == 'ENVIRONMENTAL_SENSING_MEASUREMENT':
652                print("WARNING: %s not implemented yet\n" % (parts[0]))
653                continue
654
655            # 290D
656            if parts[0] == 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING':
657                print("WARNING: %s not implemented yet\n" % (parts[0]))
658                continue
659
660            # 2906
661            if parts[0] == 'VALID_RANGE':
662                print("WARNING: %s not implemented yet\n" % (parts[0]))
663                continue
664
665            print("WARNING: unknown token: %s\n" % (parts[0]))
666
667def parse(fname_in, fin, fname_out, fout):
668    global handle
669    global total_size
670
671    fout.write(header.format(fname_out, fname_in))
672    fout.write('{\n')
673
674    parseLines(fname_in, fin, fout)
675
676    serviceDefinitionComplete(fout)
677    write_indent(fout)
678    fout.write("// END\n");
679    write_indent(fout)
680    write_16(fout,0)
681    fout.write("\n")
682    total_size = total_size + 2
683
684    fout.write("}; // total size %u bytes \n" % total_size);
685
686def listHandles(fout):
687    fout.write('\n\n')
688    fout.write('//\n')
689    fout.write('// list service handle ranges\n')
690    fout.write('//\n')
691    for define in defines_for_services:
692        fout.write(define)
693        fout.write('\n')
694    fout.write('\n')
695    fout.write('//\n')
696    fout.write('// list mapping between characteristics and handles\n')
697    fout.write('//\n')
698    for define in defines_for_characteristics:
699        fout.write(define)
700        fout.write('\n')
701
702if (len(sys.argv) < 3):
703    print(usage)
704    sys.exit(1)
705try:
706    # read defines from bluetooth_gatt.h
707    btstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..')
708    gen_path = btstack_root + '/src/bluetooth_gatt.h'
709    bluetooth_gatt = read_defines(gen_path)
710
711    filename = sys.argv[2]
712    fin  = codecs.open (sys.argv[1], encoding='utf-8')
713    fout = open (filename, 'w')
714    parse(sys.argv[1], fin, filename, fout)
715    listHandles(fout)
716    fout.close()
717    print('Created %s' % filename)
718
719except IOError as e:
720    print(usage)
721    sys.exit(1)
722
723print('Compilation successful!\n')
724