ardupilot/Tools/ardupilotwaf/esp32.py
Thomas Watson a14fb9ab28 AP_HAL_ESP32: regenerate sdkconfig after sdkconfig.defaults change
Ensures the sdkconfig does not get out of date as updates are made to
the defaults. The sdkconfig can still be manually changed for testing.
2024-12-10 10:32:47 +11:00

214 lines
7.8 KiB
Python

# encoding: utf-8
"""
Waf tool for ESP32 build
"""
from waflib import Build, ConfigSet, Configure, Context, Task, Utils
from waflib import Errors, Logs
from waflib.TaskGen import before, after_method, before_method, feature
from waflib.Configure import conf
from collections import OrderedDict
import os
import shutil
import sys
import re
import pickle
import subprocess
def configure(cfg):
mcu_esp32s3 = True if (cfg.variant[0:7] == "esp32s3") else False
target = "esp32s3" if mcu_esp32s3 else "esp32"
bldnode = cfg.bldnode.make_node(cfg.variant)
def srcpath(path):
return cfg.srcnode.make_node(path).abspath()
def bldpath(path):
return bldnode.make_node(path).abspath()
#Load cmake builder and make
cfg.load('cmake')
#define env and location for the cmake esp32 file
env = cfg.env
env.AP_HAL_ESP32 = srcpath('libraries/AP_HAL_ESP32/targets/'+target+'/esp-idf')
env.AP_PROGRAM_FEATURES += ['esp32_ap_program']
env.ESP_IDF_PREFIX_REL = 'esp-idf'
prefix_node = bldnode.make_node(env.ESP_IDF_PREFIX_REL)
env.ESP32_TARGET = target
env.BUILDROOT = bldpath('')
env.SRCROOT = srcpath('')
env.APJ_TOOL = srcpath('Tools/scripts/apj_tool.py')
#Check if esp-idf env are loaded, or load it
try:
env.IDF = os.environ['IDF_PATH']
except:
env.IDF = cfg.srcnode.abspath()+"/modules/esp_idf"
print("USING EXPRESSIF IDF:"+str(env.IDF))
try:
env.DEFAULT_PARAMETERS = os.environ['DEFAULT_PARAMETERS']
except:
env.DEFAULT_PARAMETERS = cfg.srcnode.abspath()+"/libraries/AP_HAL_ESP32/boards/defaults.parm"
print("USING DEFAULT_PARAMETERS:"+str(env.DEFAULT_PARAMETERS))
#env.append_value('GIT_SUBMODULES', 'esp_idf')
# delete the output sdkconfig file when the input defaults changes. we take the
# stamp as the output so we can compute the path to the sdkconfig, yet it
# doesn't have to exist when we're done.
class clean_sdkconfig(Task.Task):
def keyword(self):
return "delete sdkconfig generated from"
def run(self):
prefix = ".clean-stamp-"
for out in self.outputs:
if not out.name.startswith(prefix):
raise ValueError("not a stamp file: "+out)
dest = out.parent.abspath()+"/"+out.name[len(prefix):]
if os.path.exists(dest):
os.unlink(dest)
# waf needs the output to exist after the task, so touch it
open(out.abspath(), "w").close()
def pre_build(self):
"""Configure esp-idf as lib target"""
lib_vars = OrderedDict()
lib_vars['ARDUPILOT_CMD'] = self.cmd
lib_vars['WAF_BUILD_TARGET'] = self.targets
lib_vars['ARDUPILOT_LIB'] = self.bldnode.find_or_declare('lib/').abspath()
lib_vars['ARDUPILOT_BIN'] = self.bldnode.find_or_declare('lib/bin').abspath()
target = self.env.ESP32_TARGET
esp_idf = self.cmake(
name='esp-idf',
cmake_vars=lib_vars,
cmake_src='libraries/AP_HAL_ESP32/targets/'+target+'/esp-idf',
cmake_bld='esp-idf_build',
)
esp_idf_showinc = esp_idf.build('showinc', target='esp-idf_build/includes.list')
# task to delete the sdkconfig (thereby causing it to be regenerated) when
# the .defaults changes. it uses a stamp to find the sdkconfig. changing
# the sdkconfig WILL NOT cause it to be deleted as it's not an input. this
# is by design so the user can tweak it for testing purposes.
clean_sdkconfig_task = esp_idf_showinc.create_task("clean_sdkconfig",
src=self.srcnode.find_or_declare(self.env.AP_HAL_ESP32+"/sdkconfig.defaults"),
tgt=self.bldnode.find_or_declare("esp-idf_build/.clean-stamp-sdkconfig"))
esp_idf_showinc.post()
# ensure the sdkconfig will be deleted before the cmake configure occurs
# that regenerates it
esp_idf_showinc.cmake_config_task.set_run_after(clean_sdkconfig_task)
from waflib import Task
class load_generated_includes(Task.Task):
"""After includes.list generated include it in env"""
always_run = True
def run(tsk):
bld = tsk.generator.bld
includes = bld.bldnode.find_or_declare('esp-idf_build/includes.list').read().split()
#print(includes)
bld.env.prepend_value('INCLUDES', includes)
tsk = load_generated_includes(env=self.env)
tsk.set_inputs(self.path.find_resource('esp-idf_build/includes.list'))
self.add_to_group(tsk)
@feature('esp32_ap_program')
@after_method('process_source')
def esp32_firmware(self):
self.link_task.always_run = True
esp_idf = self.bld.cmake('esp-idf')
build = esp_idf.build('all', target='esp-idf_build/ardupilot.bin')
build.post()
build.cmake_build_task.set_run_after(self.link_task)
# tool that can update the default params in a .bin or .apj
#self.default_params_task = self.create_task('set_default_parameters',
# src='esp-idf_build/ardupilot.bin')
#self.default_params_task.set_run_after(self.generate_bin_task)
# optional upload is last
if self.bld.options.upload:
flasher = esp_idf.build('flash')
flasher.post()
class set_default_parameters(Task.Task):
color='CYAN'
always_run = True
def keyword(self):
return "setting default params"
def run(self):
# TODO: disabled this task outright as apjtool appears to destroy checksums and/or the esp32 partition table
# TIP: if u do try this, afterwards, be sure to 'rm -rf build/esp32buzz/idf-plane/*.bin' and re-run waf
return
# (752) esp_image: Checksum failed. Calculated 0xd3 read 0xa3
# (752) boot: OTA app partition slot 0 is not bootable
# (753) esp_image: image at 0x200000 has invalid magic byte
# (759) boot_comm: mismatch chip ID, expected 0, found 65535
# (766) boot_comm: can't run on lower chip revision, expected 1, found 255
# (773) esp_image: image at 0x200000 has invalid SPI mode 255
# (779) esp_image: image at 0x200000 has invalid SPI size 15
# (786) boot: OTA app partition slot 1 is not bootable
# (792) boot: No bootable app partitions in the partition table
# skip task if nothing to do.
if not self.env.DEFAULT_PARAMETERS:
return
default_parameters = self.env.get_flat('DEFAULT_PARAMETERS').replace("'", "")
#print("apj defaults file:"+str(default_parameters))
_bin = str(self.inputs[0])
# paranoia check before and after apj_tool to see if file hash has changed...
cmd = "shasum -b {0}".format( _bin )
result = subprocess.check_output(cmd, shell=True)
prehash = str(result).split(' ')[0][2:]
cmd = "{1} {2} --set-file {3}".format(self.env.SRCROOT, self.env.APJ_TOOL, _bin, default_parameters )
print(cmd)
result = subprocess.check_output(cmd, shell=True)
if not isinstance(result, str):
result = result.decode()
for i in str(result).split('\n'):
print("\t"+i)
# paranoia check before and after apj_tool to see if file hash has changed...
cmd = "shasum -b {0}".format( _bin )
result = subprocess.check_output(cmd, shell=True)
posthash = str(result).split(' ')[0][2:]
# display --show output, helpful.
cmd = "{1} {2} --show ".format(self.env.SRCROOT, self.env.APJ_TOOL, _bin )
print(cmd)
result = subprocess.check_output(cmd, shell=True)
if not isinstance(result, str):
result = result.decode()
for i in str(result).split('\n'):
print("\t"+i)
# were embedded params updated in .bin?
if prehash == posthash:
print("Embedded params in .bin unchanged (probably already up-to-date)")
else:
print("Embedded params in .bin UPDATED")