mirror of
https://github.com/thegecko/web-bluetooth-dfu.git
synced 2025-12-12 20:18:13 +08:00
First site
This commit is contained in:
344
dist/dfu.js
vendored
Normal file
344
dist/dfu.js
vendored
Normal file
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
* Protocol from:
|
||||
* http://developer.nordicsemi.com/nRF51_SDK/nRF51_SDK_v8.x.x/doc/8.1.0/s110/html/a00103.html
|
||||
*/
|
||||
|
||||
// https://github.com/umdjs/umd
|
||||
(function (root, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(['es6-promise', 'bleat'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node. Does not work with strict CommonJS
|
||||
module.exports = factory(Promise, require('bleat'));
|
||||
} else {
|
||||
// Browser globals with support for web workers (root is window)
|
||||
root.dfu = factory(Promise, root.navigator.bluetooth);
|
||||
}
|
||||
}(this, function(Promise, bluetooth) {
|
||||
"use strict";
|
||||
|
||||
var packetSize = 20;
|
||||
var notifySteps = 40;
|
||||
|
||||
var serviceUUID = "00001530-1212-efde-1523-785feabcd123";
|
||||
var controlUUID = "00001531-1212-efde-1523-785feabcd123";
|
||||
var packetUUID = "00001532-1212-efde-1523-785feabcd123";
|
||||
var versionUUID = "00001534-1212-efde-1523-785feabcd123";
|
||||
|
||||
var ImageType = {
|
||||
None: 0,
|
||||
SoftDevice: 1,
|
||||
Bootloader: 2,
|
||||
SoftDevice_Bootloader: 3,
|
||||
Application: 4
|
||||
};
|
||||
|
||||
var littleEndian = (function() {
|
||||
var buffer = new ArrayBuffer(2);
|
||||
new DataView(buffer).setInt16(0, 256, true);
|
||||
return new Int16Array(buffer)[0] === 256;
|
||||
})();
|
||||
|
||||
var controlChar = null;
|
||||
var packetChar = null;
|
||||
var versionChar = null;
|
||||
var server = null;
|
||||
|
||||
// Hack to see debug info
|
||||
var resultsEl = document.getElementById("results");
|
||||
function logFn(message) {
|
||||
console.log(message);
|
||||
resultsEl.innerText += message + "\n";
|
||||
}
|
||||
|
||||
function findDevice(filters) {
|
||||
return bluetooth.requestDevice({
|
||||
filters: [ filters ],
|
||||
optionalServices: [serviceUUID]
|
||||
});
|
||||
}
|
||||
|
||||
function writeMode(device) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
|
||||
// Disconnect event currently not implemented
|
||||
/*
|
||||
device.addEventListener("gattserverdisconnected", () => {
|
||||
logFn("modeData written");
|
||||
resolve();
|
||||
});
|
||||
*/
|
||||
connect(device)
|
||||
.then(() => {
|
||||
logFn("writing modeData...");
|
||||
controlChar.writeValue(new Uint8Array([1]));
|
||||
return server.disconnect();
|
||||
})
|
||||
.then(() => {
|
||||
logFn("modeData written");
|
||||
resolve(device);
|
||||
}).catch(error => {
|
||||
error = "writeMode error: " + error;
|
||||
logFn(error);
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function provision(device, arrayBuffer, imageType) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
imageType = imageType || ImageType.Application;
|
||||
|
||||
connect(device)
|
||||
.then(() => {
|
||||
if (versionChar) {
|
||||
versionChar.readValue()
|
||||
.then(data => {
|
||||
var view = new DataView(data);
|
||||
var major = view.getUint8(0);
|
||||
var minor = view.getUint8(1);
|
||||
return transfer(arrayBuffer, imageType, major, minor);
|
||||
});
|
||||
} else {
|
||||
// Default to version 6.0
|
||||
return transfer(arrayBuffer, imageType, 6, 0);
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
resolve();
|
||||
})
|
||||
.catch(error => {
|
||||
logFn(error);
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function connect(device) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var service = null;
|
||||
// Disconnect event currently not implemented
|
||||
/*
|
||||
device.addEventListener("gattserverdisconnected", () => {
|
||||
logFn("device disconnected");
|
||||
service = null;
|
||||
controlChar = null;
|
||||
packetChar = null;
|
||||
versionChar = null;
|
||||
server = null;
|
||||
});
|
||||
*/
|
||||
device.connectGATT()
|
||||
.then(gattServer => {
|
||||
// Connected
|
||||
server = gattServer;
|
||||
logFn("connected to device");
|
||||
return server.getPrimaryService(serviceUUID);
|
||||
})
|
||||
.then(primaryService => {
|
||||
logFn("found DFU service");
|
||||
service = primaryService;
|
||||
return service.getCharacteristic(controlUUID);
|
||||
})
|
||||
.then(characteristic => {
|
||||
logFn("found control characteristic");
|
||||
controlChar = characteristic;
|
||||
return service.getCharacteristic(packetUUID);
|
||||
})
|
||||
.then(characteristic => {
|
||||
logFn("found packet characteristic");
|
||||
packetChar = characteristic;
|
||||
service.getCharacteristic(versionUUID)
|
||||
.then(() => {
|
||||
logFn("found version characteristic");
|
||||
versionChar = characteristic;
|
||||
resolve();
|
||||
})
|
||||
.catch(error => {
|
||||
resolve();
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
error = "connect error: " + error;
|
||||
logFn(error);
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var interval;
|
||||
var offset;
|
||||
function transfer(arrayBuffer, imageType, majorVersion, minorVersion) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
logFn('using dfu version ' + majorVersion + "." + minorVersion);
|
||||
|
||||
// Set up receipts
|
||||
interval = Math.floor(arrayBuffer.byteLength / (packetSize * notifySteps));
|
||||
offset = 0;
|
||||
|
||||
controlChar.addEventListener('characteristicvaluechanged', data => {
|
||||
var view = new DataView(data);
|
||||
var opCode = view.getUint8(0);
|
||||
|
||||
if (opCode === 16) { // response
|
||||
var resp_code = view.getUint8(2);
|
||||
if (resp_code !== 1) {
|
||||
var error = "error from control: " + resp_code;
|
||||
logFn(error);
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
var req_opcode = view.getUint8(1);
|
||||
if (req_opcode === 1 && majorVersion > 6) {
|
||||
logFn('write null init packet');
|
||||
|
||||
controlChar.writeValue(new Uint8Array([2,0]))
|
||||
.then(() => {
|
||||
return packetChar.writeValue(new Uint8Array([0]));
|
||||
})
|
||||
.then(() => {
|
||||
return controlChar.writeValue(new Uint8Array([2,1]));
|
||||
})
|
||||
.catch(error => {
|
||||
error = "error writing init: " + error;
|
||||
logFn(error);
|
||||
reject(error);
|
||||
});
|
||||
|
||||
} else if (req_opcode === 1 || req_opcode === 2) {
|
||||
logFn('complete, send packet count');
|
||||
|
||||
var buffer = new ArrayBuffer(3);
|
||||
var view = new DataView(buffer);
|
||||
view.setUint8(0, 8);
|
||||
view.setUint16(1, interval, littleEndian);
|
||||
|
||||
controlChar.writeValue(view)
|
||||
.then(() => {
|
||||
logFn("sent packet count: " + interval);
|
||||
return controlChar.writeValue(new Uint8Array([3]));
|
||||
})
|
||||
.then(() => {
|
||||
logFn("sent receive");
|
||||
return writePacket(arrayBuffer, 0);
|
||||
})
|
||||
.catch(error => {
|
||||
error = "error sending packet count: " + error;
|
||||
logFn(error);
|
||||
reject(error);
|
||||
});
|
||||
|
||||
} else if (req_opcode === 3) {
|
||||
logFn('complete, check length');
|
||||
|
||||
controlChar.writeValue(new Uint8Array([7]))
|
||||
.catch(error => {
|
||||
error = "error checking length: " + error;
|
||||
logFn(error);
|
||||
reject(error);
|
||||
});
|
||||
|
||||
} else if (req_opcode === 7) {
|
||||
var bytecount = view.getUint32(3, littleEndian);
|
||||
logFn('length: ' + bytecount);
|
||||
logFn('complete, validate...');
|
||||
|
||||
controlChar.writeValue(new Uint8Array([4]))
|
||||
.catch(error => {
|
||||
error = "error validating: " + error;
|
||||
logFn(error);
|
||||
reject(error);
|
||||
});
|
||||
|
||||
} else if (req_opcode === 4) {
|
||||
logFn('complete, reset...');
|
||||
|
||||
controlChar.writeValue(new Uint8Array([5]))
|
||||
.then(() => {
|
||||
resolve();
|
||||
})
|
||||
.catch(error => {
|
||||
error = "error resetting: " + error;
|
||||
logFn(error);
|
||||
reject(error);
|
||||
});
|
||||
}
|
||||
|
||||
} else if (opCode === 17) {
|
||||
var bytecount = view.getUint32(1, littleEndian);
|
||||
logFn('transferred: ' + bytecount);
|
||||
writePacket(arrayBuffer, 0);
|
||||
}
|
||||
});
|
||||
|
||||
if (!controlChar.properties.notify) {
|
||||
var error = "controlChar missing notify property";
|
||||
logFn(error);
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
logFn("enabling notifications");
|
||||
controlChar.startNotifications()
|
||||
.then(() => {
|
||||
logFn("sending imagetype: " + imageType);
|
||||
return controlChar.writeValue(new Uint8Array([1, imageType]))
|
||||
})
|
||||
.then(() => {
|
||||
logFn("sent start");
|
||||
|
||||
var softLength = (imageType === ImageType.SoftDevice) ? arrayBuffer.byteLength : 0;
|
||||
var bootLength = (imageType === ImageType.Bootloader) ? arrayBuffer.byteLength : 0;
|
||||
var appLength = (imageType === ImageType.Application) ? arrayBuffer.byteLength : 0;
|
||||
|
||||
var buffer = new ArrayBuffer(12);
|
||||
var view = new DataView(buffer);
|
||||
view.setUint32(0, softLength, littleEndian);
|
||||
view.setUint32(4, bootLength, littleEndian);
|
||||
view.setUint32(8, appLength, littleEndian);
|
||||
|
||||
// Set firmware length
|
||||
packetChar.writeValue(view)
|
||||
.then(() => {
|
||||
logFn("sent buffer size: " + arrayBuffer.byteLength);
|
||||
})
|
||||
.catch(error => {
|
||||
error = "firmware length error: " + error;
|
||||
logFn(error);
|
||||
reject(error);
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
error = "start error: " + error;
|
||||
logFn(error);
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function writePacket(arrayBuffer, offset, count) {
|
||||
var size = (offset + packetSize > arrayBuffer.byteLength) ? arrayBuffer.byteLength - offset : packetSize;
|
||||
var packet = arrayBuffer.slice(offset, offset + size);
|
||||
var view = new Uint8Array(packet);
|
||||
|
||||
packetChar.writeValue(view)
|
||||
.then(() => {
|
||||
count ++;
|
||||
offset += packetSize;
|
||||
if (count < interval && offset < arrayBuffer.byteLength) {
|
||||
writePacket(arrayBuffer, count);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
error = "writePacket error: " + error;
|
||||
logFn(error);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
ImageType: ImageType,
|
||||
findDevice: findDevice,
|
||||
writeMode: writeMode,
|
||||
provision: provision
|
||||
};
|
||||
}));
|
||||
Reference in New Issue
Block a user