Web panel and HTTP API
Waybeam includes a built-in web panel and a full HTTP API for real-time parameter control. The web panel is available at http://<camera-ip>/ (default port — 80) or simply http://waybeam.local — the camera announces itself via mDNS. The reference is verified against version v0.40.1 (July 2026).
Web panel
Settings tab
Configuration fields are grouped into 14 sections:
| Section | Description |
|---|---|
| System | Port, overclock, logging |
| Sensor | Sensor selection (index / mode) |
| ISP | Exposure, AWB, AE engine (aeEngine only takes effect on Star6E; Maruko always runs the vendor's native paced AE+AWB) |
| Image | Mirror, flip, rotate |
| Video | Codec, bitrate, FPS, GOP, framing, resilience |
| Outgoing | Streaming, address, mode |
| Discovery | mDNS announcement on the network (waybeam.local) |
| Audio | Codec, sample rate, volume |
| FPV | ROI encoding + 3DNR |
| IMU | BMI270 gyro |
| Attitude | Artificial horizon: roll/pitch/yaw from the IMU, mount angles, level trims |
| Recording | SD card recording |
| Adaptive Encoder Control | Scene detection |
| Debug | OSD |
Interface elements:
- 🟢 Live — the parameter changes instantly without a restart
- 🟠 Restart — requires a pipeline reinit (automatic)
- 🔴 Reboot — requires a full camera reboot (only
video0.resilience) - Apply Changes — apply all changed fields
- Save & Restart — apply changes and restart the pipeline
- Restore Defaults — reload the on-disk configuration
For versions before v0.8 — the EIS section (GyroGlide)
Earlier versions had a separate eis section (gyroscopic GyroGlide stabilization). It was removed in v0.8.0. Stabilization now lives in the Video section — the video0.framing field (see below).
For versions before v0.40 — 13 sections, no Attitude
The Attitude section (live roll/pitch/yaw and the "Capture level trims" button) appeared in the WebUI in v0.40. In 0.24–0.39 there were 13 sections, and the BMI270 gyro was listed as a POC with no consumer.
Custom dashboard
In current builds (July 2026) the built-in web interface can be replaced by dropping your own files into /usr/share/www — they take priority over the bundled dashboard.
API Reference tab
Documentation for all HTTP endpoints with example responses. Categories: Configuration, Encoder Control, ISP & Image Quality, Recording, Dual-Stream.
Image Quality (ISP) tab
Direct access to 62 SigmaStar ISP parameters:
- Parameters — collapsible sections with parameter chips
- Multi-fields — built-in editor for complex parameters (colortrans, OBC, demosaic, etc.)
- Export / Import — save and restore ISP profiles as JSON
HTTP API reference
All endpoints use HTTP GET (BusyBox wget compatible). Responses are JSON in the {"ok": true/false, ...} format.
Three mutability levels
- live — applied instantly without interrupting the stream
- restart_required — triggers an automatic pipeline reinit
- reboot — requires a full camera reboot (only
video0.resilience)
/api/v1/capabilities shows each field's mutability level and backend support.
GET /api/v1/version
Returns version information.
curl http://<ip>/api/v1/versionResponse:
{
"ok": true,
"data": {
"app_version": "0.40.1",
"backend": "star6e",
"contract_version": "0.12.0",
"config_schema_version": "0.12.0"
}
}The contract_version / config_schema_version values grow with releases (for example, v0.19 bumped the contract to 0.11.0 by removing video0.frameLost, and v0.40 bumped it to 0.12.0 with the attitude API).
GET /api/v1/config — the full active configuration.
GET /api/v1/capabilities — each field's mutability (live / restart_required) and backend support.
curl http://<ip>/api/v1/config
curl http://<ip>/api/v1/capabilitiesChecking support
Field support is backend-specific. For example, Star6E reports video0.scene_threshold as supported, while Maruko does not. Use capabilities before writing.
GET /api/v1/modes
Sensor mode introspection (pad + resolution) — the current selection and every mode the SDK enumerates. Populates the WebUI dropdown.
curl http://<ip>/api/v1/modessensor.mode indices renumbered (v0.21–v0.34)
The mode lineups were rebuilt on both platforms: Maruko in v0.21/v0.23, Star6E in v0.25–v0.34 (in-tree IMX335/IMX415 drivers). The Star6E lineups are now: IMX335 — 2560×1920@30/60, 2560×1440@90, 2176×1224@100, 1920×1080@120, 1600×900@144; IMX415 — 5 visible modes from 4K@~33 down to 1728×816@120. The sensor.mode indices were renumbered several times along the way, and there are fewer modes than in the stock driver: a persisted index outside the new range keeps waybeam from starting. After upgrading, check old configs against the /api/v1/modes list or set -1 (auto).
v0.21/v0.23 renumbering details (Maruko)
The mode lineups on Maruko were reworked: the IMX415 gained non-binned 16:9 modes at 1485 Mbps (up to 1920×1080@100), the IMX335 — a best-per-fps lineup (30/50/60/90/100) plus the ultra-low-latency 1536×864@144 (v0.24).
Reading and writing fields
GET /api/v1/get?field_name
curl "http://<ip>/api/v1/get?video0.bitrate"{"ok": true, "data": {"field": "video0.bitrate", "value": 8192}}GET /api/v1/set?field_name=value
Write a field. Live fields apply instantly. Restart fields trigger a reinit.
# Instant bitrate change (live)
curl "http://<ip>/api/v1/set?video0.bitrate=4096"
# Multi-set (live fields only)
curl "http://<ip>/api/v1/set?video0.bitrate=4096&system.verbose=true"
# Resolution change (restart — pipeline reinit)
curl "http://<ip>/api/v1/set?video0.size=1280x720"Responses:
// Single field
{"ok": true, "data": {"field": "video0.bitrate", "value": 4096}}
// Multi-set
{"ok": true, "data": {"applied": [
{"field": "video0.bitrate", "value": 4096},
{"field": "system.verbose", "value": true}
]}}
// Restart field
{"ok": true, "data": {"field": "video0.size", "value": "1280x720", "reinit_pending": true}}Multi-set limitation
Multi-set is supported only for live fields. If any restart field is present, the whole request is rejected. Send restart changes one at a time (the daemon respawns between them — wait for it to come back).
HTTP 409 — validation error
If a value is invalid (e.g. a non-existent AWB mode or a field that does not exist), the API returns HTTP 409 Conflict instead of 200.
GET /api/v1/restart
A full pipeline reinit. Reloads /etc/waybeam.json and restarts the camera pipeline without terminating the process.
curl http://<ip>/api/v1/restartEncoder control
GET /request/idr
Request an IDR keyframe from the encoder:
curl http://<ip>/request/idrWhen to request an IDR
- After a new viewer connects
- After packet loss on the radio link
- When video artifacts appear
GET /api/v1/idr/stats
Per-channel IDR rate-limit counters: how many requests were honored vs. coalesced.
GET /api/v1/awb
Current AWB (auto white balance) state from the ISP.
curl http://<ip>/api/v1/idr/stats
curl http://<ip>/api/v1/awbStream observability
GET /api/v1/transport/status
State of the active video transport (UDP / Unix / SHM): buffer fill percentage, backpressure flag, lifetime drop counters.
GET /api/v1/audio/status
A snapshot of the audio pipeline: whether the library is loaded, capture state, codec, sample rate, channels, Opus initialization.
curl http://<ip>/api/v1/transport/status
curl http://<ip>/api/v1/audio/statusAttitude: artificial horizon from the IMU
Since v0.39–v0.40 Waybeam estimates the camera orientation (roll/pitch/yaw) from the BMI270 gyro — for an artificial horizon in the ground-station HUD. Requires imu.enabled=true and attitude.enabled=true (both restart).
GET /api/v1/attitude — a live snapshot of the angles (camera frame, mount trims applied):
curl http://<ip>/api/v1/attitudeGET /api/v1/attitude/calibrate_level — one-command level calibration: hold the camera still and level for ~1.5 s — the service averages the accelerometer, solves the trims exactly (attitude.trimRollDeg / trimPitchDeg), persists them and restarts the pipeline:
curl http://<ip>/api/v1/attitude/calibrate_levelPossible errors: 409 — the IMU is disabled or the camera is moving; 501 — on Maruko (the estimator is not wired to that backend's IMU path yet).
attitude section fields (all restart):
| Field | Description |
|---|---|
enabled | Enable the horizon estimator |
mountDeg | Camera mount angle around the lens axis: 0 / 90 / 180 / 270 |
invertRoll / invertPitch | Sign inversion |
axisFwd / axisDown | Sensor axis remap for boards mounted in a non-standard orientation (any of the 24 axis-aligned ones); defaults +x / +z |
trimRollDeg / trimPitchDeg | Level trims — written by the calibration |
ATTITUDE in the RTP sidecar
With the sidecar enabled (outgoing.sidecarPort), the per-frame telemetry gains a 12-byte ATTITUDE trailer (roll/pitch/yaw in 0.1° steps, status, IMU sample age) — for HUDs. The sidecar itself is multi-subscriber since v0.39: up to 4 receivers at once (5 s TTL per slot), so a HUD subscription no longer hijacks the telemetry from the wfb controller. The trailer is Star6E-only. Since v0.40.1 the estimator drops corrupt IMU samples (NaN/Inf) and never publishes a "frozen" horizon.
In the WebUI, the Attitude section shows the angles live (1 Hz poll) and offers a Capture level trims button — it calibrates, updates the fields and restarts the pipeline.
Discovery (mDNS)
The camera announces itself on the local network as a _waybeam-venc._tcp.local service: a unique waybeam-<suffix>.local name (the suffix is the tail of the SigmaStar chip die ID) and, by default, the short waybeam.local. The discovery section fields are exposed via the API and the WebUI (Discovery section):
# Announcement state
curl "http://<ip>/api/v1/get?discovery.enabled"
# A custom name instead of waybeam-<suffix>
curl "http://<ip>/api/v1/set?discovery.name=my-drone"
# Disable the short waybeam.local name (useful with several cameras on the network)
curl "http://<ip>/api/v1/set?discovery.bareAlias=false"The full 12-hex serial (die ID) is at GET /api/v1/config → data.device.serial (read-only). Short-name conflicts between several cameras are resolved automatically per RFC 6762 (IP tiebreak).
Snapshot (JPEG)
GET /api/v1/snapshot.jpg
One JPEG frame from a dedicated MJPEG VENC channel (tapped off the same port as the main H.265 stream). No parameters; quality defaults to 80, resolution matches the main stream.
curl -o snapshot.jpg http://<ip>/api/v1/snapshot.jpgThe response is Content-Type: image/jpeg. Possible errors: 503 snapshot_disabled (pipeline not up yet), 504 snapshot_timeout (no frame within 1500 ms), 500 snapshot_failed.
Snapshot settings
snapshot.quality is live (instant, no reinit). The snapshot.channel, snapshot.width, snapshot.height fields are restart (baked at MI_VENC_CreateChn). width=0/height=0 means "match the main stream".
SD card recording
GET /api/v1/record/start
Start recording. Uses the configured record.dir, or override with a parameter:
curl "http://<ip>/api/v1/record/start"
curl "http://<ip>/api/v1/record/start?dir=/mnt/mmcblk0p1"GET /api/v1/record/stop — stop recording.
GET /api/v1/record/status — recording status:
curl "http://<ip>/api/v1/record/status"{
"ok": true,
"data": {
"active": true,
"format": "ts",
"path": "/mnt/mmcblk0p1/rec_01h23m45s_abcd.ts",
"frames": 1500,
"bytes": 12345678,
"segments": 1,
"stop_reason": "none"
}
}Recording on Maruko
HTTP recording control (start/stop) works only on Star6E. On Maruko recording is config-only (record.enabled=true + record.mode=... in /etc/waybeam.json), and /api/v1/record/start|stop returns 501 not_implemented.
Dual-Stream (Gemini mode)
GET /api/v1/dual/status
Secondary VENC channel status (only dual / dual-stream modes):
curl "http://<ip>/api/v1/dual/status"{"ok": true, "data": {"active": true, "channel": 1, "bitrate": 20000, "fps": 120, "gop": 240}}Dual VENC not active
If the recording mode is not "dual" or "dual-stream", this endpoint returns HTTP 404.
GET /api/v1/dual/set?param=value
# Change the recording bitrate
curl "http://<ip>/api/v1/dual/set?bitrate=10000"
# Change the GOP (in seconds)
curl "http://<ip>/api/v1/dual/set?gop=1.0"GET /api/v1/dual/idr — IDR keyframe for the secondary channel.
Framing: stabilization and digital zoom
video0.framing is the single user-facing knob for the VPE crop. It is a named preset (restart-required); the crop fraction is derived from the preset (there is no separate zoomPct field).
framing | Effect | Resolution @1080p | Chips |
|---|---|---|---|
off | Full frame | 1920×1080 | both |
stab | Stabilization (centered 80% crop) | 1536×864 | both (Maruko since v0.35) |
stab-fill | Stabilization (floating image on a black border) | 1920×1080 | both (Maruko since v0.37) |
zoom-1.25x | Digital zoom 1.25× | 1536×864 | both |
zoom-1.50x | Digital zoom 1.50× | 1280×720 | both |
zoom-1.75x | Digital zoom 1.75× | 1088×608 | both |
zoom-2x | Digital zoom 2× | 960×528 | both |
zoom-3x | Digital zoom 3× | 640×352 | both |
zoom-4x | Digital zoom 4× | 480×256 | both |
Digital zoom shrinks both the crop window and the output resolution — no upscale, no extra link load. Panning inside the zoom is live, via video0.zoomX / video0.zoomY (∈ [0,1], center 0.5/0.5):
# Enable 3× zoom (restart)
curl "http://<ip>/api/v1/set?video0.framing=zoom-3x"
# Pan (live) — top-left corner / center
curl "http://<ip>/api/v1/set?video0.zoomX=0.0&video0.zoomY=0.0"
curl "http://<ip>/api/v1/set?video0.zoomX=0.5&video0.zoomY=0.5"Stabilization (stab / stab-fill) uses a Kalman trajectory filter and works on both chips since v0.35/v0.37. Fine-tuning (all restart; re-selecting the preset resets them to defaults, so set framing first, then the overrides):
| Field | Default | Description |
|---|---|---|
video0.stabAccuracy | auto | Motion-detector level: high / medium / low (quality ↔ CPU). auto = high on Star6E, low on the single-core Maruko (v0.36) |
video0.stabCropPct | 80 | Stabilization headroom (lower = bigger dead border, more motion absorbed) |
video0.stabKalmanQ | 0.03 | Pan response (0.001..1.0; higher = follows slow pans sooner) |
video0.stabKalmanR | 2.0 | The primary feel knob. Smoothness (0.1..50.0; higher = smoother but laggier) |
video0.pauseStab | — | live pause: glides the window/image back to center (stab/stab-fill only) |
Stabilization on Maruko — the CPU cost
The motion detector is SigmaStar's software NEON library, not a hardware block. On the single-core Maruko that is a noticeable CPU share (in stab-fill ≈29% of the core at 50 fps with stabAccuracy=low). Also, on Maruko stab-fill is incompatible with record.mode: "dual" — such a request is rejected.
For versions before v0.35/v0.37 — stabilization was Star6E-only
Before v0.35 stab, and before v0.37 stab-fill, worked only on Star6E: on Maruko the MI_IVE detector failed to initialize due to an incompatible vendor library, so the WebUI greyed out the stabilization controls and /set rejected the fields. Since v0.35 the Maruko tarball ships a compatible libmi_ive.so, and both presets are available on both chips.
# Enable stabilization (restart)
curl "http://<ip>/api/v1/set?video0.framing=stab"
# Pause stabilization live
curl "http://<ip>/api/v1/set?video0.pauseStab=1" # freeze (glide to center)
curl "http://<ip>/api/v1/set?video0.pauseStab=0" # resumeStabilization does not use the gyro
Stabilization works from in-frame motion analysis (Kalman) and does not use the IMU. The former BMI270 EIS (gyroglide) was removed in 0.8.0. Since v0.39 the gyro feeds a different feature — the attitude (horizon) estimator.
Resilience: packet-loss resilience
video0.resilience selects a resilience profile — intra-refresh (rolling GDR stripe), the SVC-T reference pyramid, and GOP length are all derived from the preset.
Changing resilience requires a REBOOT
Writing video0.resilience persists the value to /etc/waybeam.json and returns {"reboot_required": true}. The live pipeline keeps running the previous preset until the next camera start — the SigmaStar MI kernel module does not survive a live re-configure of these fields (on Star6E it causes a kernel panic, on Maruko it hangs the daemon). Hence the "cold-boot" model.
| Preset | intra-refresh | refPred | GOP | OSD-safe? |
|---|---|---|---|---|
off | off | off | user-set | yes |
rescue | off | off | 0.25 s (IDR-spam) | yes |
quality | off | off | 4.0 s | yes |
sprint | fast (150 ms) | off | 0.5 s | yes |
racing | fast (150 ms) | off | 2.0 s | yes |
endurance | balanced (500 ms) | off | 2.0 s | yes |
patrol | balanced (500 ms) | off | 4.0 s | yes |
rally | fast (150 ms) | base=1, enhance=1 | 2.0 s | no — "green smear" |
range | balanced (500 ms) | base=1, enhance=4 | 2.0 s | no — "green smear" |
fpv | robust (1000 ms) | base=1, enhance=4 | 2.0 s | no — "green smear" |
# FPV with an OSD overlay — fast stripe recovery, no SVC-T
curl "http://<ip>/api/v1/set?video0.resilience=racing"
# then reboot the camera to applyOSD and SVC-T
Presets with refPred (rally, range, fpv) can leave a persistent "green smear" over a static OSD until the next IDR. For flights with an OSD overlay use OSD-safe presets (racing, endurance, patrol). Budget +20–30% bitrate for presets with intra-refresh.
ISP Image Quality
GET /api/v1/iq — export all ISP parameters:
curl http://<ip>/api/v1/iq > my_tuning.jsonPOST /api/v1/iq/import — import (full or partial):
# Full import
curl -X POST -H "Content-Type: application/json" \
-d @my_tuning.json http://<ip>/api/v1/iq/import
# Partial import — specific parameters only
echo '{"lightness":{"value":75},"demosaic":{"fields":{"dir_thrd":30}}}' | \
curl -X POST -H "Content-Type: application/json" -d @- http://<ip>/api/v1/iq/importGET /api/v1/iq/set?param=value — change a single ISP parameter (dot-notation):
# Set a single field
curl "http://<ip>/api/v1/iq/set?colortrans.y_ofst=200"
# Set an array (comma-separated)
curl "http://<ip>/api/v1/iq/set?colortrans.matrix=23,45,9,1005,987,56,56,977,1015"Common scenario examples
Quick switch to 720p 90fps
curl "http://<ip>/api/v1/set?video0.size=1280x720"
# Wait for reinit...
curl "http://<ip>/api/v1/set?video0.fps=90"
curl "http://<ip>/api/v1/set?video0.bitrate=4096"Manual white balance (6500K)
curl "http://<ip>/api/v1/set?isp.awbMode=ct_manual"
curl "http://<ip>/api/v1/set?isp.awbCt=6500"Enable ROI encoding for FPV
curl "http://<ip>/api/v1/set?fpv.roiEnabled=true"
curl "http://<ip>/api/v1/set?fpv.roiQp=-18"
curl "http://<ip>/api/v1/set?fpv.roiSteps=2"Enable stabilization
# framing is a restart field; set it first, then fine-tune
curl "http://<ip>/api/v1/set?video0.framing=stab"
curl "http://<ip>/api/v1/set?video0.stabKalmanR=6"Recommended settings by scenario
FPV racing (minimal latency)
{
"video0": {"rcMode":"cbr", "fps":90, "size":"1280x720", "bitrate":6144, "gopSize":0.5, "resilience":"racing"},
"fpv": {"roiEnabled":true, "roiQp":-12, "roiSteps":2, "roiCenter":0.35},
"outgoing": {"streamMode":"rtp", "server":"unix://wfb_tx"}
}FPV freestyle (quality + recording)
{
"video0": {"rcMode":"cbr", "fps":60, "size":"1920x1080", "bitrate":8192, "gopSize":1.0},
"fpv": {"roiEnabled":true, "roiQp":-18, "roiSteps":3, "roiCenter":0.4},
"record": {"enabled":true, "mode":"dual", "bitrate":20000, "fps":120},
"outgoing": {"streamMode":"rtp", "server":"unix://wfb_tx"}
}Long range
{
"video0": {"rcMode":"cbr", "fps":30, "size":"1280x720", "bitrate":3072, "gopSize":2.0, "resilience":"range"},
"fpv": {"roiEnabled":false},
"outgoing": {"streamMode":"rtp", "server":"unix://wfb_tx"}
}resilience in the config
resilience is applied at cold boot, so in the config file it takes effect immediately. Via the API it requires a camera reboot.
Next steps
- Waybeam overview — the full feature list
- Install on the camera — initial installation
- WFB-ng integration — setting up the WFB link

