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 layout
= (json
.JSONEncoder().encode(hardware
).encode() + (b
'\0' * 2048))[0:2048]
51 firmware_file
.write(layout
)
52 except EnvironmentError:
53 sys
.stderr
.write(f
'Error opening file "{layout_file}"\n')
56 firmware_file
.write(b
'\0' * 2048)
57 if config
is not None and 'logo_file' in config
:
58 logo_file
= f
"hardware/logo/{config['logo_file']}"
59 with
open(logo_file
, 'rb') as f
:
60 firmware_file
.write(f
.read())
61 if config
is not None and 'prior_target_name' in config
:
62 firmware_file
.write(b
'\xBE\xEF\xCA\xFE')
63 firmware_file
.write(config
['prior_target_name'].upper().encode())
64 firmware_file
.write(b
'\0')
66 def doConfiguration(file, defines
, config
, moduletype
, frequency
, platform
, device_name
):
67 product_name
= "Unified"
72 with
open('hardware/targets.json') as f
:
73 targets
= json
.load(f
)
75 if config
is not None:
76 config
='.'.join(map(lambda s
: f
'"{s}"', config
.split('.')))
77 config
= jmespath
.search(config
, targets
)
78 elif not sys
.stdin
.isatty():
79 print('Not running in an interactive shell, leaving the firmware "bare".\n')
80 print('The current compile options (user defines) have been included.')
81 print('You will be able to configure the hardware via the web UI on the device.')
85 for k
in jmespath
.search(f
'[*."{moduletype}_{frequency}".*][][?platform==`{platform}`][].product_name', targets
):
89 print('Choose a configuration to load into the firmware file (press enter to leave bare)')
92 config
= products
[int(choice
)-1]
93 config
= jmespath
.search(f
'[*."{moduletype}_{frequency}".*][][?product_name==`{config}`][]', targets
)[0]
95 if config
is not None:
96 product_name
= config
['product_name']
97 lua_name
= config
['lua_name']
98 dir = 'TX' if moduletype
== 'tx' else 'RX'
99 layout
= f
"hardware/{dir}/{config['layout_file']}"
101 lua_name
= lua_name
if device_name
is None else device_name
102 appendToFirmware(file, product_name
, lua_name
, defines
, config
, layout
)
104 def appendConfiguration(source
, target
, env
):
105 target_name
= env
.get('PIOENV', '').upper()
106 device_name
= env
.get('DEVICE_NAME', None)
107 config
= env
.GetProjectOption('board_config', None)
108 if 'UNIFIED_' not in target_name
and config
is None:
113 if config
is not None:
114 moduletype
= 'tx' if '.tx_' in config
else 'rx'
115 frequency
= '2400' if '_2400.' in config
else '900' if '_900.' in config
else 'dual'
117 moduletype
= 'tx' if '_TX_' in target_name
else 'rx'
118 frequency
= '2400' if '_2400_' in target_name
else '900' if '_900_' in target_name
else 'dual'
120 if env
.get('PIOPLATFORM', '') == 'espressif32':
122 if 'esp32-s3' in env
.get('BOARD', ''):
123 platform
= 'esp32-s3'
127 defines
= json
.JSONEncoder().encode(env
['OPTIONS_JSON'])
129 with
open(str(target
[0]), "r+b") as firmware_file
:
130 doConfiguration(firmware_file
, defines
, config
, moduletype
, frequency
, platform
, device_name
)
132 if __name__
== '__main__':
133 parser
= argparse
.ArgumentParser(description
="Configure Unified Firmware")
134 parser
.add_argument("--target", type=str, help="Path into 'targets.json' file for hardware configuration")
135 parser
.add_argument("--options", type=str, help="JSON document with configuration options")
137 parser
.add_argument("file", type=argparse
.FileType("r+b"), help="The firmware file to configure")
139 args
= parser
.parse_args()
142 with
open('hardware/targets.json') as f
:
143 targets
= json
.load(f
)
145 moduletype
= 'tx' if '.tx_' in args
.target
else 'rx'
147 config
='.'.join(map(lambda s
: f
'"{s}"', args
.target
.split('.')))
148 config
= jmespath
.search(config
, targets
)
150 if config
is not None:
151 product_name
= config
['product_name']
152 lua_name
= config
['lua_name']
153 dir = 'TX' if moduletype
== 'tx' else 'RX'
154 layout
= f
"hardware/{dir}/{config['layout_file']}"
156 appendToFirmware(args
.file, product_name
, lua_name
, args
.options
, config
, layout
)