A guided tour of how PebbleOS is put together, deep-linking to the design prose that lives in the source tree. The in-source comments are canonical — these pages summarize and point, they do not duplicate.
PebbleOS runs on its own kernel API (include/pbl/kernel,
implemented under kernel/). The main source layers, as described on the
API reference main page:
fw/applib — application framework and UI, the API surface exposed to
watchapps (SDK export describes how
functions get there).
fw/services — system services (Bluetooth, filesystem, activity, …).
fw/kernel — task management, events, memory.
drivers/ — hardware drivers (public interfaces under
include/pbl/drivers).
subsys/ — OS subsystems shared beyond the firmware tree; currently
logging, cron, the Bluetooth backends, CRC, the
debug shell and the task watchdog, included
via the pbl/logging/, pbl/cron/, pbl/bluetooth/, pbl/crc/,
pbl/shell/ and pbl/task_wdt/ header paths.
Alongside these sit fw/shell (launcher/watchface UX flow),
fw/process_management (app lifecycle) and fw/comm (phone
communication).
The firmware runs a fixed set of kernel threads, enumerated in
fw/kernel/pebble_tasks.h: KernelMain, KernelBackground, Worker, App,
the Bluetooth tasks (host, controller, HCI), NewTimers and
PULSE. main()
(fw/main.c) performs SoC init and spawns KernelMain, which brings up
the rest of the system.
The bootloader is not part of this tree; it selects which firmware image to
launch, coordinated through boot bits (see BOOT_BIT_* usage in
fw/main.c) and the slot metadata in fw/system/firmware_storage.h.
Normal firmware and PRF (Pebble Recovery Firmware — the minimal fallback
image used to reinstall the main firmware) are separate compile-time
variants: pbl configure --variant=prf (see
build options) applies fw/prj_prf.conf on
top of the base config, disabling the JS engine and marking the image as
recovery firmware.
App identity — an installed app is referred to by several identifiers
(AppInstallId, AppInstallEntry, UUID, PebbleProcessMd); the comment
at the top of fw/process_management/app_install_manager.h explains
which to use where and which are deprecated.
Shell flow — which app launches at startup and what happens when an
app exits is a deliberately flat state machine rooted in the launcher and
the watchface; see the diagrammed comment at the top of
fw/shell/normal/system_app_state_machine.c.
Privilege boundary — third-party app and worker code runs unprivileged
(processes built into the firmware stay privileged); anything
touching OS state crosses into the kernel through a sys_* syscall
(declared in fw/syscall/syscall.h) defined with DEFINE_SYSCALL
from fw/syscall/syscall_internal.h, which raises privileges on
entry and drops them on return unless the caller was already privileged.
fw/linker/pebbleos.ld (“Section concepts!” comment) documents the
flash/RAM picture: VMA vs LMA, the kernel data/bss/stack/heap region, and
the fixed app region where third-party app code, data and heap live.
fw/linker/memory.ld explains how SRAM is carved between kernel, app
and worker regions, and fw/linker/regions.ld notes the MPU
power-of-two constraints.
fw/comm/ble/ is the host-side BLE layer and carries substantial design
prose:
gap_le_connect.c — connection management and the “connection intent”
abstraction that virtualizes the link across multiple clients.
gap_le_advert.c — round-robin scheduling of multiple advertising jobs
onto a controller that only holds one payload at a time (the comment
predates the current controllers, but the scheduler it describes is still
in use).
gap_le_connect_params.c — a primer on connection intervals, slave
latency and supervision timeouts, and why the firmware deviates from the
spec-recommended parameter-update pause for iOS.
Beneath it sits NimBLE (third_party/nimble), glued in by
subsys/bluetooth/. The HCI transport is chosen per SoC (BT_HCI): the
NimBLE controller on the nRF52 radio, the SiFli LCPU over IPC on SF32LB52,
and a fake controller on QEMU that acknowledges every command so the host
runs without a radio. On QEMU the phone link is the emulator’s serial
channel, fw/comm/qemu/.
PFS, the Pebble File System, lives in fw/services/filesystem/. The API
and on-flash layout are documented in pfs.h; the wear-leveling strategy
(round-robin page allocation tracking the last written page) is described
alongside the allocator in pfs.c.
Accelerometer — include/pbl/drivers/accel.h explains the split
between the dumb low-level driver and the accel service that owns
buffering, clients and subsampling, so the same service code runs on any
accel part.
Flash — drivers/flash/README.md documents the two flash APIs:
the main one, and a coredump-only path that must work without OS services.
The on-flash coredump image format (header plus chunked records, including
per-thread register sets) is documented in fw/kernel/core_dump.c.
Longer design documents live as their own pages:
Generated from PebbleOS 91af4a22c. This page is maintained in the pebbleos repository: docs/architecture/index.md.