Complete reverse-engineering of the DWARFLAB Android app (v3.4.0) protocol
and implementation of a working CLI tool to control DWARF II telescopes.
Analysis (from APK decompilation with jadx):
- Extracted 17 protobuf definitions (382 messages) from embedded descriptors
- Mapped all 323 WebSocket command IDs across 16 modules
- Documented the full protocol: BLE discovery, WebSocket control (port 9900),
RTSP preview, WsPacket envelope (proto v2.3)
- Documented the Android UI structure (screens, navigation, shooting modes)
- Key discovery: telescope responds with type=3 (reply), not type=1 (response),
and several commands are fire-and-forget (RGB, camera open/close)
dwarfctl Go client:
- Protobuf bindings generated from extracted .proto files (397 messages)
- WebSocket transport layer with request-response matching and notification fan-out
- Typed API covering cameras, motors, astrophotography, focus, tracking, system, power
- Cobra CLI with 30+ subcommands and --debug traffic logging
- 57 unit tests (transport round-trip, command routing, proto encoding)
- Validated on real hardware: state, photo, motor slew (all directions/speeds),
focus, RGB, time/location sync all confirmed working
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
81 lines
1.3 KiB
Protocol Buffer
81 lines
1.3 KiB
Protocol Buffer
syntax = "proto3";
|
|
option go_package = "github.com/antitbone/dwarfctl/proto;pb";
|
|
message ReqMotorServiceJoystick {
|
|
double vector_angle = 1;
|
|
double vector_length = 2;
|
|
}
|
|
|
|
message ReqMotorServiceJoystickFixedAngle {
|
|
double vector_angle = 1;
|
|
double vector_length = 2;
|
|
}
|
|
|
|
message ReqMotorServiceJoystickStop {
|
|
}
|
|
|
|
message ReqMotorRun {
|
|
int32 id = 1;
|
|
double speed = 2;
|
|
bool direction = 3;
|
|
int32 speed_ramping = 4;
|
|
int32 resolution_level = 5;
|
|
}
|
|
|
|
message ReqMotorRunInPulse {
|
|
int32 id = 1;
|
|
int32 frequency = 2;
|
|
bool direction = 3;
|
|
int32 speed_ramping = 4;
|
|
int32 resolution = 5;
|
|
int32 pulse = 6;
|
|
bool mode = 7;
|
|
}
|
|
|
|
message ReqMotorRunTo {
|
|
int32 id = 1;
|
|
double end_position = 2;
|
|
double speed = 3;
|
|
int32 speed_ramping = 4;
|
|
int32 resolution_level = 5;
|
|
}
|
|
|
|
message ReqMotorGetPosition {
|
|
int32 id = 1;
|
|
}
|
|
|
|
message ReqMotorStop {
|
|
int32 id = 1;
|
|
}
|
|
|
|
message ReqMotorReset {
|
|
int32 id = 1;
|
|
bool direction = 2;
|
|
}
|
|
|
|
message ReqMotorChangeSpeed {
|
|
int32 id = 1;
|
|
double speed = 2;
|
|
}
|
|
|
|
message ReqMotorChangeDirection {
|
|
int32 id = 1;
|
|
bool direction = 2;
|
|
}
|
|
|
|
message ResMotor {
|
|
int32 id = 1;
|
|
int32 code = 2;
|
|
}
|
|
|
|
message ResMotorPosition {
|
|
int32 id = 1;
|
|
int32 code = 2;
|
|
double position = 3;
|
|
}
|
|
|
|
message ReqDualCameraLinkage {
|
|
int32 x = 1;
|
|
int32 y = 2;
|
|
}
|
|
|