# Combine bootloader, partition table and application into a final binary. import os, sys sys.path.append(os.getenv("IDF_PATH") + "/components/partition_table") import gen_esp32part OFFSET_BOOTLOADER_DEFAULT = 0x1000 OFFSET_PARTITIONS_DEFAULT = 0x8000 def load_sdkconfig_value(filename, value, default): value = "CONFIG_" + value + "=" with open(filename, "r") as f: for line in f: if line.startswith(value): return line.split("=", 1)[1] return default def load_sdkconfig_hex_value(filename, value, default): value = load_sdkconfig_value(filename, value, None) if value is None: return default return int(value, 16) def load_sdkconfig_str_value(filename, value, default): value = load_sdkconfig_value(filename, value, None) if value is None: return default return value.strip().strip('"') def load_partition_table(filename): with open(filename, "rb") as f: return gen_esp32part.PartitionTable.from_binary(f.read()) # Extract command-line arguments. arg_sdkconfig = sys.argv[1] arg_bootloader_bin = sys.argv[2] arg_partitions_bin = sys.argv[3] arg_application_bin = sys.argv[4] arg_output_bin = sys.argv[5] arg_output_uf2 = sys.argv[6] # Load required sdkconfig values. idf_target = load_sdkconfig_str_value(arg_sdkconfig, "IDF_TARGET", "").upper() offset_bootloader = load_sdkconfig_hex_value( arg_sdkconfig, "BOOTLOADER_OFFSET_IN_FLASH", OFFSET_BOOTLOADER_DEFAULT ) offset_partitions = load_sdkconfig_hex_value( arg_sdkconfig, "PARTITION_TABLE_OFFSET", OFFSET_PARTITIONS_DEFAULT ) # Load the partition table. partition_table = load_partition_table(arg_partitions_bin) max_size_bootloader = offset_partitions - offset_bootloader max_size_partitions = 0 offset_application = 0 max_size_application = 0 # Inspect the partition table to find offsets and maximum sizes. for part in partition_table: if part.name == "nvs": max_size_partitions = part.offset - offset_partitions elif part.type == gen_esp32part.APP_TYPE and offset_application == 0: offset_application = part.offset max_size_application = part.size # Define the input files, their location and maximum size. files_in = [ ("bootloader", offset_bootloader, max_size_bootloader, arg_bootloader_bin), ("partitions", offset_partitions, max_size_partitions, arg_partitions_bin), ("application", offset_application, max_size_application, arg_application_bin), ] file_out = arg_output_bin # Write output file with combined firmware. cur_offset = offset_bootloader with open(file_out, "wb") as fout: for name, offset, max_size, file_in in files_in: assert offset >= cur_offset fout.write(b"\xff" * (offset - cur_offset)) cur_offset = offset with open(file_in, "rb") as fin: data = fin.read() fout.write(data) cur_offset += len(data) print( "%-12s@0x%06x % 8d (% 8d remaining)" % (name, offset, len(data), max_size - len(data)) ) if len(data) > max_size: print( "ERROR: %s overflows allocated space of %d bytes by %d bytes" % (name, max_size, len(data) - max_size) ) sys.exit(1) print("%-22s% 8d" % ("total", cur_offset)) # Generate .uf2 file if the SoC has native USB. if idf_target in ("ESP32S2", "ESP32S3"): sys.path.append(os.path.join(os.path.dirname(__file__), "../../tools")) import uf2conv families = uf2conv.load_families() uf2conv.appstartaddr = 0 uf2conv.familyid = families[idf_target] with open(arg_application_bin, "rb") as fin, open(arg_output_uf2, "wb") as fout: fout.write(uf2conv.convert_to_uf2(fin.read()))