Fix classic ESP32 full OTA builds
This commit is contained in:
1 parent
eed6581299
commit
7d5c7d4065
4 files changed
+80
-24
No files matched your search
@@ -63,6 +63,7 @@ SUPPORTED_PLATFORM_PATTERN='ESP32_PLATFORM|NRF52_PLATFORM|STM32_PLATFORM|RP2040_
|
||||
OUTPUT_DIR="out"
|
||||
ESP32_LORA_OTA_APP_LIMIT=$((0x150000 - 0x10000))
|
||||
ESP32_FULL_MAX_NEIGHBOURS=254
|
||||
ESP32_FULL_DRAM_LIMITED_MAX_NEIGHBOURS=50
|
||||
FALLBACK_VERSION_PREFIX="dev"
|
||||
FALLBACK_VERSION_DATE_FORMAT='+%Y-%m-%d-%H-%M'
|
||||
|
||||
@@ -82,8 +83,8 @@ Commands:
|
||||
build-firmware <target>: Build the firmware for the given build target.
|
||||
build-firmwares: Build all firmwares for all targets.
|
||||
build-firmwares-logging-matrix: Build all firmwares in standard, logging, MQTT, FULL ESP32 MQTT, and FULL ESP32 logging (no MQTT) profiles, logging each target under out/build-logs/ and continuing after failures.
|
||||
build-full-esp32-firmwares: Build only feature-complete ESP32 MQTT profiles with 254 neighbors, LoRa OTA, and expanded dual-OTA partitions.
|
||||
build-full-esp32-logging-firmwares: Build only feature-complete ESP32 profiles with 254 neighbors, logging, MQTT disabled, LoRa OTA, and expanded dual-OTA partitions.
|
||||
build-full-esp32-firmwares: Build only feature-complete ESP32 MQTT profiles with up to 254 neighbors, LoRa OTA, and expanded dual-OTA partitions.
|
||||
build-full-esp32-logging-firmwares: Build only feature-complete ESP32 profiles with up to 254 neighbors, logging, MQTT disabled, LoRa OTA, and expanded dual-OTA partitions.
|
||||
build-matching-firmwares <build-match-spec>: Build all firmwares for build targets containing the string given for <build-match-spec>.
|
||||
build-companion-firmwares: Build all companion firmwares for all build targets.
|
||||
build-repeater-firmwares: Build all repeater firmwares for all build targets.
|
||||
@@ -1738,6 +1739,16 @@ requires_esp32_companion_full_ota_fallback() {
|
||||
esac
|
||||
}
|
||||
|
||||
requires_esp32_full_dram_limited_neighbors() {
|
||||
# These classic ESP32 MQTT observers already declare a 50-entry table because
|
||||
# their GPS, TLS, and OTA state leave little internal DRAM. FULL restores flash-
|
||||
# limited features, but must not override that explicit RAM safety limit.
|
||||
case "${1,,}" in
|
||||
tbeam_sx1262_repeater_observer_mqtt|tbeam_sx1262_room_server_observer_mqtt|tbeam_sx1276_repeater_observer_mqtt|tbeam_sx1276_room_server_observer_mqtt) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
is_lora_ota_build() {
|
||||
local env_name=$1
|
||||
local env_name_lc=${env_name,,}
|
||||
@@ -1879,6 +1890,7 @@ apply_esp32_lora_ota_size_profile() {
|
||||
|
||||
apply_esp32_full_size_profile() {
|
||||
local env_name=$1
|
||||
local max_neighbours=$ESP32_FULL_MAX_NEIGHBOURS
|
||||
|
||||
if [ "$ESP32_FULL_BUILD" != "1" ] || ! supports_esp32_full_build "$env_name"; then
|
||||
return 0
|
||||
@@ -1890,9 +1902,15 @@ apply_esp32_full_size_profile() {
|
||||
export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -UWEBCONFIG_DISABLED -DWIFI_OTA_SEEDER=1"
|
||||
|
||||
# Keep ordinary builds at their board-defined neighbor capacity. FULL builds
|
||||
# use the largest table supported by the one-byte neighbor discovery indexes.
|
||||
append_platformio_build_unflags "-DMAX_NEIGHBOURS=50 -DMAX_NEIGHBOURS=8"
|
||||
export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DMAX_NEIGHBOURS=${ESP32_FULL_MAX_NEIGHBOURS}"
|
||||
# normally use the largest table supported by the one-byte neighbor discovery
|
||||
# indexes. Explicitly DRAM-limited classic ESP32 observers retain 50 entries.
|
||||
if requires_esp32_full_dram_limited_neighbors "$env_name"; then
|
||||
max_neighbours=$ESP32_FULL_DRAM_LIMITED_MAX_NEIGHBOURS
|
||||
append_platformio_build_unflags "-DMAX_NEIGHBOURS=8"
|
||||
else
|
||||
append_platformio_build_unflags "-DMAX_NEIGHBOURS=50 -DMAX_NEIGHBOURS=8"
|
||||
fi
|
||||
export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -DMAX_NEIGHBOURS=${max_neighbours}"
|
||||
|
||||
# Restore the full ElegantOTA implementation only when the target already
|
||||
# declares its dependency. Some ESP32-C6 targets intentionally have no
|
||||
@@ -2847,14 +2865,14 @@ run_full_esp32_profile() {
|
||||
fi
|
||||
|
||||
if [ "$logging_mode" = "on" ]; then
|
||||
echo "${profile_label}: building ${#full_targets[@]} feature-complete ESP32 target(s) with ${ESP32_FULL_MAX_NEIGHBOURS} neighbors, logging on, MQTT off, and expanded dual-OTA partitions."
|
||||
echo "${profile_label}: building ${#full_targets[@]} feature-complete ESP32 target(s) with up to ${ESP32_FULL_MAX_NEIGHBOURS} neighbors (target DRAM limits apply), logging on, MQTT off, and expanded dual-OTA partitions."
|
||||
echo "FULL logging artifacts exclude MQTT, include LoRa OTA, and use filename form: name-full-logging-ota-version."
|
||||
MESHDEBUG_OVERRIDE="on"
|
||||
PACKET_LOGGING_OVERRIDE="on"
|
||||
MQTT_BRIDGE_OVERRIDE="off"
|
||||
FIRMWARE_FILENAME_INFIX="full-logging"
|
||||
else
|
||||
echo "${profile_label}: building ${#full_targets[@]} feature-complete ESP32 MQTT target(s) with ${ESP32_FULL_MAX_NEIGHBOURS} neighbors, logging off, and expanded dual-OTA partitions."
|
||||
echo "${profile_label}: building ${#full_targets[@]} feature-complete ESP32 MQTT target(s) with up to ${ESP32_FULL_MAX_NEIGHBOURS} neighbors (target DRAM limits apply), logging off, and expanded dual-OTA partitions."
|
||||
echo "FULL artifacts include MQTT and LoRa OTA and use filename form: name-full-ota-version."
|
||||
MESHDEBUG_OVERRIDE="off"
|
||||
PACKET_LOGGING_OVERRIDE="off"
|
||||
|
||||
+5
-4
@@ -335,15 +335,16 @@ role.
|
||||
| Standard | Uses the selected target's role. Ordinary portable ESP32 repeater/room-server artifacts omit WebConfig to fit the legacy app slot. Explicit MQTT and WiFi-companion targets still use WiFi. |
|
||||
| Logging | Enables USB/debug packet logging and disables the MQTT bridge. Logging output is not an MQTT uplink. |
|
||||
| MQTT | Builds explicit MQTT observer or WiFi-companion-MQTT targets with USB packet logging off. |
|
||||
| FULL ESP32 | Uses the board's MQTT target with logging off, expanded dual-OTA partitions, 254 neighbors, LoRa OTA, and full-size ESP32 features such as WebConfig where supported. |
|
||||
| FULL ESP32 logging | Uses the board's non-MQTT target with debug and packet logging enabled, expanded dual-OTA partitions, 254 neighbors, and LoRa OTA. |
|
||||
| FULL ESP32 | Uses the board's MQTT target with logging off, expanded dual-OTA partitions, up to 254 neighbors, LoRa OTA, and full-size ESP32 features such as WebConfig where supported. Classic T-Beam MQTT observers retain their 50-entry table because they have no PSRAM. |
|
||||
| FULL ESP32 logging | Uses the board's non-MQTT target with debug and packet logging enabled, expanded dual-OTA partitions, up to 254 neighbors, and LoRa OTA. |
|
||||
| LoRa-OTA no-external-sensors | A lean repeater image with no MQTT; ESP32 builds retain the compact on-demand browser WiFi uploader and 254 neighbors. |
|
||||
|
||||
The interactive Option 1 **FULL everything** choice selects the FULL logging
|
||||
profile: logging is enabled and MQTT is disabled. The standalone FULL ESP32
|
||||
profile and Profile 4 of the five-profile matrix use the matching MQTT target
|
||||
instead. Both FULL profiles include LoRa OTA, WebConfig where supported, 254
|
||||
neighbors, and expanded dual-OTA partitions.
|
||||
instead. Both FULL profiles include LoRa OTA, WebConfig where supported, up to
|
||||
254 neighbors, and expanded dual-OTA partitions. Target-specific internal-DRAM
|
||||
limits still apply.
|
||||
|
||||
FULL images change the ESP32 partition layout. Flash the matching
|
||||
`*-merged.bin` once when installing that layout. A partition-layout change can
|
||||
|
||||
@@ -10,6 +10,19 @@
|
||||
namespace mesh {
|
||||
namespace ota {
|
||||
|
||||
OtaManager::~OtaManager() {
|
||||
free(_catalog_heap);
|
||||
}
|
||||
|
||||
bool OtaManager::expandCatalog() {
|
||||
if (_catalog_heap || OTA_INLINE_CATALOG >= OTA_MAX_CATALOG) return false;
|
||||
CatRow* expanded = static_cast<CatRow*>(malloc(sizeof(CatRow) * OTA_MAX_CATALOG));
|
||||
if (!expanded) return false;
|
||||
memcpy(expanded, _catalog_inline, sizeof(CatRow) * _n_cat);
|
||||
_catalog_heap = expanded;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t* OtaManager::ensureSourceLeaves() {
|
||||
#if defined(ESP32_PLATFORM)
|
||||
if (!_src_leaves) {
|
||||
@@ -539,25 +552,27 @@ void OtaManager::handleHave(const uint8_t* m, uint16_t n) {
|
||||
uint32_t target = rd_u32le(row + 4), fwver = rd_u32le(row + 8);
|
||||
uint8_t codec = row[12], flags = row[13];
|
||||
uint32_t have_count = rd_u16le(row + 14); // this source's progress
|
||||
CatRow* catalog = catalogData();
|
||||
int slot = -1, lru = 0; // upsert into the catalog (dedup by mid)
|
||||
for (int i = 0; i < _n_cat; i++) {
|
||||
if (memcmp(_catalog[i].mid, mid, 4) == 0) { slot = i; break; }
|
||||
if (_catalog[i].last_ms < _catalog[lru].last_ms) lru = i;
|
||||
if (memcmp(catalog[i].mid, mid, 4) == 0) { slot = i; break; }
|
||||
if (catalog[i].last_ms < catalog[lru].last_ms) lru = i;
|
||||
}
|
||||
if (slot < 0) {
|
||||
slot = (_n_cat < OTA_MAX_CATALOG) ? _n_cat++ : lru;
|
||||
_catalog[slot] = CatRow{};
|
||||
memcpy(_catalog[slot].mid, mid, 4);
|
||||
memcpy(_catalog[slot].seeders[0], hv.seeder_id, 4);
|
||||
_catalog[slot].n_seeders = 1;
|
||||
if (_n_cat >= catalogCapacity() && expandCatalog()) catalog = catalogData();
|
||||
slot = (_n_cat < catalogCapacity()) ? _n_cat++ : lru;
|
||||
catalog[slot] = CatRow{};
|
||||
memcpy(catalog[slot].mid, mid, 4);
|
||||
memcpy(catalog[slot].seeders[0], hv.seeder_id, 4);
|
||||
catalog[slot].n_seeders = 1;
|
||||
} else {
|
||||
CatRow& cc = _catalog[slot]; // count DISTINCT sources (no double-count)
|
||||
CatRow& cc = catalog[slot]; // count DISTINCT sources (no double-count)
|
||||
bool known = false;
|
||||
for (uint8_t k = 0; k < cc.n_seeders; k++)
|
||||
if (memcmp(cc.seeders[k], hv.seeder_id, 4) == 0) { known = true; break; }
|
||||
if (!known && cc.n_seeders < OTA_CAT_SEEDERS) memcpy(cc.seeders[cc.n_seeders++], hv.seeder_id, 4);
|
||||
}
|
||||
CatRow& c = _catalog[slot];
|
||||
CatRow& c = catalog[slot];
|
||||
c.target_id = target; c.fw_version = fwver; c.codec = codec; c.flags = flags; c.last_ms = _now_ms;
|
||||
if (have_count > c.have_max) c.have_max = have_count; // best-known progress among sources
|
||||
if (wantRow(mid, target, codec, flags)) startFetch(mid, target);
|
||||
@@ -566,9 +581,10 @@ void OtaManager::handleHave(const uint8_t* m, uint16_t n) {
|
||||
|
||||
void OtaManager::deferCatalog(const uint8_t mid[4], uint32_t until_ms) {
|
||||
if (!mid) return;
|
||||
CatRow* catalog = catalogData();
|
||||
for (uint8_t i = 0; i < _n_cat; i++) {
|
||||
if (memcmp(_catalog[i].mid, mid, 4) == 0) {
|
||||
_catalog[i].retry_after_ms = until_ms;
|
||||
if (memcmp(catalog[i].mid, mid, 4) == 0) {
|
||||
catalog[i].retry_after_ms = until_ms;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +93,16 @@ typedef bool (*ServeReadFn)(void* ctx, uint32_t off, uint8_t* buf, uint32_t len)
|
||||
#ifndef OTA_MAX_CATALOG
|
||||
#define OTA_MAX_CATALOG 255 // protocol maximum; large SD seeders must remain browsable remotely
|
||||
#endif
|
||||
#ifndef OTA_INLINE_CATALOG
|
||||
#define OTA_INLINE_CATALOG 12 // keep the common case static; expand to OTA_MAX_CATALOG only on demand
|
||||
#endif
|
||||
#if OTA_INLINE_CATALOG < 1
|
||||
#error "OTA_INLINE_CATALOG must be at least 1"
|
||||
#endif
|
||||
#if OTA_INLINE_CATALOG > OTA_MAX_CATALOG
|
||||
#undef OTA_INLINE_CATALOG
|
||||
#define OTA_INLINE_CATALOG OTA_MAX_CATALOG
|
||||
#endif
|
||||
#ifndef OTA_QUERY_MIN_MS
|
||||
#define OTA_QUERY_MIN_MS 300 // min delay before sending a catalog query (overhear-suppression window)
|
||||
#endif
|
||||
@@ -128,6 +138,11 @@ typedef bool (*ServeReadFn)(void* ctx, uint32_t off, uint8_t* buf, uint32_t len)
|
||||
|
||||
class OtaManager {
|
||||
public:
|
||||
OtaManager() = default;
|
||||
~OtaManager();
|
||||
OtaManager(const OtaManager&) = delete;
|
||||
OtaManager& operator=(const OtaManager&) = delete;
|
||||
|
||||
// PAUSED: a folder-destination write failed mid-transfer (the seeder link dropped). Progress is held on
|
||||
// the host; the manager stops requesting and does NOT fall back to RAM/flash. resumeStaged() (called on
|
||||
// reconnect) re-STATs the host file, recomputes which blocks are missing, and resumes.
|
||||
@@ -319,7 +334,7 @@ public:
|
||||
uint32_t retry_after_ms; // per-image archive retry deadline (0 = eligible)
|
||||
};
|
||||
uint8_t catalogCount() const { return _n_cat; }
|
||||
const CatRow* catalogRow(uint8_t i) const { return i < _n_cat ? &_catalog[i] : nullptr; }
|
||||
const CatRow* catalogRow(uint8_t i) const { return i < _n_cat ? &catalogData()[i] : nullptr; }
|
||||
uint8_t sourceCount() const { return _n_src; } // distinct OTA sources (beacon senders) heard
|
||||
void deferCatalog(const uint8_t mid[4], uint32_t until_ms);
|
||||
bool catalogReady(const CatRow* row, uint32_t now_ms) const {
|
||||
@@ -327,6 +342,11 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
CatRow* catalogData() { return _catalog_heap ? _catalog_heap : _catalog_inline; }
|
||||
const CatRow* catalogData() const { return _catalog_heap ? _catalog_heap : _catalog_inline; }
|
||||
uint16_t catalogCapacity() const { return _catalog_heap ? OTA_MAX_CATALOG : OTA_INLINE_CATALOG; }
|
||||
bool expandCatalog();
|
||||
|
||||
void emit(const uint8_t* b, uint16_t n, bool flood) { if (_send && n) _send(_ctx, b, n, flood); }
|
||||
void handleAdv(const uint8_t* m, uint16_t n); // beacon -> sources table (+ query if interested)
|
||||
void handleQuery(const uint8_t* m, uint16_t n); // serve: reply OTA_HAVE catalog
|
||||
@@ -464,7 +484,8 @@ private:
|
||||
};
|
||||
Source _sources[OTA_MAX_SOURCES];
|
||||
uint8_t _n_src = 0;
|
||||
CatRow _catalog[OTA_MAX_CATALOG];
|
||||
CatRow _catalog_inline[OTA_INLINE_CATALOG];
|
||||
CatRow* _catalog_heap = nullptr;
|
||||
uint8_t _n_cat = 0;
|
||||
uint32_t _now_ms = 0; // coarse clock (fed by set_clock; for ages/LRU/jitter)
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user