After hitting the mechanical limit, the firmware locks the altitude axis
(rejects all slew commands on that axis). The azimuth axis continues to
work. Neither motor reset (causes reboot) nor power cycle unlocks it —
only a full astro calibration (which includes motor homing) restores
absolute positioning. This matches the NEED_RESET (-14520) error code.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
dwarfctl — Open-source DWARF telescope control CLI
A Go command-line tool for controlling DWARF II (and upcoming DWARF III) smart telescopes over Wi-Fi via their WebSocket API.
Built from reverse-engineered protocol specs (see ../analysis/).
Quick start
make build
./dwarfctl --ip 192.168.1.100 state
Prerequisites
The telescope must be connected to your Wi-Fi (or your phone joined to the
telescope's AP hotspot). Once you can ping the telescope's IP, dwarfctl can
reach it.
Find the IP from:
- The official DWARFLAB app's Settings → My Device
- Your router's DHCP table (hostname like
DWARFxxxx) - The BLE handshake (see
../analysis/API_REFERENCE.md)
Usage examples
# Device state
dwarfctl --ip 192.168.1.100 state
# Camera
dwarfctl camera open --cam tele
dwarfctl camera photo --cam tele
dwarfctl camera photo --cam wide --raw
dwarfctl camera burst start --cam tele
dwarfctl camera exp --cam tele 156 # 1/4s exposure
dwarfctl camera gain --cam tele 60
dwarfctl camera params --cam tele
# Motor / slew
dwarfctl motor slew 90 0.5 # angle 90°, half speed
dwarfctl motor stop 0 # stop RA motor
dwarfctl motor goto 0 45.0 5.0 # RA motor to 45° at speed 5
# Astrophotography
dwarfctl astro calibrate start
dwarfctl astro goto-dso 10.6847 41.2687 "M31 Andromeda"
dwarfctl astro goto-solar 3 # Earth = index 3
dwarfctl astro stack start
dwarfctl astro eq-solve start # polar alignment
# Focus
dwarfctl focus auto
dwarfctl focus step 1 # 1=out, 0=in
dwarfctl focus astro-af start
# Tracking
dwarfctl track start 640 360 100 100 0 # bbox at center of tele cam
dwarfctl track stop
# System
dwarfctl system sync-time
dwarfctl system set-location 48.8566 2.3522 35
# Power
dwarfctl power rgb-on
dwarfctl power rgb-off
dwarfctl power reboot
dwarfctl power off
# Monitor notifications (live status events)
dwarfctl monitor
Architecture
dwarfctl/
├── proto/dwarf.proto # unified proto3 definitions (397 messages)
├── proto/dwarf.pb.go # generated Go bindings (24948 lines)
├── internal/
│ ├── transport/client.go # WebSocket client + WsPacket envelope
│ └── api/
│ ├── commands.go # 323 command IDs + module routing
│ └── client.go # typed Telescope API (camera/motor/astro/...)
└── cmd/dwarfctl/main.go # cobra CLI
Protocol summary
| Layer | Transport | Port |
|---|---|---|
| Control | WebSocket (binary protobuf) | 9900 |
| Preview | RTSP (not implemented here) | 554 |
Every command is a serialized WsPacket{major=2, minor=3, device_id, module_id, cmd, type, data, client_id}. The module_id is derived from cmd
by range. See ../analysis/API_REFERENCE.md for details.
Regenerating protos
If the proto definitions change:
# From the APK analysis directory:
python3 ../analysis/extract_protos.py ../extracted/jadx/.../proto ../analysis/protos
# Merge into unified proto:
cd dwarfctl/proto
python3 merge.py . dwarf.proto
protoc --go_out=. --go_opt=paths=source_relative dwarf.proto
Roadmap
- BLE discovery + handshake (DwarfPing/DwarfEcho)
- RTSP preview viewer
- Interactive REPL mode
- Schedule plan management
- OTA firmware update
- Full Notify event parsing (typed)