8 from external
import jmespath
9 from firmware
import TXType
12 def findFirmwareEnd(f
):
14 (magic
, segments
, _
, _
, _
) = struct
.unpack('<BBBBI', f
.read(8))
16 sys
.stderr
.write('The file provided does not the right magic for a firmware file!\n')
20 if segments
== 2: # we have to assume it's an ESP8266/85
22 (magic
, segments
, _
, _
, _
) = struct
.unpack('<BBBBI', f
.read(8))
27 for _
in range(segments
):
28 (_
, size
) = struct
.unpack('<II', f
.read(8))
32 pos
= (pos
+ 16) & ~
15
37 def appendToFirmware(firmware_file
, product_name
, lua_name
, defines
, config
, layout_file
, rx_as_tx
):
38 product
= (product_name
.encode() + (b
'\0' * 128))[0:128]
39 device
= (lua_name
.encode() + (b
'\0' * 16))[0:16]
40 end
= findFirmwareEnd(firmware_file
)
41 firmware_file
.seek(end
, 0)
42 firmware_file
.write(product
)
43 firmware_file
.write(device
)
44 defines
= (defines
.encode() + (b
'\0' * 512))[0:512]
45 firmware_file
.write(defines
)
46 if layout_file
is not None:
48 with
open(layout_file
) as h
:
49 hardware
= json
.load(h
)
50 if 'overlay' in config
:
51 hardware
.update(config
['overlay'])
52 if rx_as_tx
is not None:
53 if 'serial_rx' not in hardware
or 'serial_tx' not in hardware
:
54 sys
.stderr
.write(f
'Cannot select this target as RX-as-TX\n')
56 if rx_as_tx
== TXType
.external
and hardware
['serial_rx']:
57 hardware
['serial_rx'] = hardware
['serial_tx']
58 if 'led_red' not in hardware
and 'led' in hardware
:
59 hardware
['led_red'] = hardware
['led']
61 layout
= (json
.JSONEncoder().encode(hardware
).encode() + (b
'\0' * 2048))[0:2048]
62 firmware_file
.write(layout
)
63 except EnvironmentError:
64 sys
.stderr
.write(f
'Error opening file "{layout_file}"\n')
67 firmware_file
.write(b
'\0' * 2048)
68 if config
is not None and 'logo_file' in config
:
69 logo_file
= f
"hardware/logo/{config['logo_file']}"
70 with
open(logo_file
, 'rb') as f
:
71 firmware_file
.write(f
.read())
72 if config
is not None and 'prior_target_name' in config
:
73 firmware_file
.write(b
'\xBE\xEF\xCA\xFE')
74 firmware_file
.write(config
['prior_target_name'].upper().encode())
75 firmware_file
.write(b
'\0')
77 def doConfiguration(file, defines
, config
, moduletype
, frequency
, platform
, device_name
, rx_as_tx
):
78 product_name
= "Unified"
83 with
open('hardware/targets.json') as f
:
84 targets
= json
.load(f
)
86 if config
is not None:
87 config
='.'.join(map(lambda s
: f
'"{s}"', config
.split('.')))
88 config
= jmespath
.search(config
, targets
)
89 elif not sys
.stdin
.isatty():
90 print('Not running in an interactive shell, leaving the firmware "bare".\n')
91 print('The current compile options (user defines) have been included.')
92 print('You will be able to configure the hardware via the web UI on the device.')
96 for k
in jmespath
.search(f
'[*."{moduletype}_{frequency}".*][][?platform==`{platform}`][].product_name', targets
):
100 print('Choose a configuration to load into the firmware file (press enter to leave bare)')
103 config
= products
[int(choice
)-1]
104 config
= jmespath
.search(f
'[*."{moduletype}_{frequency}".*][][?product_name==`{config}`][]', targets
)[0]
106 if config
is not None:
107 product_name
= config
['product_name']
108 lua_name
= config
['lua_name']
109 dir = 'TX' if moduletype
== 'tx' else 'RX'
110 layout
= f
"hardware/{dir}/{config['layout_file']}"
112 lua_name
= lua_name
if device_name
is None else device_name
113 appendToFirmware(file, product_name
, lua_name
, defines
, config
, layout
, rx_as_tx
)
115 def appendConfiguration(source
, target
, env
):
116 target_name
= env
.get('PIOENV', '').upper()
117 device_name
= env
.get('DEVICE_NAME', None)
118 config
= env
.GetProjectOption('board_config', None)
119 if 'UNIFIED_' not in target_name
and config
is None:
124 if config
is not None:
125 moduletype
= 'tx' if '.tx_' in config
else 'rx'
126 frequency
= '2400' if '_2400.' in config
else '900' if '_900.' in config
else 'dual'
128 moduletype
= 'tx' if '_TX_' in target_name
else 'rx'
129 frequency
= '2400' if '_2400_' in target_name
else '900' if '_900_' in target_name
else 'dual'
131 if env
.get('PIOPLATFORM', '') == 'espressif32':
133 if 'esp32-s3' in env
.get('BOARD', ''):
134 platform
= 'esp32-s3'
135 elif 'esp32-c3' in env
.get('BOARD', ''):
136 platform
= 'esp32-c3'
140 defines
= json
.JSONEncoder().encode(env
['OPTIONS_JSON'])
142 with
open(str(target
[0]), "r+b") as firmware_file
:
143 doConfiguration(firmware_file
, defines
, config
, moduletype
, frequency
, platform
, device_name
, None)
145 if __name__
== '__main__':
146 parser
= argparse
.ArgumentParser(description
="Configure Unified Firmware")
147 parser
.add_argument("--target", type=str, help="Path into 'targets.json' file for hardware configuration")
148 parser
.add_argument("--options", type=str, help="JSON document with configuration options")
150 parser
.add_argument("file", type=argparse
.FileType("r+b"), help="The firmware file to configure")
152 args
= parser
.parse_args()
155 with
open('hardware/targets.json') as f
:
156 targets
= json
.load(f
)
158 moduletype
= 'tx' if '.tx_' in args
.target
else 'rx'
160 config
='.'.join(map(lambda s
: f
'"{s}"', args
.target
.split('.')))
161 config
= jmespath
.search(config
, targets
)
163 if config
is not None:
164 product_name
= config
['product_name']
165 lua_name
= config
['lua_name']
166 dir = 'TX' if moduletype
== 'tx' else 'RX'
167 layout
= f
"hardware/{dir}/{config['layout_file']}"
169 appendToFirmware(args
.file, product_name
, lua_name
, args
.options
, config
, layout
, None)