Skip to content

Give the 4MB T3-S3 boards an app partition that fits - #10978

Merged
thebentern merged 2 commits into
developfrom
fix/tlora-t3s3-partition-table
Jul 10, 2026
Merged

Give the 4MB T3-S3 boards an app partition that fits#10978
thebentern merged 2 commits into
developfrom
fix/tlora-t3s3-partition-table

Conversation

@thebentern

@thebentern thebentern commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Problem

tlora-t3s3-v1 and tlora-t3s3-epaper have exceeded the app slot of the shared partition-table.csv (ota_0 = 0x250000 = 2,424,832 B) on every develop build since 22072c5 (2026-06-20). The last green build, ca7d82629, cleared it by 881 bytes. develop today is ~38.7 KB over:

Error: The program size (2,463,504 bytes) is greater than maximum allowed (2,424,832 bytes)

These envs have no board_level, so bin/generate_ci_matrix.py --level pr omits them — they only build in the full matrix on push to develop. That's why no PR ever caught it. The board never really had room; TMM nexthop (#10745) just tipped it.

Fix

A dedicated partition-table-t3s3.csv for the 4 MB ESP32-S3FH4R2 T3-S3 boards, wired to all three t3s3 envs:

partition subtype offset size change
app ota_0 0x010000 0x290000 +256 KB (was 0x250000)
flashApp ota_1 0x2A0000 0x0A0000 unchanged
spiffs 0x340000 0x0C0000 −256 KB (was 0x100000)

The headroom comes out of spiffs, not flashApp. The unified BLE OTA image (meshtastic/esp32-unified-ota v1.0.1, mt-esp32s3-ota.bin) is 636,544 B against a 655,360 B slot, so ota_1 has nothing to give. The table is contiguous, ends exactly at 4 MiB, and keeps both app partitions 64 KB-aligned.

This follows existing precedent — partition-table-8MB.csv, default_8MB.csv, and default_16MB.csv are already selected per-variant via board_build.partitions.

Why not trim instead

Measured, not estimated:

lever bytes verdict
-D EXCLUDE_EMOJI (guard already exists, unused) −6,304 16% of the deficit
drop espressif/network_provisioning −5,740
HAS_TRAFFIC_MANAGEMENT=0 + exclude paxcounter/storeforward/rangetest/atak −46,636 cumulative lands at 99.7% full

The full trim clears the cap by only 8,413 bytes — weeks of headroom at develop's observed ~38 KB/3-weeks growth — in exchange for shipping a feature-reduced board. The slot was mis-sized, not the firmware.

(For the record: EXCLUDE_EMOJI also cannot be enabled as-is. numEmotes becomes 0, the clamp at CannedMessageModule.cpp:1835 sets emotePickerIndex = -1, and :1016 then reads emotes[-1].label. Reachable via CardKB fn+e.)

Flasher fix (first commit)

bin/device-install.sh read the spiffs offset from .mt.json into SPIFFS_OFFSET, then flashed littlefs at $OFFSET — a different variable still holding the hardcoded 0x300000. The metadata value was never used. Under the new table that writes the filesystem into flashApp, clobbering the OTA image.

Unified on SPIFFS_OFFSET, guarded both metadata overrides against empty/null jq results, and quoted the offsets at the call site. device-install.bat already did all three correctly; this brings the shell script to parity.

Verification

Docker build of all three envs against the new table:

env flash usage
tlora-t3s3-v1 2,463,504 91.7%
tlora-t3s3-epaper 2,458,487 91.5%
tlora-t3s3-epaper-inkhud 2,400,851 89.4%

Partition table validated for contiguity, 64 KB app alignment, and exact 4 MiB end. bash -n + shellcheck clean on the flasher.

⚠️ Flash layout change

Existing T3-S3 units must be erased and factory-flashed once to pick up the new offsets; an OTA across the boundary would land the image at the wrong address. No working configuration regresses — these boards already cannot run a current develop build.

Downstream flashers that hardcode 0x260000/0x300000 for ESP32 (web flasher, mobile apps) will need the same metadata-driven offset handling that device-install.sh/.bat now use.

Follow-up (not in this PR)

Once this is green, adding board_level = pr to the t3s3 envs would make PR CI catch the next overflow instead of develop.

Summary by CodeRabbit

  • Bug Fixes

    • Improved device installation by correctly applying firmware-specific OTA and SPIFFS partition offsets.
    • Added safer handling for missing partition metadata and flashing parameters.
  • Configuration

    • Updated ESP32-S3 builds to use the correct T3S3 partition table.
    • Applied consistent partition settings across supported T3S3 device variants.

