Files

Cardputer Zero Linux App Shell

This app shell is the Trail Mate product route for the Cardputer Zero portable Linux device. It is not the Linux simulator.

The first slice intentionally keeps the runtime entry small: the shell validates the cardputerzero target profile, consumes the boards/cardputerzero facts, selects the cardputer_compact UX pack, and builds through builds/linux_cmake. The device package now owns a Wayland APPLaunch product entry and a separate explicit fbdev/evdev debug fallback; hardware validation still has to happen on the real device session.

The shared LVGL main-menu profile for this target is already Cardputer Zero specific and Pager-derived: make_cardputer_zero_profile() starts from make_pager_profile() and then shrinks geometry for the 320 x 170 display. The remaining UI work is page-by-page Cardputer Zero closure from that visual baseline, not a Linux-simulator or custom Linux-panel redesign.

Known hardware facts in this repo:

  • display: 320 x 170 logical pixels
  • input: built-in keyboard
  • touch, pointer, and trackball: absent in current board facts
  • external module: M5Stack Cap LoRa-1262, completing both LoRa and GPS/GNSS capability for this target
  • LoRa: SX1262 on /dev/spidev0.1, 500000 Hz, Reset=26, IRQ/DIO1=23, Busy=22, DIO2 RF switch and DIO3 TCXO enabled
  • GPS/GNSS: ATGM336H-6N@AT6668 on the Cap LoRa-1262 UART, NMEA 0183 4.1, default 115200 bps, with Cardputer Zero wiring G15=GPS_RX and G14=GPS_TX
  • The G15/G14 mapping is Cardputer Zero evidence and must not be inferred from the Cardputer-Adv G13/G15 table.

Known Cardputer Zero Linux session facts in this shell:

  • notifications are created through the standard org.freedesktop.Notifications D-Bus interface; the $XDG_RUNTIME_DIR/cardputer-zero/notifyd.sock channel is status/control only
  • text entry uses Fcitx5 through the normal Linux toolkit/frontend path
  • the Cardputer Zero IME addon/panel pair uses $XDG_RUNTIME_DIR/cardputer-zero/ime-panel.sock only to display preedit, candidates, and input-method state
  • Trail Mate does not own the notification daemon implementation, Fcitx5 UI addon, IME panel renderer, input method engine, dictionaries, or text commit path
  • the shared embedded touch/pinyin IME is not the Cardputer Zero Linux input method strategy
  • Cardputer Zero business pages are wired through runtime/presentation ports; this target opts out of legacy chat-delivery and legacy presentation source compilation, and the Linux CMake helper withholds the legacy-adapter include root for this target, so Legacy* compatibility adapters cannot become the new product path
  • Trail Mate must use its own Linux raw LoRa and GPS runtime paths for the Cap LoRa-1262; the external Meshtastic daemon package is hardware-reference evidence only, not the implementation owner

The simulator remains a separate development shell under apps/linux_sim_shell.

Device Launch And Debian Package

The package route builds the real Cardputer Zero shell, not the screenshot tool and not the simulator:

cd builds/linux_cmake
cmake --preset linux-cardputer-zero-release
cmake --build --preset linux-cardputer-zero-deb

On Windows/WSL, build from the helper instead so the package staging tree lives on a Linux filesystem and does not inherit /mnt/c executable-bit pollution:

bash apps/linux_cardputer_zero/tools/build_cardputer_zero_deb.sh

The resulting package is written under build/cardputer-zero-deb/. The helper builds through the Cardputer Zero Docker Compose builder, producing an actual Docker linux/arm64 Debian package while keeping build artifacts out of the repository root. The first run builds the reusable trailmate-cardputer-zero-builder:bookworm-arm64 image and installs Debian build dependencies into that image layer. Later package builds reuse the image and the compose-owned CMake build volume, so they should not repeat apt-get install unless the builder image is rebuilt, the APT mirror argument changes, or the Docker cache is cleared.

The package helper still creates a filtered source snapshot under /tmp/trailmate-cardputer-zero-package, excluding local object files, root CMake state, and existing package outputs before copying that snapshot into the builder container. This keeps Windows/WSL executable-bit behavior and accidental root build artifacts out of the .deb.

The container package entrypoint validates package version 0.1.29-alpha, architecture, required runtime dependencies, APPLaunch paths, executable permissions, AArch64 ELF metadata, and ldd resolution before copying the .deb into build/cardputer-zero-deb/.

If Docker's Debian mirror path is unstable, override the builder image APT mirror with TRAIL_MATE_CARDPUTER_ZERO_APT_MIRROR. The value should be the Debian archive root, for example:

