-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
150 lines (117 loc) · 4.99 KB
/
index.html
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
144
145
146
147
148
149
150
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Meshtastic Flasher BLE</title>
</head>
<body>
<div>
<div style="margin-bottom:1rem;">
<div>1. Select firmware file for your device.</div>
<input id="firmware_file" type="file"/>
</div>
<div style="margin-bottom:1rem;">
<div>2. Put device into OTA Update mode via the iOS app (will support here soon).</div>
</div>
<div style="margin-bottom:1rem;">
<div>3. Flash firmware via BLE.</div>
<button onclick="flashFirmware()">Flash</button>
</div>
<div>
<div>Progress Log</div>
<div id="progress">...</div>
</div>
</div>
<script>
// BLE UUIDs
const meshtasticOTAServiceId = "4FAFC201-1FB5-459E-8FCC-C5C9C331914B".toLowerCase();
const statusCharacteristicId = "62EC0272-3EC5-11EB-B378-0242AC130003".toLowerCase(); // ESP32 pTxCharacteristic
const otaCharacteristicId = "62EC0272-3EC5-11EB-B378-0242AC130005".toLowerCase(); // ESP32 pOtaCharacteristic
async function flashFirmware() {
try {
// ensure browser supports bluetooth
if(!navigator.bluetooth || !navigator.bluetooth.requestDevice){
alert("Your device does not support the Web Bluetooth API. Try using a different device.");
return;
}
// find progress element
const progressElement = document.getElementById("progress");
// ensure firmware file selected
const firmwareFileInput = document.getElementById("firmware_file");
const firmwareFile = firmwareFileInput.files[0];
if(!firmwareFile){
alert("Select a Firmware File...")
return;
}
// get firmware from selected file
const firmwareArrayBuffer = await firmwareFile.arrayBuffer();
const firmwareBytes = new Uint8Array(firmwareArrayBuffer);
// break firmware in to chunks
const chunks = [];
const chunkSize = 510; // todo: determine best size dynamically
var bytesWritten = 0;
for (let i = 0; i < firmwareBytes.length; i += chunkSize) {
// get chunk
const chunk = firmwareBytes.slice(i, i + chunkSize);
if(chunk.length === 0){
break;
}
chunks.push(chunk);
}
// function to send the next chunk
var currentChunkIndex = 0;
async function sendChunk() {
const chunk = chunks[currentChunkIndex];
if(!chunk || chunk.length === 0){
console.log("no more to send!");
return;
}
await otaCharacteristic.writeValue(chunk);
bytesWritten += chunk.length;
// determine progress
const chunksCompleted = currentChunkIndex + 1;
const percentage = (chunksCompleted / chunks.length) * 100;
const log = `Progress: ${parseFloat(percentage).toFixed(2)}% ${formatBytes(bytesWritten)}/${formatBytes(firmwareBytes.byteLength)} (${chunksCompleted}/${chunks.length}) wrote ${chunk.length} bytes`;
// show progress
console.log(log);
progressElement.textContent = log;
}
// request device
const device = await navigator.bluetooth.requestDevice({
filters: [{
services: [
meshtasticOTAServiceId,
],
}],
});
// connect to device
const server = await device.gatt.connect();
// get service and characteristics
const service = await server.getPrimaryService(meshtasticOTAServiceId);
const otaCharacteristic = await service.getCharacteristic(otaCharacteristicId);
const statusCharacteristic = await service.getCharacteristic(statusCharacteristicId);
// device will notify us when it wants the next chunk of data to be sent
statusCharacteristic.addEventListener('characteristicvaluechanged', async function() {
currentChunkIndex++;
await sendChunk();
});
// listen for notifications from device
await statusCharacteristic.startNotifications();
// send first chunk of firmware to device
await sendChunk();
} catch(e) {
alert(e.message || e);
console.error(e);
}
}
function formatBytes(bytes, decimals = 2) {
if (!+bytes) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
}
</script>
</body>
</html>