diff --git a/dist/secure-dfu.js b/dist/secure-dfu.js index 1a340c0..646978f 100644 --- a/dist/secure-dfu.js +++ b/dist/secure-dfu.js @@ -190,7 +190,7 @@ .then(() => { this.log("enabled buttonless notifications"); buttonChar.addEventListener("characteristicvaluechanged", this.handleNotification.bind(this)); - return this.sendOperation(buttonChar, OPERATIONS.BUTTON_COMMAND); + this.sendOperation(buttonChar, OPERATIONS.BUTTON_COMMAND); }) .then(() => { this.log("sent dfu mode"); @@ -436,5 +436,6 @@ secureDfu.prototype.removeEventListener = removeEventListener; secureDfu.prototype.dispatchEvent = dispatchEvent; + secureDfu.SERVICE_UUID = SERVICE_UUID; return secureDfu; })); diff --git a/examples/secure_dfu_node.js b/examples/secure_dfu_node.js index 5200cc9..7122ca0 100644 --- a/examples/secure_dfu_node.js +++ b/examples/secure_dfu_node.js @@ -1,18 +1,176 @@ -var fs = require('fs'); -var bluetooth = require("bleat").webbluetooth; +var fs = require("fs"); +var http = require("http"); +var https = require("https"); +var readline = require("readline"); var crc = require("crc-32"); -var progress = require('progress'); +var JSZip = require("jszip"); +var progress = require("progress"); +var bluetooth = require("bleat").webbluetooth; var secureDfu = require("../index").secure; -var bluetoothDevices = []; -var dat = fs.readFileSync("test_images_update_nrf52832/dfu_test_app_hrm_s132/nrf52832_xxaa.dat"); -var bin = fs.readFileSync("test_images_update_nrf52832/dfu_test_app_hrm_s132/nrf52832_xxaa.bin"); +var bluetoothDevices = []; +var progressBar = null; function logError(error) { - console.log(error); + console.log(error.message || error); process.exit(); } +function getFileName() { + return new Promise((resolve, reject) => { + if (process.argv[2]) { + return resolve(process.argv[2]); + } + + var rl = readline.createInterface(process.stdin, process.stdout); + rl.question("Enter a URL or file path for the firmware package: ", answer => { + rl.close(); + resolve(answer); + }); + rl.write("firmware/dfu_app_s132.zip"); + }); +} + +function downloadFile(url) { + return new Promise((resolve, reject) => { + console.log("Downloading file..."); + var scheme = (url.indexOf("https") === 0) ? https : http; + + scheme.get(url, response => { + var data = []; + response.on("data", chunk => { + data.push(chunk); + }); + response.on("end", () => { + if (response.statusCode !== 200) return reject(response.statusMessage); + + var download = Buffer.concat(data); + resolve(new Uint8Array(download).buffer); + }); + }) + .on("error", error => { + reject(error); + }); + }); +} + +function loadFile(fileName) { + return new Promise((resolve, reject) => { + var file = fs.readFileSync(fileName); + resolve(new Uint8Array(file).buffer); + }); +} + +function handleDeviceFound(bluetoothDevice, selectFn) { + var discovered = bluetoothDevices.some(device => { + return (device.id === bluetoothDevice.id); + }); + if (discovered) return; + + if (bluetoothDevices.length === 0) { + process.stdin.setRawMode(true); + console.log("Select a device to update:"); + } + + bluetoothDevices.push({ id: bluetoothDevice.id, select: selectFn }); + console.log(`${bluetoothDevices.length}: ${bluetoothDevice.name}`); +} + +function updateFirmware(dfu, package, manifest, device, type) { + var init = null; + + return package.file(manifest.dat_file).async("arraybuffer") + .then(data => { + init = data; + return package.file(manifest.bin_file).async("arraybuffer"); + }) + .then(data => { + console.log(`Using firmware: ${manifest.bin_file}`); + progressBar = new progress(`Updating ${type} [:bar] :percent :etas`, { + complete: "=", + incomplete: " ", + width: 20, + total: data.byteLength + }); + return dfu.update(device, init, data); + }); +} + +function update() { + var dfu = null; + var package = null; + var manifest = null; + + getFileName() + .then(fileName => { + if (!fileName) throw new Error("No file name specified"); + if (fileName.indexOf("http") === 0) return downloadFile(fileName); + return loadFile(fileName); + }) + .then(file => { + return JSZip.loadAsync(file); + }) + .then(zipFile => { + try { + package = zipFile; + return zipFile.file("manifest.json").async("string"); + } catch(e) { + throw new Error("Unable to find manifest, is this a proper DFU package?"); + } + }) + .then(content => { + manifest = JSON.parse(content).manifest; + dfu = new secureDfu(crc.buf); + dfu.addEventListener("progress", event => { + if (progressBar && event.object === "firmware") { + progressBar.update(event.currentBytes / event.totalBytes); + } + }); + + console.log("Scanning for DFU devices..."); + return bluetooth.requestDevice({ + acceptAllDevices: true, + optionalServices: [secureDfu.SERVICE_UUID], + deviceFound: handleDeviceFound + }); + }) + .then(device => { + console.log(`${device.name} selected, connecting...`); + return dfu.setDfuMode(device); + }) + .then(device => { + if (device) return device; + + console.log("DFU mode set"); + return bluetooth.requestDevice({ + filters: [{ services: [secureDfu.SERVICE_UUID] }], + deviceFound: device => { + // Select first device found with correct service + return true; + } + }); + }) + .then(selectedDevice => { + device = selectedDevice; + + for (type of ["softdevice", "bootloader", "softdevice_bootloader"]) { + if (manifest[type]) { + return updateFirmware(dfu, package, manifest[type], device, type); + } + } + }) + .then(() => { + if (manifest.application) { + return updateFirmware(dfu, package, manifest.application, device, "application"); + } + }) + .then(() => { + console.log("Update complete!"); + process.exit(); + }) + .catch(logError); +} + process.stdin.setEncoding('utf8'); process.stdin.on('readable', () => { var input = process.stdin.read(); @@ -22,77 +180,9 @@ process.stdin.on('readable', () => { var index = parseInt(input); if (index && index <= bluetoothDevices.length) { process.stdin.setRawMode(false); - selectDevice(index - 1); + bluetoothDevices[index - 1].select(); } } }); -function toArrayBuffer(buffer) { - var ab = new ArrayBuffer(buffer.length); - var view = new Uint8Array(ab); - for (var i = 0; i < buffer.length; ++i) { - view[i] = buffer[i]; - } - return ab; -} - -function selectDevice(index) { - var bar = null; - var device = bluetoothDevices[index]; - var dfu = new secureDfu(crc.buf); - dfu.addEventListener("progress", event => { - if (bar) bar.update(event.currentBytes / event.totalBytes); - }); - - console.log(); - - dfu.connect(device) - .then(() => { - process.stdout.write("Transferring init packet..."); - return dfu.transferInit(toArrayBuffer(dat)); - }) - .then(data => { - console.log("Done"); - var data = toArrayBuffer(bin); - bar = new progress('Transferring firmware [:bar] :percent :etas', { - complete: '=', - incomplete: ' ', - width: 20, - total: data.byteLength - }); - return dfu.transferFirmware(data); - }) - .then(() => { - console.log("\nComplete!"); - process.exit(); - }) - .catch(logError); -} - -function handleDeviceFound(bluetoothDevice) { - var discovered = bluetoothDevices.some(device => { - return (device.id === bluetoothDevice.id); - }); - if (discovered) return; - - if (bluetoothDevices.length === 0) { - process.stdin.setRawMode(true); - console.log("Select a device to update:"); - } - - bluetoothDevices.push(bluetoothDevice); - console.log(bluetoothDevices.length + ": " + bluetoothDevice.name); -} - -console.log("Scanning for DFU devices..."); -bluetooth.requestDevice({ - filters: [{ services: [0xFE59] }], - deviceFound: handleDeviceFound -}) -.then(() => { - if (bluetoothDevices.length === 0) { - console.log("no devices found"); - process.exit(); - } -}) -.catch(logError); +update(); diff --git a/examples/secure_dfu_web.html b/examples/secure_dfu_web.html index 7b924e6..d16981c 100644 --- a/examples/secure_dfu_web.html +++ b/examples/secure_dfu_web.html @@ -195,7 +195,7 @@ dfu.requestDevice(true) .then(device => { if (!device) { - setStatus("Buttonless mode set, select device again"); + setStatus("DFU mode set, select device again"); return; } return update(dfu, device); diff --git a/firmware/dfu_app_s132.zip b/firmware/dfu_app_s132.zip new file mode 100755 index 0000000..a4f94b4 Binary files /dev/null and b/firmware/dfu_app_s132.zip differ diff --git a/firmware/dfu_bootloader_s132.zip b/firmware/dfu_bootloader_s132.zip new file mode 100755 index 0000000..a551b85 Binary files /dev/null and b/firmware/dfu_bootloader_s132.zip differ diff --git a/package.json b/package.json index bfc535b..31a42af 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "example": "node examples/secure_dfu_node.js" }, "dependencies": { - "bleat": "^0.1.2" + "bleat": "^0.1.3" }, "devDependencies": { "crc-32": "^1.0.2",