Skip to content

9. Build from Source

liu weikai edited this page Apr 19, 2026 · 1 revision

Build from Source

Language: English. Chinese version: 9. Build from Source (中文)

This page is for developers rather than first-time users. It answers a different question: if you plan to modify code locally, validate targets, follow the current branch, or adapt new hardware, how should you understand the repository’s current build paths.

If your goal is only to flash a device and get started, read [[3. Installation & Flashing]] first.

Current Build Routes

Trail Mate is no longer a single-toolchain project. The main repository currently has at least two real build paths in active use:

  • PlatformIO + Arduino for the existing ESP32 handheld targets and the current nRF52 target
  • ESP-IDF for the shared-shell large-screen line centered on tab5 and t_display_p4

The longer-term architecture documents also leave room for Linux paths, but that is not the main stable user-facing route today.

Which Targets Developers Should Start With

For daily builds, regression checks, or mainline debugging, start with:

  • tlora_pager_sx1262
  • tdeck
  • gat562_mesh_evb_pro

The reason is practical:

  • tlora_pager_sx1262 and tdeck are closest to a full feature loop today
  • gat562_mesh_evb_pro exposes resource-constrained boundaries early

If you start with Tab5 or T-Display P4, you will often be debugging bring-up and platform-evolution issues before you are really looking at the main Trail Mate path itself.

PlatformIO Development Path

Build commands

platformio run -e tlora_pager_sx1262
platformio run -e tdeck
platformio run -e gat562_mesh_evb_pro

Debug environments

The repository already contains several debug environments, for example:

platformio run -e tlora_pager_sx1262_debug
platformio run -e tlora_pager_sx1280_debug
platformio run -e tdeck_debug
platformio run -e lilygo_twatch_s3_debug

These enable additional logging-related macros such as:

  • MESHCORE_LOG_ENABLE
  • LORA_LOG_ENABLE
  • APP_EVENT_LOG_ENABLE

That makes them useful for protocol, radio, and board-level diagnosis, but not automatically ideal for every constrained target during normal validation.

Where environment differences live

The root platformio.ini now mainly carries shared configuration. Specific target environments are distributed under:

variants/*/envs/*.ini

So when you are looking for build differences, do not stop at the root config. Screen size, input model, radio variant, and debug macro differences often live inside the target-specific environment files.

ESP-IDF Development Path

Current targets

The README currently names these IDF targets explicitly:

  • tab5
  • t_display_p4

Typical commands

idf.py -B build.tab5 -DTRAIL_MATE_IDF_TARGET=tab5 reconfigure build
idf.py -B build.tab5 -DTRAIL_MATE_IDF_TARGET=tab5 -p COM6 flash
idf.py -B build.tab5 -DTRAIL_MATE_IDF_TARGET=tab5 monitor

What is different about this path

  • apps/esp_idf is the shared IDF application-shell root
  • each board target hangs off apps/esp_idf/targets/<board>/
  • sdkconfig lives with the build directory rather than polluting the repository root

If you are used to “one board, one whole app tree”, the project is intentionally moving away from that model and toward “shared shell plus board-specific target metadata”.

Repository Structure You Should Understand Before Editing

The top-level directories most relevant to development are:

apps/
boards/
modules/
platform/
variants/
tools/
docs/

Their roles can be summarized like this:

  • apps/: app shells and entry points
  • boards/: board runtime and board-specific facts
  • modules/: shared business logic and protocol, map, Team, UI cores
  • platform/: platform adaptation layers
  • variants/: PlatformIO target environments and build differences
  • tools/: helper scripts
  • docs/: design, protocol, and focused technical notes

Release vs Debug Builds

The repository does not currently express “release” and “debug” as one unified global switch. Instead, those differences are mostly modeled through target environments.

The practical distinctions are usually:

  • whether extra protocol, radio, or event logs are enabled
  • whether the configuration remains suitable for constrained targets
  • whether the result matches the mainline validation path more closely

So if your goal is “is this feature still working”, start with the ordinary environment. If your goal is “why is this protocol or board path behaving strangely”, then move to the debug environment.

Resource Files Still Matter Even After A Successful Build

A successful firmware build does not mean the whole feature set is ready to demonstrate. In Trail Mate, code and resources are separate concerns.

Even after firmware builds correctly:

  • maps still need offline tiles
  • routes still need /routes/*.kml
  • track workflows still need /trackers/*

When validating features, treat resource preparation as part of the test setup rather than assuming build success implies full behavior.

Common Build Failure Triage

First ask whether the target is wrong

This is more common than many C++ errors. It includes cases such as choosing the wrong Pager radio variant or trying to validate the mainline through an experimental large-screen target.

Then ask whether platform boundaries were broken

If a piece of code suddenly depends directly on Arduino, ESP-IDF, FreeRTOS, or LVGL headers from inside what should be a shared module, the problem may be an architectural boundary violation rather than a simple build break.

Then ask whether the target is resource-constrained

Targets like GAT562 are not just another compile destination. They have explicit resource and feature boundaries, so assumptions that are harmless on larger ESP32 devices may not survive there.

A Helpful Reading Order For Developers

If you plan to change code, this order usually builds the right context fastest:

  1. [[10. Codebase Overview]]
  2. [[11. Architecture]]
  3. [[12. Design Decisions]]
  4. then come back to the target-specific env, board, and platform implementation

That order helps prevent a common mistake: reading one .cpp file in isolation and confusing the current local implementation with the project’s intended long-term boundary.

Clone this wiki locally