mirror of
https://github.com/thegecko/web-bluetooth-dfu.git
synced 2025-12-12 03:58:12 +08:00
Added support for buttonless devices and multiple firmware images ina package
This commit is contained in:
254
dist/secure-dfu.js
vendored
254
dist/secure-dfu.js
vendored
@@ -46,11 +46,13 @@
|
||||
const SERVICE_UUID = 0xFE59;
|
||||
const CONTROL_UUID = "8ec90001-f315-4f60-9fb8-838830daea50";
|
||||
const PACKET_UUID = "8ec90002-f315-4f60-9fb8-838830daea50";
|
||||
const BUTTON_UUID = "8ec90003-f315-4f60-9fb8-838830daea50";
|
||||
|
||||
const LITTLE_ENDIAN = true;
|
||||
const PACKET_SIZE = 20;
|
||||
|
||||
const OPERATIONS = {
|
||||
BUTTON_COMMAND: [0x01],
|
||||
CREATE_COMMAND: [0x01, 0x01],
|
||||
CREATE_DATA: [0x01, 0x02],
|
||||
RECEIPT_NOTIFICATIONS: [0x02],
|
||||
@@ -58,7 +60,7 @@
|
||||
EXECUTE: [0x04],
|
||||
SELECT_COMMAND: [0x06, 0x01],
|
||||
SELECT_DATA: [0x06, 0x02],
|
||||
RESPONSE: [0x60]
|
||||
RESPONSE: [0x60, 0x20]
|
||||
};
|
||||
|
||||
const RESPONSE = {
|
||||
@@ -95,10 +97,8 @@
|
||||
this.crc32 = crc32;
|
||||
this.events = {};
|
||||
this.notifyFns = {};
|
||||
this.connected = false;
|
||||
this.controlChar = null;
|
||||
this.packetChar = null;
|
||||
this.buffer = null;
|
||||
}
|
||||
|
||||
function createListenerFn(eventTypes) {
|
||||
@@ -129,65 +129,163 @@
|
||||
});
|
||||
};
|
||||
|
||||
secureDfu.prototype.requestDevice = function(filters) {
|
||||
if (!filters) {
|
||||
filters = [{
|
||||
services: [SERVICE_UUID]
|
||||
}];
|
||||
secureDfu.prototype.progress = function(bytes) {
|
||||
this.dispatchEvent({
|
||||
type: "progress",
|
||||
object: "unknown",
|
||||
totalBytes: 0,
|
||||
currentBytes: bytes
|
||||
});
|
||||
}
|
||||
|
||||
secureDfu.prototype.requestDevice = function(hiddenDfu, filters) {
|
||||
if (!hiddenDfu && !filters) {
|
||||
filters = [{ services: [SERVICE_UUID] }];
|
||||
}
|
||||
|
||||
return bluetooth.requestDevice({
|
||||
filters: filters,
|
||||
acceptAllDevices: !filters,
|
||||
optionalServices: [SERVICE_UUID]
|
||||
})
|
||||
.then(device => {
|
||||
if (hiddenDfu) {
|
||||
return this.setDfuMode(device);
|
||||
}
|
||||
return device;
|
||||
});
|
||||
};
|
||||
|
||||
secureDfu.prototype.connect = function(device) {
|
||||
let service = null;
|
||||
secureDfu.prototype.setDfuMode = function(device) {
|
||||
return this.gattConnect(device)
|
||||
.then(characteristics => {
|
||||
this.log(`found ${characteristics.length} characteristic(s)`);
|
||||
|
||||
device.addEventListener("gattserverdisconnected", event => {
|
||||
this.connected = false;
|
||||
this.controlChar = null;
|
||||
this.packetChar = null;
|
||||
this.buffer = null;
|
||||
this.log("disconnected");
|
||||
});
|
||||
let controlChar = characteristics.find(characteristic => {
|
||||
return (characteristic.uuid === CONTROL_UUID);
|
||||
});
|
||||
let packetChar = characteristics.find(characteristic => {
|
||||
return (characteristic.uuid === PACKET_UUID);
|
||||
});
|
||||
|
||||
return device.gatt.connect()
|
||||
.then(gattServer => {
|
||||
this.log("connected to gatt server");
|
||||
return gattServer.getPrimaryService(SERVICE_UUID);
|
||||
})
|
||||
.then(primaryService => {
|
||||
this.log("found DFU service");
|
||||
service = primaryService;
|
||||
return service.getCharacteristic(CONTROL_UUID);
|
||||
})
|
||||
.then(characteristic => {
|
||||
this.log("found control characteristic");
|
||||
if (!characteristic.properties.notify) {
|
||||
throw new Error("control characterisitc does not allow notifications");
|
||||
if (controlChar && packetChar) {
|
||||
return device;
|
||||
}
|
||||
this.controlChar = characteristic;
|
||||
return characteristic.startNotifications();
|
||||
|
||||
let buttonChar = characteristics.find(characteristic => {
|
||||
return (characteristic.uuid === BUTTON_UUID);
|
||||
});
|
||||
|
||||
if (!buttonChar) {
|
||||
throw new Error("Unsupported device");
|
||||
}
|
||||
|
||||
// Support buttonless devices
|
||||
this.log("found buttonless characteristic");
|
||||
if (!buttonChar.properties.notify && !buttonChar.properties.indicate) {
|
||||
throw new Error("Buttonless characteristic does not allow notifications");
|
||||
}
|
||||
|
||||
return buttonChar.startNotifications()
|
||||
.then(() => {
|
||||
this.log("enabled buttonless notifications");
|
||||
buttonChar.addEventListener("characteristicvaluechanged", this.handleNotification.bind(this));
|
||||
return this.sendOperation(buttonChar, OPERATIONS.BUTTON_COMMAND);
|
||||
})
|
||||
.then(() => {
|
||||
this.log("sent dfu mode");
|
||||
return new Promise((resolve, reject) => {
|
||||
device.addEventListener("gattserverdisconnected", event => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
secureDfu.prototype.update = function(device, init, firmware) {
|
||||
if (!device) throw new Error("Device not specified");
|
||||
if (!init) throw new Error("Init not specified");
|
||||
if (!firmware) throw new Error("Firmware not specified");
|
||||
|
||||
return this.connect(device)
|
||||
.then(() => {
|
||||
this.log("transferring init");
|
||||
return this.transferInit(init);
|
||||
})
|
||||
.then(() => {
|
||||
this.log("enabled control notifications");
|
||||
this.controlChar.addEventListener("characteristicvaluechanged", this.handleNotification.bind(this));
|
||||
return service.getCharacteristic(PACKET_UUID);
|
||||
this.log("transferring firmware");
|
||||
return this.transferFirmware(firmware);
|
||||
})
|
||||
.then(characteristic => {
|
||||
.then(() => {
|
||||
this.log("complete, disconnecting...");
|
||||
return new Promise((resolve, reject) => {
|
||||
device.addEventListener("gattserverdisconnected", event => {
|
||||
this.log("disconnected");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
secureDfu.prototype.connect = function(device) {
|
||||
device.addEventListener("gattserverdisconnected", event => {
|
||||
this.controlChar = null;
|
||||
this.packetChar = null;
|
||||
});
|
||||
|
||||
return this.gattConnect(device)
|
||||
.then(characteristics => {
|
||||
this.log(`found ${characteristics.length} characteristic(s)`);
|
||||
|
||||
this.packetChar = characteristics.find(characteristic => {
|
||||
return (characteristic.uuid === PACKET_UUID);
|
||||
});
|
||||
if (!this.packetChar) throw new Error("Unable to find packet characteristic");
|
||||
this.log("found packet characteristic");
|
||||
this.packetChar = characteristic;
|
||||
this.connected = true;
|
||||
|
||||
this.controlChar = characteristics.find(characteristic => {
|
||||
return (characteristic.uuid === CONTROL_UUID);
|
||||
});
|
||||
if (!this.controlChar) throw new Error("Unable to find control characteristic");
|
||||
this.log("found control characteristic");
|
||||
|
||||
if (!this.controlChar.properties.notify && !this.controlChar.properties.indicate) {
|
||||
throw new Error("Control characteristic does not allow notifications");
|
||||
}
|
||||
return this.controlChar.startNotifications();
|
||||
})
|
||||
.then(() => {
|
||||
this.controlChar.addEventListener("characteristicvaluechanged", this.handleNotification.bind(this));
|
||||
this.log("enabled control notifications");
|
||||
return device;
|
||||
});
|
||||
};
|
||||
|
||||
secureDfu.prototype.gattConnect = function(device) {
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
if (device.gatt.connected) return device.gatt;
|
||||
return device.gatt.connect();
|
||||
})
|
||||
.then(server => {
|
||||
this.log("connected to gatt server");
|
||||
return server.getPrimaryService(SERVICE_UUID)
|
||||
.catch(error => {
|
||||
throw new Error("Unable to find DFU service");
|
||||
});
|
||||
})
|
||||
.then(service => {
|
||||
this.log("found DFU service");
|
||||
return service.getCharacteristics();
|
||||
});
|
||||
}
|
||||
|
||||
secureDfu.prototype.handleNotification = function(event) {
|
||||
let view = event.target.value;
|
||||
|
||||
if (view.getUint8(0) !== OPERATIONS.RESPONSE[0]) {
|
||||
throw new Error("unrecognised control characteristic response notification");
|
||||
if (OPERATIONS.RESPONSE.indexOf(view.getUint8(0)) < 0) {
|
||||
throw new Error("Unrecognised control characteristic response notification");
|
||||
}
|
||||
|
||||
let operation = view.getUint8(1);
|
||||
@@ -213,12 +311,8 @@
|
||||
}
|
||||
};
|
||||
|
||||
secureDfu.prototype.sendOperation = function(operation, buffer) {
|
||||
secureDfu.prototype.sendOperation = function(characteristic, operation, buffer) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.connected) throw new Error("device not connected");
|
||||
if (!this.controlChar) throw new Error("control characteristic not found");
|
||||
if (!this.packetChar) throw new Error("packet characteristic not found");
|
||||
|
||||
let size = operation.length;
|
||||
if (buffer) size += buffer.byteLength;
|
||||
|
||||
@@ -234,72 +328,80 @@
|
||||
reject: reject
|
||||
};
|
||||
|
||||
this.controlChar.writeValue(value);
|
||||
characteristic.writeValue(value);
|
||||
});
|
||||
}
|
||||
|
||||
secureDfu.prototype.sendControl = function(operation, buffer) {
|
||||
return this.sendOperation(this.controlChar, operation, buffer);
|
||||
}
|
||||
|
||||
secureDfu.prototype.transferInit = function(buffer) {
|
||||
return this.sendOperation(OPERATIONS.SELECT_COMMAND)
|
||||
return this.transfer(buffer, "init", OPERATIONS.SELECT_COMMAND, OPERATIONS.CREATE_COMMAND);
|
||||
}
|
||||
|
||||
secureDfu.prototype.transferFirmware = function(buffer) {
|
||||
return this.transfer(buffer, "firmware", OPERATIONS.SELECT_DATA, OPERATIONS.CREATE_DATA);
|
||||
}
|
||||
|
||||
secureDfu.prototype.transfer = function(buffer, type, selectType, createType) {
|
||||
return this.sendControl(selectType)
|
||||
.then(response => {
|
||||
|
||||
let maxSize = response.getUint32(0, LITTLE_ENDIAN);
|
||||
let offset = response.getUint32(4, LITTLE_ENDIAN);
|
||||
let crc = response.getInt32(8, LITTLE_ENDIAN);
|
||||
|
||||
if (offset === buffer.byteLength && this.checkCrc(buffer, crc)) {
|
||||
if (type === "init" && offset === buffer.byteLength && this.checkCrc(buffer, crc)) {
|
||||
this.log("init packet already available, skipping transfer");
|
||||
return;
|
||||
}
|
||||
|
||||
this.buffer = buffer;
|
||||
return this.transferObject(OPERATIONS.CREATE_COMMAND, maxSize, offset);
|
||||
this.progress = function(bytes) {
|
||||
this.dispatchEvent({
|
||||
type: "progress",
|
||||
object: type,
|
||||
totalBytes: buffer.byteLength,
|
||||
currentBytes: bytes
|
||||
});
|
||||
}
|
||||
this.progress(0);
|
||||
|
||||
return this.transferObject(buffer, createType, maxSize, offset);
|
||||
});
|
||||
}
|
||||
|
||||
secureDfu.prototype.transferFirmware = function(buffer) {
|
||||
return this.sendOperation(OPERATIONS.SELECT_DATA)
|
||||
.then(response => {
|
||||
|
||||
let maxSize = response.getUint32(0, LITTLE_ENDIAN);
|
||||
let offset = response.getUint32(4, LITTLE_ENDIAN);
|
||||
let crc = response.getInt32(8, LITTLE_ENDIAN);
|
||||
|
||||
this.buffer = buffer;
|
||||
return this.transferObject(OPERATIONS.CREATE_DATA, maxSize, offset);
|
||||
});
|
||||
}
|
||||
|
||||
secureDfu.prototype.transferObject = function(createType, maxSize, offset) {
|
||||
secureDfu.prototype.transferObject = function(buffer, createType, maxSize, offset) {
|
||||
let start = offset - offset % maxSize;
|
||||
let end = Math.min(start + maxSize, this.buffer.byteLength);
|
||||
let end = Math.min(start + maxSize, buffer.byteLength);
|
||||
|
||||
let view = new DataView(new ArrayBuffer(4));
|
||||
view.setUint32(0, end - start, LITTLE_ENDIAN);
|
||||
|
||||
return this.sendOperation(createType, view.buffer)
|
||||
return this.sendControl(createType, view.buffer)
|
||||
.then(response => {
|
||||
let data = this.buffer.slice(start, end);
|
||||
let data = buffer.slice(start, end);
|
||||
return this.transferData(data, start);
|
||||
})
|
||||
.then(() => {
|
||||
return this.sendOperation(OPERATIONS.CACULATE_CHECKSUM);
|
||||
return this.sendControl(OPERATIONS.CACULATE_CHECKSUM);
|
||||
})
|
||||
.then(response => {
|
||||
let crc = response.getInt32(4, LITTLE_ENDIAN);
|
||||
let transferred = response.getUint32(0, LITTLE_ENDIAN);
|
||||
let data = this.buffer.slice(0, transferred);
|
||||
let data = buffer.slice(0, transferred);
|
||||
|
||||
if (this.checkCrc(data, crc)) {
|
||||
this.log(`written ${transferred} bytes`);
|
||||
offset = transferred;
|
||||
return this.sendOperation(OPERATIONS.EXECUTE);
|
||||
return this.sendControl(OPERATIONS.EXECUTE);
|
||||
} else {
|
||||
this.log("object failed to validate");
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
if (end < this.buffer.byteLength) {
|
||||
return this.transferObject(createType, maxSize, offset);
|
||||
if (end < buffer.byteLength) {
|
||||
return this.transferObject(buffer, createType, maxSize, offset);
|
||||
} else {
|
||||
this.log("transfer complete");
|
||||
}
|
||||
@@ -313,11 +415,7 @@
|
||||
|
||||
return this.packetChar.writeValue(packet)
|
||||
.then(() => {
|
||||
this.dispatchEvent({
|
||||
type: "progress",
|
||||
currentBytes: offset + end,
|
||||
totalBytes: this.buffer.byteLength
|
||||
});
|
||||
this.progress(offset + end);
|
||||
|
||||
if (end < data.byteLength) {
|
||||
return this.transferData(data, offset, end);
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
font-weight: 600;
|
||||
}
|
||||
#drop {
|
||||
margin: 40px auto;
|
||||
max-width: 640px;
|
||||
background-color: rgba(255, 255, 255, 0.10);
|
||||
position: relative;
|
||||
padding: 100px 0px 70px;
|
||||
margin: 40px auto;
|
||||
max-width: 680px;
|
||||
background-color: rgba(255, 255, 255, 0.10);
|
||||
padding: 120px 0px 100px;
|
||||
outline: 2px dashed #072b44;
|
||||
outline-offset: -10px;
|
||||
}
|
||||
@@ -33,50 +33,49 @@
|
||||
}
|
||||
#icon {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
fill: #d7ecfb;
|
||||
display: block;
|
||||
margin-bottom: 40px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
#github {
|
||||
fill: #d7ecfb;
|
||||
}
|
||||
#github:hover {
|
||||
fill: #8bb5ba;
|
||||
}
|
||||
#file {
|
||||
width: 0.1px;
|
||||
height: 0.1px;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
}
|
||||
#label {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
}
|
||||
#label:hover strong {
|
||||
color: #8bb5ba;
|
||||
}
|
||||
#update {
|
||||
#select {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
visibility: hidden;
|
||||
}
|
||||
#button {
|
||||
font-size: 12px;
|
||||
color: #d5e8f1;
|
||||
color: inherit;
|
||||
margin: 20px auto;
|
||||
border: 0px;
|
||||
background-color: #1b679c;
|
||||
outline: none;
|
||||
height: 30px;
|
||||
padding: 0 30px;
|
||||
border-radius: 4px;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
}
|
||||
#button:hover {
|
||||
background: #2674ab;
|
||||
}
|
||||
#status {
|
||||
position: relative;
|
||||
margin: 20px auto;
|
||||
border: 1px solid #d7ecfb;
|
||||
width: 400px;
|
||||
@@ -89,9 +88,9 @@
|
||||
height: 100%;
|
||||
}
|
||||
#transfer {
|
||||
position: absolute;
|
||||
line-height: 24px;
|
||||
width: 100%;
|
||||
line-height: 24px;
|
||||
margin-top: -24px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
@@ -109,16 +108,22 @@
|
||||
<span>or drag it here</span>
|
||||
</label>
|
||||
|
||||
<div id="update">
|
||||
<button id="button">Update</button>
|
||||
<div id="select">
|
||||
<button id="button">Select Device</button>
|
||||
</div>
|
||||
|
||||
<div id="status">
|
||||
<div id="bar" />
|
||||
<div id="transfer" />
|
||||
<div id="bar"></div>
|
||||
<div id="transfer"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="https://github.com/thegecko/web-bluetooth-dfu/">
|
||||
<svg id="github" xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 16 16">
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"/>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/crc-32/1.0.2/crc32.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.3/jszip.min.js"></script>
|
||||
<script src="/dist/secure-dfu.js"></script>
|
||||
@@ -126,7 +131,8 @@
|
||||
<script>
|
||||
let dropEl = document.getElementById("drop");
|
||||
let fileEl = document.getElementById("file");
|
||||
let updateEl = document.getElementById("update");
|
||||
let selectEl = document.getElementById("select");
|
||||
let buttonEl = document.getElementById("button");
|
||||
let labelEl = document.getElementById("label");
|
||||
let statusEl = document.getElementById("status");
|
||||
let transferEl = document.getElementById("transfer");
|
||||
@@ -135,22 +141,22 @@
|
||||
let package = null;
|
||||
let manifest = null;
|
||||
|
||||
function updateStatus(state) {
|
||||
function setStatus(state) {
|
||||
labelEl.textContent = state;
|
||||
}
|
||||
|
||||
function updateTransfer(state) {
|
||||
function setTransfer(state) {
|
||||
if (!state) {
|
||||
statusEl.style.visibility = "hidden";
|
||||
return;
|
||||
}
|
||||
updateEl.style.visibility = "hidden";
|
||||
selectEl.style.visibility = "hidden";
|
||||
statusEl.style.visibility = "visible";
|
||||
barEl.style.width = state.currentBytes / state.totalBytes * 100 + '%';
|
||||
transferEl.textContent = `${state.currentBytes}/${state.totalBytes} bytes written`;
|
||||
transferEl.textContent = `${state.currentBytes}/${state.totalBytes} ${state.object} bytes written`;
|
||||
}
|
||||
|
||||
function updatePackage(file) {
|
||||
function setPackage(file) {
|
||||
if (!file) return;
|
||||
|
||||
JSZip.loadAsync(file)
|
||||
@@ -164,79 +170,91 @@
|
||||
})
|
||||
.then(content => {
|
||||
manifest = JSON.parse(content).manifest;
|
||||
updateStatus(`Package: ${file.name}`);
|
||||
updateEl.style.visibility = "visible";
|
||||
setStatus(`Firmware package: ${file.name}`);
|
||||
selectEl.style.visibility = "visible";
|
||||
})
|
||||
.catch(error => {
|
||||
updateEl.style.visibility = "hidden";
|
||||
selectEl.style.visibility = "hidden";
|
||||
statusEl.style.visibility = "hidden";
|
||||
updateStatus(error);
|
||||
setStatus(error);
|
||||
});
|
||||
}
|
||||
|
||||
function transfer() {
|
||||
if (!package) return;
|
||||
updateStatus(`Selecting device...`);
|
||||
updateTransfer();
|
||||
function selectDevice() {
|
||||
setStatus("Selecting device...");
|
||||
setTransfer();
|
||||
|
||||
const dfu = new SecureDfu(CRC32.buf);
|
||||
dfu.addEventListener("log", event => {
|
||||
console.log(event.message);
|
||||
});
|
||||
dfu.addEventListener("progress", event => {
|
||||
updateTransfer(event);
|
||||
setTransfer(event);
|
||||
});
|
||||
|
||||
dfu.requestDevice()
|
||||
dfu.requestDevice(true)
|
||||
.then(device => {
|
||||
return dfu.connect(device);
|
||||
if (!device) {
|
||||
setStatus("Buttonless mode set, select device again");
|
||||
return;
|
||||
}
|
||||
return update(dfu, device);
|
||||
})
|
||||
.catch(error => {
|
||||
statusEl.style.visibility = "hidden";
|
||||
setStatus(error);
|
||||
});
|
||||
}
|
||||
|
||||
function update(dfu, device) {
|
||||
if (!package) return;
|
||||
|
||||
Promise.resolve()
|
||||
.then(() => {
|
||||
for (type of ["softdevice", "bootloader", "softdevice_bootloader"]) {
|
||||
if (manifest[type]) {
|
||||
return transferImage(dfu, manifest[type]);
|
||||
return updateFirmware(device, dfu, manifest[type], type);
|
||||
}
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
if (manifest.application) {
|
||||
return transferImage(dfu, manifest.application);
|
||||
return updateFirmware(device, dfu, manifest.application, "application");
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
updateStatus("Update complete!");
|
||||
updateTransfer();
|
||||
setStatus("Update complete!");
|
||||
setTransfer();
|
||||
})
|
||||
.catch(error => {
|
||||
statusEl.style.visibility = "hidden";
|
||||
updateStatus(error);
|
||||
setStatus(error);
|
||||
});
|
||||
}
|
||||
|
||||
function transferImage(dfu, manifest) {
|
||||
function updateFirmware(device, dfu, manifest, type) {
|
||||
let init = null;
|
||||
|
||||
return package.file(manifest.dat_file).async("arraybuffer")
|
||||
.then(data => {
|
||||
updateStatus(`Transferring init: ${manifest.dat_file}...`);
|
||||
return dfu.transferInit(data);
|
||||
})
|
||||
.then(() => {
|
||||
init = data;
|
||||
return package.file(manifest.bin_file).async("arraybuffer");
|
||||
})
|
||||
.then(data => {
|
||||
updateStatus(`Transferring firmware: ${manifest.bin_file}...`);
|
||||
return dfu.transferFirmware(data);
|
||||
setStatus(`Updating ${type}: ${manifest.bin_file}...`);
|
||||
return dfu.update(device, init, data);
|
||||
});
|
||||
}
|
||||
|
||||
fileEl.addEventListener("change", event => {
|
||||
updatePackage(event.target.files[0]);
|
||||
setPackage(event.target.files[0]);
|
||||
});
|
||||
|
||||
dropEl.addEventListener("drop", event => {
|
||||
updatePackage(event.dataTransfer.files[0]);
|
||||
setPackage(event.dataTransfer.files[0]);
|
||||
});
|
||||
|
||||
updateEl.addEventListener("click", transfer);
|
||||
buttonEl.addEventListener("click", selectDevice);
|
||||
|
||||
["drag", "dragstart", "dragend", "dragover", "dragenter", "dragleave", "drop"].forEach(eventName => {
|
||||
dropEl.addEventListener(eventName, event => {
|
||||
|
||||
13
package.json
13
package.json
@@ -20,14 +20,17 @@
|
||||
"nordic",
|
||||
"web-bluetooth"
|
||||
],
|
||||
"scripts": {
|
||||
"example": "node examples/secure_dfu_node.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"bleat": "^0.1.1",
|
||||
"crc-32": "^1.0.2",
|
||||
"jszip": "^3.1.3",
|
||||
"progress": "^2.0.0"
|
||||
"bleat": "^0.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"crc-32": "^1.0.2",
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-jshint": "^1.9.0"
|
||||
"gulp-jshint": "^1.9.0",
|
||||
"jszip": "^3.1.3",
|
||||
"progress": "^2.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user