device-install.sh read the spiffs offset out of the .mt.json metadata into
SPIFFS_OFFSET, but flashed the littlefs image at $OFFSET -- a separate
variable still holding the hardcoded 0x300000 default. The metadata value
was never used, so any board with a non-default partition table had its
filesystem written to the wrong place.

Unify on SPIFFS_OFFSET, and guard both metadata overrides so a table that
omits ota_1 or spiffs falls back to the built-in default instead of handing
esptool an empty offset. Quote both offsets at the call site: the spiffs one
is newly consumed from jq, and a multi-line result would otherwise word-split
into esptool's argv and silently mis-pair address with file.

device-install.bat already does all of this correctly; this brings the shell
script to parity.
tlora-t3s3-v1 and tlora-t3s3-epaper have overflowed the shared 4MB
partition-table.csv app slot (ota_0 = 0x250000 = 2,424,832 bytes) on every
develop build since 22072c5 (2026-06-20). The last green build,
ca7d826, cleared it by 881 bytes; develop is now ~38.7KB over. These envs
have no board_level, so they only build in the full matrix on push to
develop -- which is why no PR ever caught it.

Add partition-table-t3s3.csv, dedicated to the 4MB ESP32-S3FH4R2 T3-S3
boards, and point all three t3s3 envs at it:

  app      ota_0  0x010000  0x290000   (was 0x250000, +256KB)
  flashApp ota_1  0x2A0000  0x0A0000   (unchanged size)
  spiffs          0x340000  0x0C0000   (was 0x100000, -256KB)

The headroom comes out of spiffs, not flashApp: the unified BLE OTA image is
636,544 bytes against a 655,360 byte slot, so ota_1 has nothing to give. The
table is contiguous, ends exactly on the 4MB boundary, keeps both app
partitions 64KB-aligned, and leaves the littlefs image these boards ship well
inside the remaining 768KB.

Verified with a Docker build of all three envs:

  tlora-t3s3-v1             2,463,504  91.7%
  tlora-t3s3-epaper         2,458,487  91.5%
  tlora-t3s3-epaper-inkhud  2,400,851  89.4%

Trimming was considered and rejected. The entire emoji set is 6,304 bytes
(measured A/B build), and cutting traffic management plus the paxcounter,
storeforward, rangetest and atak modules recovers 46,636 -- enough to land at
99.7% full, i.e. weeks of headroom at develop's observed growth rate, in
exchange for shipping a feature-reduced board. The slot was simply mis-sized
for a board this full.

Note this changes flash layout: existing T3-S3 units must be erased and
factory-flashed once to pick up the new offsets. They already cannot run a
current develop build, so no working configuration regresses.
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 130248dd-90d6-41dd-8a58-136dfab864d2

📥 Commits

Reviewing files that changed from the base of the PR and between f4efbd9 and edff110.

