跪拜 Guibai
← Back to the summary

Two BLE Provisioning Protocols for Android IoT Devices, Side by Side

Android IoT Bluetooth Network Configuration Simple Encapsulation

1. Bluetooth Network Configuration Flowchart:

image.png

2. Bluetooth Network Configuration Sequence Diagram:

image.png

IoT BLE Network Configuration Process:

3. IoT Configuration Management Overall Architecture:

image.png

4. Implementation Steps:

User clicks start configuration   
     ↓
Enter IoT BLE Flow (pause Tuya old BLE)   
     ↓
BLE connects to device   
     ↓
Send wifisetup (78 bytes)   
     ↓
Receive notify ACK = 0x04   
     ↓
[Bind Phase begins]   
     ↓
Call preAdd → get add_token   
     ↓
Send bindinfo (contains add_token / uid / deviceId / homeId)   
     ↓
Receive notify ACK = 0x10 / 0x11   
     ↓
Start pollAddResult(add_token)   
     ↓
Polling success → return device_id   
     ↓
UI displays "Added successfully"

5. IoT Device Search:

5.1 Device Search Flowchart:

image.png

5.2 Core Communication Flowchart:

CoolKit Device Communication Flow

flowchart TD
A([connectGatt<br/>ssid, psd, deviceId]) --> B[HTTP get secretKey<br/>GET /v2/device/fdbaseinfo]
B --> C[connectGatt<br/>autoConnect=false<br/>TRANSPORT_LE + PHY_LE_2M]
C --> D{onConnectionStateChange}
D -->|STATE_CONNECTED| E[gatt.discoverServices]
D -->|STATE_DISCONNECTED| F{reConnectTime < 3<br/>and not successful?}
F -->|Yes| C
F -->|No| G[bleConnStatus = false<br/>end]
E --> H[onServicesDiscovered<br/>requestMtu512]
H --> I{Iterate characteristics}
I -->|UUID_WRITE<br/>0000bbb0| J[Cache writeCharacteristic<br/>Delay 2s write data]
I -->|UUID_READ<br/>0000bbb1| K[Cache readCharacteristic<br/>Delay 1s enable Notify]

J --> L[Write AES encrypted UUID<br/>handshake request]
K --> M[Wait for device report]

M -->|0x01| N[Parse deviceCode<br/>Reply AESUUID_deviceCode]
N --> M
M -->|0x03| O{data[4] == 0x00?}
O -->|Yes| P[Reply {4,0,3,0,1,0,5}<br/>Start WiFi scan polling]
O -->|No| Q[Handshake failed<br/>bleConnStatus=false]

P --> R[Send scan command every 5s<br/>until 0x06 received]
M -->|0x05| S[Parse SSID/BSSID/RSSI<br/>match target WiFi]
S --> T[Build WiFi connection request packet<br/>Password AES encrypted]
M -->|0x06| U[Scan ended<br/>Send connWifiData]

M -->|0x08| V{WiFi connection status}
V -->|0x00| W[Connection successful]
V -->|0x02| X[Password error<br/>bleConnStatus=false]
V -->|Other| Y[Connection failed<br/>bleConnStatus=false]

W --> Z[0x0A Report device info<br/>deviceId / apikey / chipId]
Z --> AA[addDevice register to CoolKit cloud]
AA --> AB[Send dispatch server address<br/>0x0B command]
AB --> AC[0x0C Configuration complete<br/>status=0x00 success]
AC --> AD[bleConnStatus = true<br/>close GATT]

style AA fill:#d4edda
style G fill:#f8d7da
style X fill:#f8d7da
style Y fill:#f8d7da

TanGe / IoT Device Communication Flow

image.png

5.3 GATT Services and Characteristics:

Device Type Direction UUID Property Description
CoolKit App → Device 0000bbb0-0000-1000-8000-00805f9b34fb Write Command delivery
CoolKit Device → App 0000bbb1-0000-1000-8000-00805f9b34fb Read / Notify Status and data reporting
TanGe/IoT App → Device 00009999-0000-1000-8000-00805f9b34fb Write WiFi packet / binding info write
TanGe/IoT Device → App 00008888-0000-1000-8000-00805f9b34fb Read / Notify ACK / JSON data reporting

5.5 Scan Broadcast Packet Parsing (CoolKit PN Value)

Offset (relative to AD Type segment) Length Content Byte Order
+2 ~ +3 2 bytes PN Value Little-endian (needs reverse read)
+4 1 byte Reserved value -
+5 1 byte Product type -

5.5 CoolKit Device Communication Protocol

General Packet Structure (both device reporting and App delivery follow this):

[0]      cmd_id      // Command identifier
[1]      0x00        // Reserved
[2]      len_low     // Subsequent data length (low byte)
[3]      len_high    // Subsequent data length (high byte)
[4..N]   payload     // Data payload

Command Set

cmd_id Direction Command Payload Description
0x01 Device→App Handshake Response [len(2B), AES(deviceCode)]
0x03 Device→App Handshake Confirmation [status: 0x00=success]
0x05 Device→App WiFi Scan Result [ssidLen, ssid..., bssid[6], rssi, isConnect]
0x06 Device→App WiFi Scan End No payload
0x08 Device→App WiFi Connection Status [status] 0success 1failure 2password error 3no AP 4timeout
0x0A Device→App Device Identity Info [deviceIdLen, deviceId..., apikeyLen, apikey..., chipIdLen, chipId...]
0x0C Device→App Configuration Complete Status [status: 0x00=success]
0x0E Device→App IP Acquisition Result [status: 0x00=success]

