1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
<html>
<head>
<title>Setup AIRMX device</title>
</head>
<body>
<h1>Setup your AIRMX device</h1>
<div>
<button id="connect" type="button">
Connect
</button>
</div>
<script>
const DEVICE_NAME = 'AIRMX Pro'
const MAIN_SERVICE_UUID = '22210000-554a-4546-5542-46534450464d'
const WRITE_CHAR_UUID = '22210001-554a-4546-5542-46534450464d'
const NOTIFY_CHAR_UUID = '22210002-554a-4546-5542-46534450464d'
async function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
async function connectToDevice() {
const device = await navigator.bluetooth.requestDevice({
filters: [
{ name: DEVICE_NAME }
],
optionalServices: [MAIN_SERVICE_UUID]
})
const server = await device.gatt.connect()
const service = await server.getPrimaryService(MAIN_SERVICE_UUID)
const writeChar = await service.getCharacteristic(WRITE_CHAR_UUID)
const notifyChar = await service.getCharacteristic(NOTIFY_CHAR_UUID)
// 1. Enable notifications
await notifyChar.startNotifications()
notifyChar.addEventListener('characteristicvaluechanged', handleDeviceResponse)
await delay(500)
// 2. Send the first bind command
await sendBindCommand(writeChar)
await delay(500)
// 3. Send Wi-Fi credentials
const ssid = '<yourssid>'
const password = '<yourpassword>'
await sendWifiCredentialsCommand(writeChar, ssid, password)
await delay(500)
}
function handleDeviceResponse(event) {
const value = event.target.value
const receivedBytes = []
for (let i = 0; i < value.byteLength; i++) {
receivedBytes.push(value.getUint8(i).toString(16).padStart(2, '0'))
}
console.log(`Received data from device: ${receivedBytes.join(' ')}`)
}
async function sendBindCommand(writeChar) {
const handshakePacket = new Uint8Array([
// --- 4-byte Header ---
0x01, // Sequence Number: 1
0x11, // Packet Info: (Packet 1 of 1)
0x00, // Command ID: 11 (Big Endian Short, MSB)
0x0B, // Command ID: 11 (Big Endian Short, LSB)
// --- 15-byte Payload ---
0x08, // Hardcoded first byte of payload
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Token (as 0L)
0x05, // Length of version string "1.0.0"
0x31, 0x2e, 0x30, 0x2e, 0x30 // ASCII for "1.0.0"
])
await sendRawData(writeChar, handshakePacket)
}
async function sendWifiCredentialsCommand(writeChar, ssid, password) {
const encoder = new TextEncoder()
const ssidBytes = encoder.encode(ssid)
const passwordBytes = encoder.encode(password)
const ssidLenSize = 1
const ssidSize = ssidBytes.length
const passwordLenSize = 1
const passwordSize = passwordBytes.length
const plaintextPayload = new Uint8Array(ssidLenSize + ssidSize + passwordLenSize + passwordSize)
let offset = 0
plaintextPayload[offset] = ssidSize
offset++
plaintextPayload.set(ssidBytes, offset)
offset += ssidSize
plaintextPayload[offset] = passwordSize
offset++
plaintextPayload.set(passwordBytes, offset)
const packet1 = new Uint8Array([
// Header (4 bytes)
0x02, // Sequence Number: 2
0x12, // Packet Info: (Packet 1 of 2)
0x00, // Command ID: 21, Enc-Flag: 0 (Big Endian)
0x15,
...plaintextPayload.slice(0, 16)
])
const packet2 = new Uint8Array([
// Header (4 bytes)
0x03, // Sequence Number: 3
0x22, // Packet Info: (Packet 2 of 2)
0x00, // Command ID: 21, Enc-Flag: 0 (Big Endian)
0x15,
...plaintextPayload.slice(16)
])
console.log('Sending Wi-Fi credentials packet 1/2...')
await sendRawData(writeChar, packet1)
await delay(100)
console.log('Sending Wi-Fi credentials packet 2/2...')
await sendRawData(writeChar, packet2)
}
async function sendRawData(characteristic, data) {
const chunkSize = 20
for (let i = 0; i < data.length; i += chunkSize) {
const chunk = data.slice(i, i + chunkSize)
console.log(`Sending chunk: ${Array.from(chunk).map(b => b.toString(16).padStart(2, '0')).join(' ')}`)
await characteristic.writeValueWithResponse(chunk)
}
}
const connect = document.getElementById('connect')
connect.addEventListener('click', connectToDevice)
</script>
</body>
</html>
|