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
This commit is contained in:
209
13_connection_flow.md
Normal file
209
13_connection_flow.md
Normal file
@ -0,0 +1,209 @@
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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)
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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 |
|
||||
Reference in New Issue
Block a user