TRAIL_MATE_CARDPUTER_ZERO_APT_MIRROR=http://mirrors.tuna.tsinghua.edu.cn/debian \
  bash apps/linux_cardputer_zero/tools/build_cardputer_zero_deb.sh

Using an HTTP Debian mirror here avoids the base container's pre-bootstrap certificate gap; package integrity still comes from APT's signed repository metadata.

To force dependency refresh after changing mirrors or base images, remove or rebuild the builder image:

TRAIL_MATE_CARDPUTER_ZERO_APT_MIRROR=http://mirrors.tuna.tsinghua.edu.cn/debian \
  docker compose \
  -f apps/linux_cardputer_zero/tools/compose.cardputer-zero-builder.yml \
  -p trailmate-cardputer-zero-package \
  build --no-cache deb-builder

The package installs the Cardputer Zero APPLaunch layout:

  • /usr/lib/trailmate-cardputer-zero/trailmate-cardputer-zero
  • /usr/lib/trailmate-cardputer-zero/trailmate-cardputer-zero-fbdev
  • /usr/lib/trailmate-cardputer-zero/trailmate-cardputer-zero-applaunch
  • /usr/share/APPLaunch/applications/trailmate.desktop
  • /usr/share/APPLaunch/share/images/trailmate-cardputer-zero.png

The default APPLaunch path is Wayland. In auto mode the wrapper starts trailmate-cardputer-zero only when WAYLAND_DISPLAY is present, and exits with a diagnostic instead of silently falling back to framebuffer when no Wayland session exists. The fbdev binary is reached only by setting TRAIL_MATE_DISPLAY_BACKEND=fbdev or another explicit framebuffer alias; that debug path honors APPLAUNCH_LINUX_FBDEV_DEVICE and APPLAUNCH_LINUX_KEYBOARD_DEVICE, detects the ST7789V framebuffer from /proc/fb, and falls back to /dev/fb1 plus the documented Cardputer Zero keyboard by-path event device. Set TRAIL_MATE_FRAMEBUFFER=/dev/fbN or TRAIL_MATE_INPUT_DEVICE=/dev/input/eventN only for manual framebuffer debugging outside the product Wayland session.

The APPLaunch wrapper also owns the Cardputer Zero Cap LoRa-1262 runtime defaults. Unless explicitly overridden by the environment, it launches in real mesh mode with:

  • TRAIL_MATE_RUNTIME_MODE=mesh
  • TRAIL_MATE_LORA_SPI=/dev/spidev0.1
  • TRAIL_MATE_LORA_GPIOCHIP=/dev/gpiochip0
  • TRAIL_MATE_LORA_RESET_GPIO=26
  • TRAIL_MATE_LORA_BUSY_GPIO=22
  • TRAIL_MATE_LORA_IRQ_GPIO=23
  • TRAIL_MATE_LORA_SPI_HZ=500000
  • TRAIL_MATE_LORA_DIO2_RF_SWITCH=1
  • TRAIL_MATE_LORA_DIO3_TCXO_1V8=1
  • TRAIL_MATE_GPS_BAUD=115200
  • TRAIL_MATE_GPS_AUTO_SERIAL=1
  • TRAIL_MATE_GPS_DEVICE_CANDIDATES=/dev/serial0:/dev/ttyAMA1:/dev/ttyAMA0:/dev/ttyS0:/dev/ttyS1
  • TRAIL_MATE_DESKTOP_NOTIFICATIONS=freedesktop

TRAIL_MATE_GPS_DEVICE is reserved for an explicit user override. When it is unset, the resident GPS service probes TRAIL_MATE_GPS_DEVICE_CANDIDATES and fails over to the next existing candidate when a serial endpoint opens but no NMEA bytes arrive. Serial aliases are de-duplicated after path canonicalization, so /dev/serial0 and its /dev/tty* target do not create a false failover loop. The app launcher therefore publishes candidates rather than hard-selecting /dev/serial0.

GPS and LoRa are resident services in this product shell. The app service loop ticks the Cap LoRa-1262 radio path and the GPS serial/NMEA path even when the Chat, Contacts, Map, or Sky Plot page is not currently open. Pages consume runtime state and send commands; they do not own radio polling or GPS serial reads.

Cardputer Zero Settings follows the same ownership boundary. Trail Mate exposes application settings that its runtime can apply. Linux session brightness and screen timeout belong to the operating system or desktop environment and are therefore not shown in the application Settings page. For GPS, the Linux runtime currently exposes the GPS enable switch, receiver baud selection, and diagnostics; ESP-only receiver initialization policy items are not shown on Cardputer Zero until the Linux runtime owns real receiver-command behavior for them.

