Files
dwarf_protocol/13_connection_flow.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

7.1 KiB

Connection Flow

Complete Connection Sequence

Step 1: BLE Discovery          Step 4: WebSocket              Step 6: Commands
┌──────────────┐               ┌──────────────┐              ┌──────────────┐
│  SCAN for     │               │  Connect to   │              │  Send WsPacket│
│  "DWARF*"     │               │  ws://IP:9900 │              │  (moduleId+cmd)│
│  devices      │               │  ?client_id=  │              │              │
│  via BleManager│              │  <clientId>   │              │  Receive      │
└──────┬───────┘               └──────┬───────┘              │  WsPacket     │
       │                              │                       │  (response/   │
Step 2: GATT Connect           Step 5: Keepalive              │  notification)│
┌──────────────┐               ┌──────────────┐              └──────────────┘
│  Connect to   │               │  DwarfPing →  │
│  Service DAF2 │               │  ← DwarfEcho │
│  (UUID DAF2)  │               │  (periodic)   │
└──────┬───────┘               └──────────────┘
       │
Step 3: Auth
┌──────────────┐
│  Write        │
│  ReqGetconfig │
│  on DAF3      │
│  → Receive    │
│  ResGetconfig │
│  on DAF4      │
└──────────────┘

Step 1-3: BLE Phase

Pseudocode: BLE Authentication

# 1. Scan for DWARF devices
devices = ble_scan(filter_name="DWARF*")

# 2. Connect to GATT service
gatt = ble_connect(device, service_uuid="0000DAF2-0000-1000-8000-00805f9b34fb")

# 3. Authenticate
auth_request = BleProto.ReqGetconfig(
    cmd=1,  # GetWiFiConfig
    blePsd="DWARF_12345678",  # default password
    clientId="my-app-client-id"
)
ble_packet = build_ble_packet(cmd=1, data=auth_request.SerializeToString())
ble_write(characteristic=DAF3, data=ble_packet)

# 4. Receive response
response_data = ble_notify(characteristic=DAF4)
response_packet = parse_ble_packet(response_data)
config = BleProto.ResGetconfig()
config.ParseFromString(response_packet.dataPayload)

device_ip = config.ip  # e.g., "192.168.88.1"

Pseudocode: WiFi Configuration (STA mode)

# Configure device to connect to local WiFi
sta_request = BleProto.ReqSta(
    cmd=3,  # SetSTAMode
    autoStart=1,
    blePsd="DWARF_12345678",
    ssid="MyHomeWiFi",
    psd="home_password",
    clientId="my-app-client-id"
)
ble_packet = build_ble_packet(cmd=3, data=sta_request.SerializeToString())
ble_write(characteristic=DAF5, data=ble_packet)

# Receive STA response
sta_data = ble_notify(characteristic=DAF6)
sta_packet = parse_ble_packet(sta_data)
sta_response = BleProto.ResSta()
sta_response.ParseFromString(sta_packet.dataPayload)
device_ip = sta_response.ip  # IP for WebSocket

Step 4: WebSocket Phase

Pseudocode: WebSocket Connection

import websocket
import protobuf

# Connect to device WebSocket
ws = websocket.connect(f"ws://{device_ip}:9900/?client_id={client_id}")

# Start keepalive thread
def keepalive():
    while connected:
        ping = BleProto.DwarfPing(
            vocaltype=1,  # VT_PING
            timestamp=current_time_ms(),
            magic=session_magic_bytes
        )
        packet = WsPacket(
            majorVersion=2,
            minorVersion=3,
            deviceId=device_id,
            moduleId=0,  # NONE
            cmd=0,
            type=0,  # request
            data=ping.SerializeToString(),
            clientId=client_id
        )
        ws.send(packet.SerializeToString())
        sleep(5)  # 5-second interval

Pseudocode: Send Command

def send_command(ws, device_id, module_id, cmd, payload_bytes):
    """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
    )
    ws.send(packet.SerializeToString())

# Example: Start tracking
track_req = TrackProto.ReqStartTrack(x=500, y=300, w=100, h=100, camId=0)
send_command(ws, device_id=4, module_id=7, cmd=14800,
             payload_bytes=track_req.SerializeToString())

# Example: Open tele camera
camera_req = CameraProto.ReqOpenCamera(binning=False, rtspEncodeType=0)
send_command(ws, device_id=4, module_id=1, cmd=10000,
             payload_bytes=camera_req.SerializeToString())

BLE Packet Binary Format

Byte  Field               Size  Description
----  -----------------   ----  -----------
0     magic               1     0xAA (constant)
1     protocolVersion     1     Protocol version (typically 1)
2     cmdInstruction      1     BLE command (0-7)
3     packageSequence     1     Current packet number (0-based)
4     totalPackages       1     Total packets for this message
5-6   extendedProtocol    2     Extended protocol (big-endian, usually 0)
7-8   validDataLength     2     Length of protobuf payload (big-endian)
9..N  dataPayload         N     Protobuf-encoded BleProto message
N+1..N+2  crc             2     CRC-16/MODBUS (poly=0x8005, init=0xFFFF, reflected)
N+3   endMarker            1     0x0D (constant)

Total = validDataLength + 12 bytes

CRC-16/MODBUS Parameters

  • Polynomial: 0x8005 (reflected: 0xA001)
  • Initial value: 0xFFFF
  • Reflect input: Yes
  • Reflect output: Yes
  • XOR out: 0x0000
  • Computed over: header (9 bytes) + payload (N bytes), excluding CRC and end marker

WsPacket Binary Format

Field           Proto#  Type      Description
--------------   ------  ----      -----------
majorVersion     1       int32     Always 2
minorVersion     2       int32     Always 3
deviceId         3       int32     Target device (0-6, see DeviceType)
moduleId         4       int32     Module (0-18, see module table)
cmd              5       int32     Command ID (see WsCmd index)
type             6       int32     0=request, 1=response, 2=notification, 3=reply
data             7       bytes     Protobuf-encoded module-specific payload
clientId         8       string   App client identifier

Serialized using standard protobuf proto3 encoding.

Device Models

Model deviceId Codename Max Name Special
Unknown 0
DWARF 2 1 21
DWARF 3 2 19
(gap) 3 unused
DWARF mini 4 Bilbo 15 No Panorama Create, indexBilbo.html
DWARF 4 5 19
DWARF DRAGON 6

Ports & Endpoints

Service URL/Port Note
WebSocket ws://<ip>:9900/?client_id=<id> Primary control channel
WebSocket (alt) Ports 4409 (0x1139), 4410 (0x113A) Secondary ports
Firebase dwarflab-a7062.appspot.com Cloud backend
S3 dwarf-bucket-02-1251529954 OTA storage