Files
dwarf_protocol/03_websocket_protocol.md
Jacquin Antoine be87e8a4ca Add complete DWARFLAB protocol documentation
- Architecture overview, BLE and WebSocket protocols
- All 19 modules with command IDs validated against smali source
- BLE packet format with CRC-16/MODBUS pseudocode
- Complete WsCmd index (~300 commands)
- Connection flow with pseudocode examples
- Methodology section explaining APK decompilation process
2026-06-13 00:21:19 +02:00

5.0 KiB

WebSocket Protocol

Connection

After BLE authentication, the app connects via WebSocket for all telescope control.

URL: ws://<device_ip>:9900/?client_id=<client_id>
Port: 9900 (primary), 4409/4410 (secondary)
Scheme: ws:// (unencrypted, NOT wss://)

The device IP is obtained from ResGetconfig.ip (if already on WiFi) or ResSta.ip (after STA configuration).

The client_id is stored in MMKV under key "data_default_client_id", or generated if not present.

Default device IP: 192.168.88.1

WsPacket Format

Every WebSocket message is a serialized BaseProto.WsPacket protobuf:

message WsPacket {
    int32  majorVersion = 1;  // Always 2
    int32  minorVersion = 2;  // Always 3
    int32  deviceId     = 3;  // Target device ID (see DeviceType)
    int32  moduleId     = 4;  // Module ID (0-18, see module table)
    int32  cmd          = 5;  // Command ID (see WsCmd index)
    int32  type         = 6;  // Message type: 0=request, 1=response, 2=notification, 3=reply
    bytes  data         = 7;  // Protobuf-encoded payload for the specific module
    string clientId     = 8;  // App client identifier
}

Pseudocode: Sending a Command

import protobuf

def send_command(websocket, device_id, module_id, cmd, payload_bytes, client_id):
    """Send a command via WebSocket."""
    packet = WsPacket(
        majorVersion=2,
        minorVersion=3,
        deviceId=device_id,
        moduleId=module_id,
        cmd=cmd,
        type=0,  # request
        data=payload_bytes,
        clientId=client_id
    )
    websocket.send(packet.SerializeToString())

def send_track_start(websocket, device_id, client_id, x, y, w, h, cam_id):
    """Example: Start tracking."""
    track_req = TrackProto.ReqStartTrack(x=x, y=y, w=w, h=h, camId=cam_id)
    send_command(websocket, device_id, module_id=7, cmd=14800,
                 payload_bytes=track_req.SerializeToString(), client_id=client_id)

Message Types

type Name Description
0 request App → Device command
1 response Device → App response to a request
2 notification Device → App spontaneous notification
3 reply Device → App acknowledgement

Keepalive: DwarfPing / DwarfEcho

After WebSocket connection, the app periodically sends DwarfPing and the device responds with DwarfEcho.

message DwarfPing {
    int32  vocaltype  = 1;  // VT_PING = 1
    int64  timestamp  = 2;  // Unix timestamp
    bytes  magic      = 3;  // Session magic bytes
    repeated DwarfPing vocals = 4;
    repeated NifAP mutes = 5;
}

message DwarfEcho {
    int32  vocaltype  = 1;   // VT_ECHO = 2
    int64  timestamp  = 2;   // Unix timestamp
    bytes  magic      = 3;   // Session magic bytes
    int64  tsPing     = 4;   // Echo of the ping timestamp
    bytes  macAddress = 5;   // Device MAC address
    BleProto model    = 6;   // Device model info
    string sn         = 7;   // Serial number
    string name       = 8;   // Device name
    string psw        = 9;   // Password
    string fwVersion  = 10;  // Firmware version
    string wsScheme   = 11;  // WebSocket scheme ("ws")
    int32  session    = 12;  // Session ID
    BleProto ap        = 13;  // AP info
    BleProto sta       = 14;  // STA info
}

Note

: DwarfEcho contains complete device info (firmware, MAC, SSID, IP, etc.).

Response Routing

The client uses WsRequestHandle to dispatch responses:

  1. Parse WsPacket from received bytes
  2. Extract cmd (command ID)
  3. Look up registered handlers in a HashMap<Integer, List> for this cmd
  4. Execute callbacks with WsPacket.getData() (deserialized payload)

Typical Exchange

App                                    Device
 │                                        │
 │  WsPacket(type=0, moduleId=1,          │
 │    cmd=10000, data=ReqOpenCamera)       │
 │ ─────────────────────────────────────► │
 │                                        │
 │  WsPacket(type=1, cmd=10000,           │
 │    data=ComResponse{code=0})            │
 │ ◄───────────────────────────────────── │
 │                                        │
 │  WsPacket(type=0, moduleId=7,          │
 │    cmd=14800, data=ReqStartTrack)       │
 │ ─────────────────────────────────────► │
 │                                        │
 │  WsPacket(type=2, cmd=15200,            │
 │    data=NotifyProto{...})               │
 │ ◄───────────────────────────────────── │
 │  (spontaneous notification)             │

Protocol Version

Field Value Description
WS_MAJOR_VERSION_NUMBER 2 Current major version
WS_MINOR_VERSION_NUMBER 3 Current minor version