8 from external
import jmespath
10 def findFirmwareEnd(f
):
12 (magic
, segments
, _
, _
, _
) = struct
.unpack('<BBBBI', f
.read(8))
14 sys
.stderr
.write('The file provided does not the right magic for a firmware file!\n')
18 if segments
== 2: # we have to assume it's an ESP8266/85
20 (magic
, segments
, _
, _
, _
) = struct
.unpack('<BBBBI', f
.read(8))
25 for _
in range(segments
):
26 (_
, size
) = struct
.unpack('<II', f
.read(8))
30 pos
= (pos
+ 16) & ~
15
35 def appendToFirmware(firmware_file
, product_name
, lua_name
, defines
, config
, layout_file
):
36 product
= (product_name
.encode() + (b
'\0' * 128))[0:128]
37 device
= (lua_name
.encode() + (b
'\0' * 16))[0:16]
38 end
= findFirmwareEnd(firmware_file
)
39 firmware_file
.seek(end
, 0)
40 firmware_file
.write(product
)
41 firmware_file
.write(device
)
42 defines
= (defines
.encode() + (b
'\0' * 512))[0:512]
43 firmware_file
.write(defines
)
44 if layout_file
is not None:
46 with
open(layout_file
) as h
:
47 hardware
= json
.load(h
)
48 if 'overlay' in config
:
49 hardware
.update(config
['overlay'])
50 firmware_file
.write(json
.JSONEncoder().encode(hardware
).encode())
51 except EnvironmentError:
52 sys
.stderr
.write(f
'Error opening file "{layout_file}"\n')
54 firmware_file
.write(b
'\0')
55 if config
is not None and 'prior_target_name' in config
:
56 firmware_file
.write(b
'\xBE\xEF\xCA\xFE')
57 firmware_file
.write(config
['prior_target_name'].upper().encode())
58 firmware_file
.write(b
'\0')
60 def doConfiguration(file, defines
, config
, moduletype
, frequency
, platform
, device_name
):
61 product_name
= "Unified"
66 with
open('hardware/targets.json') as f
:
67 targets
= json
.load(f
)
69 if config
is not None:
70 config
='.'.join(map(lambda s
: f
'"{s}"', config
.split('.')))
71 config
= jmespath
.search(config
, targets
)
72 elif not sys
.stdin
.isatty():
73 print('Not running in an interactive shell, leaving the firmware "bare".\n')
74 print('The current compile options (user defines) have been included.')
75 print('You will be able to configure the hardware via the web UI on the device.')
79 for k
in jmespath
.search(f
'[*."{moduletype}_{frequency}".*][][?platform==`{platform}`][].product_name', targets
):
83 print('Choose a configuration to load into the firmware file (press enter to leave bare)')
86 config
= products
[int(choice
)-1]
87 config
= jmespath
.search(f
'[*."{moduletype}_{frequency}".*][][?product_name==`{config}`][]', targets
)[0]
89 if config
is not None:
90 product_name
= config
['product_name']
91 lua_name
= config
['lua_name']
92 dir = 'TX' if moduletype
== 'tx' else 'RX'
93 layout
= f
"hardware/{dir}/{config['layout_file']}"
95 lua_name
= lua_name
if device_name
is None else device_name
96 appendToFirmware(file, product_name
, lua_name
, defines
, config
, layout
)
98 def appendConfiguration(source
, target
, env
):
99 target_name
= env
.get('PIOENV', '').upper()
100 device_name
= env
.get('DEVICE_NAME', None)
101 config
= env
.GetProjectOption('board_config', None)
102 if 'UNIFIED_' not in target_name
and config
is None:
107 if config
is not None:
108 moduletype
= 'tx' if '.tx_' in config
else 'rx'
109 frequency
= '2400' if '_2400.' in config
else '900'
111 moduletype
= 'tx' if '_TX_' in target_name
else 'rx'
112 frequency
= '2400' if '_2400_' in target_name
else '900'
114 platform
= 'esp32' if env
.get('PIOPLATFORM', '') in ['espressif32'] else 'esp8285'
116 defines
= json
.JSONEncoder().encode(env
['OPTIONS_JSON'])
118 with
open(str(target
[0]), "r+b") as firmware_file
:
119 doConfiguration(firmware_file
, defines
, config
, moduletype
, frequency
, platform
, device_name
)
121 if __name__
== '__main__':
122 parser
= argparse
.ArgumentParser(description
="Configure Unified Firmware")
123 parser
.add_argument("--target", type=str, help="Path into 'targets.json' file for hardware configuration")
124 parser
.add_argument("--options", type=str, help="JSON document with configuration options")
126 parser
.add_argument("file", type=argparse
.FileType("r+b"), help="The firmware file to configure")
128 args
= parser
.parse_args()
131 with
open('hardware/targets.json') as f
:
132 targets
= json
.load(f
)
134 moduletype
= 'tx' if '.tx_' in args
.target
else 'rx'
136 config
='.'.join(map(lambda s
: f
'"{s}"', args
.target
.split('.')))
137 config
= jmespath
.search(config
, targets
)
139 if config
is not None:
140 product_name
= config
['product_name']
141 lua_name
= config
['lua_name']
142 dir = 'TX' if moduletype
== 'tx' else 'RX'
143 layout
= f
"hardware/{dir}/{config['layout_file']}"
145 appendToFirmware(args
.file, product_name
, lua_name
, args
.options
, config
, layout
)