LoRa And GPS Hardware Checks

Install the package on Cardputer Zero:

sudo apt install ./trailmate-cardputer-zero_0.1.29-alpha_arm64.deb

Check the installed package metadata and APPLaunch entry:

dpkg -s trailmate-cardputer-zero | sed -n '/^Package:/,/^Description:/p'
dpkg -L trailmate-cardputer-zero
sed -n '1,140p' /usr/lib/trailmate-cardputer-zero/trailmate-cardputer-zero-applaunch
cat /usr/share/APPLaunch/applications/trailmate.desktop

Before starting Trail Mate, confirm the hardware nodes exist:

ls -l /dev/spidev0.1 /dev/gpiochip0
ls -l /dev/serial0 /dev/ttyAMA1 /dev/ttyAMA0 /dev/ttyS0 /dev/ttyS1 2>/dev/null || true
cat /proc/cmdline | tr ' ' '\n' | grep '^console=' || true
pinctrl get 14
pinctrl get 15
systemctl status serial-getty@ttyS0.service --no-pager || true

If /dev/spidev0.1 is missing, LoRa cannot be brought up by Trail Mate. If all serial paths are missing, GPS cannot be read by Trail Mate.

console=ttyS0,115200 or console=serial0,115200 means the OS is still using the GPS UART as a serial console. That belongs in the Cardputer Zero OS profile, not the Trail Mate package. Re-run the OS profile installer or manually remove those console tokens, then reboot.

To watch the app launch logs directly from a terminal:

WAYLAND_DISPLAY=${WAYLAND_DISPLAY:-wayland-0} \
  /usr/lib/trailmate-cardputer-zero/trailmate-cardputer-zero-applaunch \
  2>&1 | tee /tmp/trailmate-cardputer-zero.log

The product path requires Wayland. For explicit framebuffer debugging only:

TRAIL_MATE_DISPLAY_BACKEND=fbdev \
  /usr/lib/trailmate-cardputer-zero/trailmate-cardputer-zero-applaunch \
  2>&1 | tee /tmp/trailmate-cardputer-zero-fbdev.log

Runtime packet logs are written under the Trail Mate settings root. By default that is $HOME/.trailmate_cardputer_zero/logs/. If TRAIL_MATE_SETTINGS_ROOT is set, use that root instead.

Watch LoRa packet logs:

tail -F "${TRAIL_MATE_SETTINGS_ROOT:-$HOME/.trailmate_cardputer_zero}/logs/lora.log"

Useful LoRa signs:

  • LoRa raw TX, Meshtastic raw TX, or protocol-specific TX lines mean the app attempted radio transmission.
  • LoRa raw RX, Meshtastic raw RX, or protocol-specific RX lines mean the SX1262 delivered received bytes to Trail Mate.
  • LoRa radio degraded, SX1262 endpoint present; driver not online, open spidev failed, configure spidev failed, or probe failed mean the runtime found the candidate path but could not bind or configure the radio.
  • No lora.log after the app has been running for a few seconds usually means either TRAIL_MATE_RUNTIME_MODE is not mesh, /dev/spidev0.1 is absent, or the app did not enter the Linux service loop.

Trigger a useful LoRa check from the UI by opening Contacts and running local scan/discovery, then watch lora.log for TX/RX entries. Another node nearby should cause RX entries when it broadcasts or replies.

Watch GPS/NMEA packet logs:

tail -F "${TRAIL_MATE_SETTINGS_ROOT:-$HOME/.trailmate_cardputer_zero}/logs/gps.log"

Useful GPS signs:

  • GPS source opened means Trail Mate opened the selected serial source and configured its baud rate.
  • GPS source open failed means the selected source path exists in config but could not be opened; check permissions, the dialout group, and the selected /dev/tty* path.
  • GPS serial waiting for NMEA means the serial source is open but Trail Mate has not yet parsed a complete NMEA sentence.
  • GPS serial no traffic means the currently opened auto-probed source produced no bytes within the failover window; Trail Mate will try the next candidate and log next_path.
  • NMEA sentence parsed means Trail Mate read a complete NMEA sentence and its checksum was valid.
  • NMEA checksum failed means UART bytes are arriving, but data integrity, baud rate, or wiring should be checked.
  • No gps.log after the app has been running for a few seconds usually means the app has not reached the Linux service loop, GPS was disabled in Settings, every configured/auto-probed serial path is absent, or TRAIL_MATE_SETTINGS_ROOT points somewhere else. The Cardputer Zero GPS service runs independently of the Sky Plot or Map page being open.

Quick raw GPS byte check:

for dev in /dev/serial0 /dev/ttyAMA1 /dev/ttyAMA0 /dev/ttyS0 /dev/ttyS1; do
  [ -e "$dev" ] || continue
  echo "===== $dev ====="
  sudo stty -F "$dev" 115200 raw -echo -ixon -ixoff -crtscts 2>&1 || true
  timeout 8s sh -c "cat '$dev' | head -c 500" | od -An -tx1 -c
done

Healthy output contains $GNGGA, $GNRMC, $GPGSV, $GLGSV, $GAGSV, or $BDGSV style NMEA sentences. A no-fix GPS can still be healthy; the key first signal is valid NMEA traffic, then later FIX/satellite values on the Sky Plot page.

Map tile diagnostics:

tail -F "${TRAIL_MATE_SETTINGS_ROOT:-$HOME/.trailmate_cardputer_zero}/logs/map.log"
find "${TRAIL_MATE_SD_ROOT:-$HOME/.trailmate_cardputer_zero/sd}/maps/base" -type f | head
sqlite3 "${TRAIL_MATE_SETTINGS_ROOT:-$HOME/.trailmate_cardputer_zero}/trailmate.sqlite" \
  "select source,z,x,y,status,http_status,bytes,last_error from map_tile_cache order by fetched_at desc limit 20;"

If map.log shows queued tiles followed by download failures, the Map page has already handed the request to the Linux tile runtime. Check the network path from the device itself:

getent ahosts tile.openstreetmap.org
curl -I -L --connect-timeout 5 https://tile.openstreetmap.org/7/100/54.png

Linux tile downloads use the system resolver by default. DoH is opt-in through TRAIL_MATE_MAP_DOH_URL or TRAIL_MATE_CURL_DOH_URL; set one of them only when that resolver is reachable from the device network. If a network prefers a specific IP family, set TRAIL_MATE_MAP_IP_RESOLVE=ipv4 or TRAIL_MATE_MAP_IP_RESOLVE=ipv6. If the default public tile endpoint is not reachable from the device network, configure a reachable template such as TRAIL_MATE_OSM_TILE_URL.

When a local mixed HTTP proxy is already listening on 127.0.0.1:7890, the Cardputer Zero APPLaunch wrapper exports the standard curl proxy variables for Trail Mate. Explicit proxy variables supplied by the session are preserved.

Check the Cardputer Zero notification path:

notify-send "Trail Mate" "Notification daemon is working"
busctl --user call org.freedesktop.Notifications \
  /org/freedesktop/Notifications \
  org.freedesktop.Notifications GetServerInformation
journalctl --user -u cardputer-zero-notifyd -f

Incoming Trail Mate chat messages call the same freedesktop notification interface when TRAIL_MATE_DESKTOP_NOTIFICATIONS=freedesktop. The $XDG_RUNTIME_DIR/cardputer-zero/notifyd.sock channel remains status/control only and is not used to create notifications.

This is a CPack device-install package. It intentionally does not claim the full LoFiBox-Zero Debian source-package flow yet: there is no debian/ directory, no dpkg-buildpackage source package, no lintian/autopkgtest gate, and no signed APT publication path for amd64/arm64. Those belong to a later packaging-governance slice if Cardputer Zero needs LoFiBox-style preview APT distribution.

Screenshot Evidence

The current shared LVGL Cardputer compact route has screenshot evidence for the ten Cardputer Zero product pages. PC Link, SSTV, Energy Sweep / Spectrum, and SD Storage / USB Disk are intentionally excluded from this product menu. SD Storage is the card-access/USB mass-storage entry, not Extensions.

$targets = @(
  'dashboard',
  'chat',
  'contacts',
  'map',
  'sky_plot',
  'team',
  'tracker',
  'walkie',
  'extensions',
  'settings'
)
wsl bash -lc 'cd /mnt/c/Users/VicLi/Documents/Projects/trail-mate && rm -f docs/images/cardputerzero/screenshots/*.png'
foreach ($target in $targets) {
  wsl bash -lc "cd /mnt/c/Users/VicLi/Documents/Projects/trail-mate && timeout 40s env TRAIL_MATE_LORA_DISABLE=1 builds/linux_cmake/build/linux-cardputer-zero-debug/apps/linux_cardputer_zero/trailmate_linux_cardputer_zero_screenshot_capture /mnt/c/Users/VicLi/Documents/Projects/trail-mate/docs/images/cardputerzero/screenshots $target"
  if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
}

See docs/targets/cardputerzero-screenshots.md for the screenshot matrix, including the Map page's compact zoom, center, base-layer, and contour controls, and docs/targets/cardputerzero-adaptation.md for the current validation state and remaining hardware/session closure.