5.6 Key App Delivery Command Examples

Scenario Byte Stream Description
Initial Handshake {0,0,(byte)appCodeLen,0} + AES(UUID) UUID is randomly generated, AES encrypted
Handshake Reply {2,0,(byte)secretDataLen,0} + AES(UUID_deviceCode) Reply after device returns deviceCode
Trigger WiFi Scan {4,0,3,0,1,0,5} Fixed command, sent every 5s polling
WiFi Connection Request {7,0,dataLen,0, bssid[6], isConnect, timeout, pwdLen} + AES(pwd) Sent after matching target SSID
Send Dispatch Address {11,0,(byte)(urlLen+2),0,0,(byte)urlLen} + urlBytes Sent after CoolKit cloud registration succeeds

AES Encryption Parameters

Parameter Value
Algorithm AES/CBC/PKCS7Padding
Key secretKey (Hex decoded to 16 bytes)
IV 1111111111111111 (16 bytes ASCII) This is just an example
Base64 Base64.NO_WRAP

5.7 TanGe / IoT Device Communication Protocol

78-byte WiFi Configuration Packet (App → Device)

Written via 00009999 characteristic, fixed 78 bytes:

Byte Offset Length Content Padding
0 1 0x01 (Frame header) -
1 ~ 12 12 "wifisetup" (UTF-8) Pad with 0x00
13 ~ 44 32 SSID (UTF-8) Pad with 0x00
45 ~ 76 32 Password (UTF-8) Pad with 0x00
77 1 0x04 (Frame tail) -

Device Response (Device → App, via 8888 Notify)

Data Format Content App Handling
Single byte 0x01 Same format as sent packet Ignore
Single byte 0x02 WiFi connection failed bleConnStatus = false
Single byte 0x03 WiFi password error bleConnStatus = false
JSON string Device info (DeviceInfoTanGeBean) Parse and report to cloud, close GATT, mark success

6. Key Mechanism Description:

Mechanism Implementation Details
Connection Retry Non-TanGe devices automatically reconnect on disconnect, up to 3 times (reConnectTime count)
TanGe Timeout Independent 3-minute (180s) timeout timer, if not successful on timeout then bleConnStatus = false
Total Timeout 180-second countdownTask heartbeat timeout, forces end of configuration
WiFi Scan Polling Every 5 seconds via scanWifiTask sends a 0x05 scan trigger command, until 0x06 scan end is received
Resource Cleanup resetData() unified release: stop scanning, close GATT, clear device list, cancel all Disposables, reset status bits
Data Write All sent data uniformly goes through setWriteCharacteristicData(byte[]), forced WRITE_TYPE_NO_RESPONSE

7. IoT Device Binding:

7.1 Binding Success:

/**
 * Binding success
 */
private void showBindSuccess() {
        closeCountDown(); // Immediately stop countdown
        mDataBinding.ivFaild.setVisibility(View.GONE);
        mDataBinding.tvProgress.setVisibility(View.VISIBLE);
        mDataBinding.vProgress.setVisibility(View.VISIBLE);
        mDataBinding.btnProblem.setVisibility(View.GONE);
        mDataBinding.btnRebind.setVisibility(View.GONE);
        mDataBinding.tvBindingStatus.setText(UIUtils.getString(R.string.ty_bind_email_success));
        mDataBinding.tvBindingDesc.setText("IoT device binding successful");
        mDataBinding.titleview.setTitle(UIUtils.getString(R.string.ty_bind_email_success));
        mDataBinding.titleview.showLeftImage(false);
    }
    
/**
 * Go to next step, display binding success interface then jump to success interface
 */
public void gotoNext() {
    if (mData != null) {
        SharedPrefs.getInstance().setBindDevicePic(mData.getPic());
        DeviceBindSuccessActivity.startActivity(mContext, mDevId, mData.getHome_device_id(), mProductId);
    } else {
        LogUploadUtils.d("bind device viewModel gotoNext mData is null");
        bindResult.setValue(false);
    }
}

7.2 Binding Failure:

/**
 * Binding failure UI
 */
private void showBindFailed() {
        if (isBindSuccess) return;
        mDownCount = 0;
        setClickText();
        mDataBinding.titleview.setTitle(UIUtils.getString(R.string.text_bind_faild));
        if (TextUtils.isEmpty(mRobotViewModel.bindErrorMsg)) {
            mDataBinding.tvErrorBind.setVisibility(View.GONE);
        } else {
            mDataBinding.tvErrorBind.setText(mRobotViewModel.bindErrorMsg);
            mDataBinding.tvErrorBind.setVisibility(View.VISIBLE);
        }
        mDataBinding.tvErrorBind.setVisibility(View.VISIBLE);
        mDataBinding.ivFaild.setVisibility(View.VISIBLE);
        mDataBinding.tvProgress.setVisibility(View.INVISIBLE);
        mDataBinding.vProgress.setVisibility(View.INVISIBLE);
        mDataBinding.btnProblem.setVisibility(View.VISIBLE);
        mDataBinding.btnRebind.setVisibility(View.VISIBLE);
        mDataBinding.llFailTipsNew.setVisibility(View.VISIBLE);
        mDataBinding.tvBindingStatus.setVisibility(View.GONE);
        mDataBinding.tvBindingDesc.setVisibility(View.GONE);
        mDataBinding.titleview.showLeftImage(false);
        mDataBinding.btnRebind.setOnClickListener(v -> {
            mRobotViewModel.onDestroyed();
            ProductListActivity3.startActivity(mContext, false, false);
        });
    }

8. Summary