⛔ Files ignored due to path filters (1)
  • partition-table-t3s3.csv is excluded by !**/*.csv
📒 Files selected for processing (3)
  • bin/device-install.sh
  • variants/esp32s3/tlora_t3s3_epaper/platformio.ini
  • variants/esp32s3/tlora_t3s3_v1/platformio.ini

📝 Walkthrough

Walkthrough

T3S3 PlatformIO environments now select an explicit partition table. The device installer defines fallback offsets, conditionally uses valid firmware metadata offsets, and flashes OTA and SPIFFS images using the resolved values.

Changes

Partition-aware firmware flashing

Layer / File(s) Summary
T3S3 partition table configuration
variants/esp32s3/tlora_t3s3_epaper/platformio.ini, variants/esp32s3/tlora_t3s3_v1/platformio.ini
T3S3 environments explicitly select partition-table-t3s3.csv and update their build_flags assignments.
Metadata offset resolution and flashing
bin/device-install.sh
The installer uses fallback offsets, conditionally accepts non-empty metadata offsets, and passes resolved OTA and SPIFFS offsets to esptool.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: a dedicated 4MB T3-S3 partition layout for a fitting app slot.
Description check ✅ Passed The description is detailed and covers the problem, fix, verification, and flash-layout impact, with only the template-style checkboxes omitted.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/tlora-t3s3-partition-table

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

⚡ Try this PR in the Web Flasher

Flash this PR in the Web Flasher

firmware commit boards expires

Warning

This is an automated, unreviewed CI test build. Back up your device configuration
before flashing, and only flash devices you are able to recover.

Supported boards built by this PR (27)
Device Board Platform
Crowpanel Adv 3.5 TFT elecrow-adv-35-tft esp32-s3
Heltec HT62 heltec-ht62-esp32c3-sx1262 esp32-c3
Heltec Mesh Node 096 heltec-mesh-node-t096 nrf52840
Heltec Mesh Node T1 heltec-mesh-node-t1 nrf52840
Heltec Mesh Node T114 heltec-mesh-node-t114 nrf52840
Heltec V3 heltec-v3 esp32-s3
Heltec V4 heltec-v4 esp32-s3
Meshnology W10 meshnology_w10 esp32-s3
Raspberry Pi Pico pico rp2040
Raspberry Pi Pico W picow rp2040
RAK WisMesh Tag rak_wismeshtag nrf52840
RAK WisBlock 11200 rak11200 esp32
RAK WisBlock 11310 rak11310 rp2040
RAK3312 rak3312 esp32-s3
RAK WisBlock 4631 rak4631 nrf52840
Seeed SenseCAP Mesh-Tracker-X1 seeed_mesh_tracker_X1 nrf52840
Seeed Wio Tracker L1 seeed_wio_tracker_L1 nrf52840
Seeed Xiao NRF52840 Kit seeed_xiao_nrf52840_kit nrf52840
Seeed Xiao ESP32-S3 seeed-xiao-s3 esp32-s3
Station G2 station-g2 esp32-s3
Station G3 station-g3 esp32-s3
LILYGO T-Deck t-deck-tft esp32-s3
LILYGO T-Echo t-echo nrf52840
LILYGO T-Echo Plus t-echo-plus nrf52840
LILYGO T-Impulse Plus t-impulse-plus nrf52840
LilyGo T3-C6 tlora-c6 esp32-c6
Seeed SenseCAP T1000-E tracker-t1000-e nrf52840

Build artifacts expire on 2026-08-09. Updated for edff110.

@thebentern
thebentern merged commit 8abae90 into develop Jul 10, 2026
97 of 99 checks passed
@caveman99
caveman99 deleted the fix/tlora-t3s3-partition-table branch August 26, 2026 19:41
Itzdavid01 pushed a commit to Itzdavid01/firmware that referenced this pull request Sep 5, 2026
* fix(device-install): flash littlefs at the offset from firmware metadata

device-install.sh read the spiffs offset out of the .mt.json metadata into
SPIFFS_OFFSET, but flashed the littlefs image at $OFFSET -- a separate
variable still holding the hardcoded 0x300000 default. The metadata value
was never used, so any board with a non-default partition table had its
filesystem written to the wrong place.

Unify on SPIFFS_OFFSET, and guard both metadata overrides so a table that
omits ota_1 or spiffs falls back to the built-in default instead of handing
esptool an empty offset. Quote both offsets at the call site: the spiffs one
is newly consumed from jq, and a multi-line result would otherwise word-split
into esptool's argv and silently mis-pair address with file.

device-install.bat already does all of this correctly; this brings the shell
script to parity.

* fix(tlora-t3s3): give the 4MB T3-S3 boards an app partition that fits

tlora-t3s3-v1 and tlora-t3s3-epaper have overflowed the shared 4MB
partition-table.csv app slot (ota_0 = 0x250000 = 2,424,832 bytes) on every
develop build since 22072c5 (2026-06-20). The last green build,
ca7d826, cleared it by 881 bytes; develop is now ~38.7KB over. These envs
have no board_level, so they only build in the full matrix on push to
develop -- which is why no PR ever caught it.

Add partition-table-t3s3.csv, dedicated to the 4MB ESP32-S3FH4R2 T3-S3
boards, and point all three t3s3 envs at it:

  app      ota_0  0x010000  0x290000   (was 0x250000, +256KB)
  flashApp ota_1  0x2A0000  0x0A0000   (unchanged size)
  spiffs          0x340000  0x0C0000   (was 0x100000, -256KB)

The headroom comes out of spiffs, not flashApp: the unified BLE OTA image is
636,544 bytes against a 655,360 byte slot, so ota_1 has nothing to give. The
table is contiguous, ends exactly on the 4MB boundary, keeps both app
partitions 64KB-aligned, and leaves the littlefs image these boards ship well
inside the remaining 768KB.

Verified with a Docker build of all three envs:

  tlora-t3s3-v1             2,463,504  91.7%
  tlora-t3s3-epaper         2,458,487  91.5%
  tlora-t3s3-epaper-inkhud  2,400,851  89.4%

Trimming was considered and rejected. The entire emoji set is 6,304 bytes
(measured A/B build), and cutting traffic management plus the paxcounter,
storeforward, rangetest and atak modules recovers 46,636 -- enough to land at
99.7% full, i.e. weeks of headroom at develop's observed growth rate, in
exchange for shipping a feature-reduced board. The slot was simply mis-sized
for a board this full.

Note this changes flash layout: existing T3-S3 units must be erased and
factory-flashed once to pick up the new offsets. They already cannot run a
current develop build, so no working configuration regresses.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant