mirror of
https://github.com/thegecko/web-bluetooth-dfu.git
synced 2025-12-12 20:18:13 +08:00
Merge remote-tracking branch 'refs/remotes/thegecko/master'
This commit is contained in:
20
README.md
20
README.md
@@ -1,29 +1,31 @@
|
|||||||
# Web Bluetooth DFU
|
# Web Bluetooth DFU
|
||||||
Device firmware update with Web Bluetooth
|
Device firmware update with Web Bluetooth
|
||||||
|
|
||||||
Update device firmware via [Web Bluetooth](https://webbluetoothcg.github.io/web-bluetooth/) using the protocol here:
|
Update device firmware via [Web Bluetooth](https://webbluetoothcg.github.io/web-bluetooth/) following the protocol here:
|
||||||
|
|
||||||
[http://developer.nordicsemi.com/nRF51_SDK/nRF51_SDK_v8.x.x/doc/8.1.0/s110/html/a00103.html](http://developer.nordicsemi.com/nRF51_SDK/nRF51_SDK_v8.x.x/doc/8.1.0/s110/html/a00103.html)
|
http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.sdk52.v0.9.2/bledfu_transport.html?cp=4_0_2_4_2_4
|
||||||
|
|
||||||
## Device Configuration
|
## Device Configuration
|
||||||
|
|
||||||
Put this firmware onto an [nrf51822](https://www.nordicsemi.com/eng/Products/nRF51-DK):
|
You will need an [nRF51](https://www.nordicsemi.com/Products/nRF51-DK) or [nRF52](https://www.nordicsemi.com/Products/Bluetooth-Smart-Bluetooth-low-energy/nRF52-DK) development kit, flashed with the appropriate image:
|
||||||
|
|
||||||
[NRF51822_DFU_Test_BOOT.hex](https://thegecko.github.io/web-bluetooth-dfu/firmware/NRF51822_DFU_Test_BOOT.hex)
|
[nrf51_boot_s110.hex](https://thegecko.github.io/web-bluetooth-dfu/firmware/nrf51_boot_s110.hex)
|
||||||
|
|
||||||
Then reset the device.
|
[nrf52_boot_s132.hex](https://thegecko.github.io/web-bluetooth-dfu/firmware/nrf52_boot_s132.hex)
|
||||||
|
|
||||||
## Web Example
|
## Web Example
|
||||||
|
|
||||||
Open this site in a Web Bluetooth enabled browser:
|
Open this site in a [Web Bluetooth](https://webbluetoothcg.github.io/web-bluetooth/) enabled browser:
|
||||||
|
|
||||||
[https://thegecko.github.io/web-bluetooth-dfu/](https://thegecko.github.io/web-bluetooth-dfu/)
|
[https://thegecko.github.io/web-bluetooth-dfu/](https://thegecko.github.io/web-bluetooth-dfu/)
|
||||||
|
|
||||||
## Node Example
|
## Node Example
|
||||||
|
|
||||||
Install the npm dependencies and run.
|
Clone this repository, install the npm dependencies and execute.
|
||||||
|
|
||||||
```
|
```
|
||||||
npm install
|
npm install
|
||||||
node example_node
|
node example_node <device-type>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Where ```<device-type>``` is one of ```nrf51``` or ```nrf52```.
|
||||||
305
dist/dfu.js
vendored
305
dist/dfu.js
vendored
@@ -1,10 +1,10 @@
|
|||||||
/* @license
|
/* @license
|
||||||
*
|
*
|
||||||
* Device firmware update with Web Bluetooth
|
* Device firmware update with Web Bluetooth
|
||||||
* Version: 0.0.1
|
* Version: 0.0.2
|
||||||
*
|
*
|
||||||
* Protocol from:
|
* Protocol from:
|
||||||
* http://developer.nordicsemi.com/nRF51_SDK/nRF51_SDK_v8.x.x/doc/8.1.0/s110/html/a00103.html
|
* http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.sdk52.v0.9.2/bledfu_transport.html?cp=4_0_2_4_2_4
|
||||||
*
|
*
|
||||||
* The MIT License (MIT)
|
* The MIT License (MIT)
|
||||||
*
|
*
|
||||||
@@ -44,6 +44,8 @@
|
|||||||
}(this, function(Promise, bluetooth) {
|
}(this, function(Promise, bluetooth) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
var LITTLE_ENDIAN = true;
|
||||||
|
|
||||||
var packetSize = 20;
|
var packetSize = 20;
|
||||||
var notifySteps = 40;
|
var notifySteps = 40;
|
||||||
|
|
||||||
@@ -59,12 +61,30 @@
|
|||||||
SoftDevice_Bootloader: 3,
|
SoftDevice_Bootloader: 3,
|
||||||
Application: 4
|
Application: 4
|
||||||
};
|
};
|
||||||
|
|
||||||
var littleEndian = (function() {
|
// TODO: This should be configurable by the user. For now this will work with any of Nordic's SDK examples.
|
||||||
var buffer = new ArrayBuffer(2);
|
var initPacket = {
|
||||||
new DataView(buffer).setInt16(0, 256, true);
|
device_type: 0xFFFF,
|
||||||
return new Int16Array(buffer)[0] === 256;
|
device_rev: 0xFFFF,
|
||||||
})();
|
app_version: 0xFFFFFFFF,
|
||||||
|
softdevice_len: 0x0001,
|
||||||
|
softdevice: 0xFFFE,
|
||||||
|
crc: 0x0000
|
||||||
|
};
|
||||||
|
|
||||||
|
var OPCODE = {
|
||||||
|
RESERVED: 0,
|
||||||
|
START_DFU: 1,
|
||||||
|
INITIALIZE_DFU_PARAMETERS: 2,
|
||||||
|
RECEIVE_FIRMWARE_IMAGE: 3,
|
||||||
|
VALIDATE_FIRMWARE: 4,
|
||||||
|
ACTIVATE_IMAGE_AND_RESET: 5,
|
||||||
|
RESET_SYSTEM: 6,
|
||||||
|
REPORT_RECEIVED_IMAGE_SIZE: 7,
|
||||||
|
PACKET_RECEIPT_NOTIFICATION_REQUEST: 8,
|
||||||
|
RESPONSE_CODE: 16,
|
||||||
|
PACKET_RECEIPT_NOTIFICATION: 17
|
||||||
|
};
|
||||||
|
|
||||||
var loggers = [];
|
var loggers = [];
|
||||||
function addLogger(loggerFn) {
|
function addLogger(loggerFn) {
|
||||||
@@ -85,28 +105,41 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switch to bootloader/DFU mode by writing to the control point of the DFU Service.
|
||||||
|
* The DFU Controller is not responsible for disconnecting from the application (DFU Target) after the write.
|
||||||
|
* The application (DFU Target) will issue a GAP Disconnect and reset into bootloader/DFU mode.
|
||||||
|
*
|
||||||
|
* https://infocenter.nordicsemi.com/topic/com.nordic.infocenter.sdk5.v11.0.0/bledfu_appswitching.html?cp=4_0_0_4_1_3_2_2
|
||||||
|
*/
|
||||||
function writeMode(device) {
|
function writeMode(device) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
/*
|
/*
|
||||||
// Disconnect event currently not implemented
|
// TODO: once disconnect event is implemented we should resolve in its callback...
|
||||||
device.addEventListener("gattserverdisconnected", () => {
|
device.addEventListener("gattserverdisconnected", () => {
|
||||||
log("modeData written");
|
log("DFU Target issued GAP Disconnect and reset into Bootloader/DFU mode.");
|
||||||
resolve();
|
resolve(device);
|
||||||
});
|
});
|
||||||
*/
|
*/
|
||||||
|
var characteristics = null;
|
||||||
|
|
||||||
connect(device)
|
connect(device)
|
||||||
.then(chars => {
|
.then(chars => {
|
||||||
log("writing modeData...");
|
log("enabling notifications");
|
||||||
chars.controlChar.writeValue(new Uint8Array([1]));
|
characteristics = chars;
|
||||||
|
return characteristics.controlChar.startNotifications();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
log("writing modeData");
|
||||||
|
return characteristics.controlChar.writeValue(new Uint8Array([1, 4]));
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
log("modeData written");
|
||||||
// Hack to gracefully disconnect without disconnect event
|
// Hack to gracefully disconnect without disconnect event
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
chars.server.disconnect();
|
characteristics.server.disconnect();
|
||||||
setTimeout(() => {
|
resolve(device);
|
||||||
log("modeData written");
|
}, 2000);
|
||||||
resolve(device);
|
|
||||||
}, 3000);
|
|
||||||
}, 3000);
|
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
error = "writeMode error: " + error;
|
error = "writeMode error: " + error;
|
||||||
@@ -116,22 +149,44 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains basic functionality for performing safety checks on software updates for nRF5 based devices.
|
||||||
|
* Init packet used for pre-checking to ensure the following image is compatible with the device.
|
||||||
|
* Contains information on device type, revision, and supported SoftDevices along with a CRC or hash of firmware image.
|
||||||
|
*
|
||||||
|
* Not used in mbed bootloader (init packet was optional in SDK v6.x).
|
||||||
|
*/
|
||||||
|
function generateInitPacket() {
|
||||||
|
var buffer = new ArrayBuffer(14);
|
||||||
|
var view = new DataView(buffer);
|
||||||
|
view.setUint16(0, initPacket.device_type, LITTLE_ENDIAN);
|
||||||
|
view.setUint16(2, initPacket.device_rev, LITTLE_ENDIAN);
|
||||||
|
view.setUint32(4, initPacket.app_version, LITTLE_ENDIAN); // Application version for the image software. This field allows for additional checking, for example ensuring that a downgrade is not allowed.
|
||||||
|
view.setUint16(8, initPacket.softdevice_len, LITTLE_ENDIAN); // Number of different SoftDevice revisions compatible with this application.
|
||||||
|
view.setUint16(10, initPacket.softdevice, LITTLE_ENDIAN); // Variable length array of SoftDevices compatible with this application. The length of the array is specified in the length (softdevice_len) field. 0xFFFE indicates any SoftDevice.
|
||||||
|
view.setUint16(12, initPacket.crc, LITTLE_ENDIAN);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
function provision(device, arrayBuffer, imageType) {
|
function provision(device, arrayBuffer, imageType) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
|
var versionChar = null;
|
||||||
imageType = imageType || ImageType.Application;
|
imageType = imageType || ImageType.Application;
|
||||||
|
|
||||||
connect(device)
|
connect(device)
|
||||||
.then(chars => {
|
.then(chars => {
|
||||||
if (chars.versionChar) {
|
versionChar = chars.versionChar;
|
||||||
return chars.versionChar.readValue()
|
if (versionChar) { // Older DFU implementations (from older Nordic SDKs < 7.0) have no DFU Version characteristic.
|
||||||
|
return versionChar.readValue()
|
||||||
.then(data => {
|
.then(data => {
|
||||||
|
console.log('read versionChar');
|
||||||
var view = new DataView(data);
|
var view = new DataView(data);
|
||||||
var major = view.getUint8(0);
|
var major = view.getUint8(0);
|
||||||
var minor = view.getUint8(1);
|
var minor = view.getUint8(1);
|
||||||
return transfer(chars, arrayBuffer, imageType, major, minor);
|
return transfer(chars, arrayBuffer, imageType, major, minor);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Default to version 6.0
|
// Default to version 6.0 (mbed).
|
||||||
return transfer(chars, arrayBuffer, imageType, 6, 0);
|
return transfer(chars, arrayBuffer, imageType, 6, 0);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -183,12 +238,14 @@
|
|||||||
log("found packet characteristic");
|
log("found packet characteristic");
|
||||||
packetChar = characteristic;
|
packetChar = characteristic;
|
||||||
service.getCharacteristic(versionUUID)
|
service.getCharacteristic(versionUUID)
|
||||||
|
// Older DFU implementations (from older Nordic SDKs) have no DFU Version characteristic. So this may fail.
|
||||||
.then(characteristic => {
|
.then(characteristic => {
|
||||||
log("found version characteristic");
|
log("found version characteristic");
|
||||||
versionChar = characteristic;
|
versionChar = characteristic;
|
||||||
complete();
|
complete();
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
log("info: no version characteristic found");
|
||||||
complete();
|
complete();
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
@@ -213,9 +270,9 @@
|
|||||||
offset = 0;
|
offset = 0;
|
||||||
|
|
||||||
if (!controlChar.properties.notify) {
|
if (!controlChar.properties.notify) {
|
||||||
var error = "controlChar missing notify property";
|
var err = "controlChar missing notify property";
|
||||||
log(error);
|
log(err);
|
||||||
return reject(error);
|
return reject(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
log("enabling notifications");
|
log("enabling notifications");
|
||||||
@@ -223,7 +280,7 @@
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
controlChar.addEventListener('characteristicvaluechanged', handleControl);
|
controlChar.addEventListener('characteristicvaluechanged', handleControl);
|
||||||
log("sending imagetype: " + imageType);
|
log("sending imagetype: " + imageType);
|
||||||
return controlChar.writeValue(new Uint8Array([1, imageType]));
|
return controlChar.writeValue(new Uint8Array([OPCODE.START_DFU, imageType]));
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
log("sent start");
|
log("sent start");
|
||||||
@@ -234,15 +291,14 @@
|
|||||||
|
|
||||||
var buffer = new ArrayBuffer(12);
|
var buffer = new ArrayBuffer(12);
|
||||||
var view = new DataView(buffer);
|
var view = new DataView(buffer);
|
||||||
view.setUint32(0, softLength, littleEndian);
|
view.setUint32(0, softLength, LITTLE_ENDIAN);
|
||||||
view.setUint32(4, bootLength, littleEndian);
|
view.setUint32(4, bootLength, LITTLE_ENDIAN);
|
||||||
view.setUint32(8, appLength, littleEndian);
|
view.setUint32(8, appLength, LITTLE_ENDIAN);
|
||||||
|
|
||||||
// Set firmware length
|
|
||||||
return packetChar.writeValue(view);
|
return packetChar.writeValue(view);
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
log("sent buffer size: " + arrayBuffer.byteLength);
|
log("sent image size: " + arrayBuffer.byteLength);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
error = "start error: " + error;
|
error = "start error: " + error;
|
||||||
@@ -253,103 +309,112 @@
|
|||||||
function handleControl(event) {
|
function handleControl(event) {
|
||||||
var data = event.target.value;
|
var data = event.target.value;
|
||||||
var view = new DataView(data);
|
var view = new DataView(data);
|
||||||
|
|
||||||
var opCode = view.getUint8(0);
|
var opCode = view.getUint8(0);
|
||||||
|
var req_opcode = view.getUint8(1);
|
||||||
|
var resp_code = view.getUint8(2);
|
||||||
|
|
||||||
if (opCode === 16) { // response
|
if (opCode === OPCODE.RESPONSE_CODE) {
|
||||||
var resp_code = view.getUint8(2);
|
|
||||||
if (resp_code !== 1) {
|
if (resp_code !== 1) {
|
||||||
var error = "error from control: " + resp_code;
|
var err = "error from control point notification, resp_code: " + resp_code;
|
||||||
log(error);
|
log(err);
|
||||||
return reject(error);
|
return reject(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch(req_opcode) {
|
||||||
|
case OPCODE.START_DFU:
|
||||||
|
case OPCODE.INITIALIZE_DFU_PARAMETERS:
|
||||||
|
if(req_opcode === OPCODE.START_DFU && majorVersion > 6) { // init packet is not used in SDK v6 (so not used in mbed).
|
||||||
|
log('write init packet');
|
||||||
|
controlChar.writeValue(new Uint8Array([OPCODE.INITIALIZE_DFU_PARAMETERS, 0]))
|
||||||
|
.then(() => {
|
||||||
|
return packetChar.writeValue(generateInitPacket());
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return controlChar.writeValue(new Uint8Array([OPCODE.INITIALIZE_DFU_PARAMETERS, 1]));
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
error = "error writing dfu init parameters: " + error;
|
||||||
|
log(error);
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
log('send packet count');
|
||||||
|
|
||||||
var req_opcode = view.getUint8(1);
|
var buffer = new ArrayBuffer(3);
|
||||||
if (req_opcode === 1 && majorVersion > 6) {
|
view = new DataView(buffer);
|
||||||
log('write null init packet');
|
view.setUint8(0, OPCODE.PACKET_RECEIPT_NOTIFICATION_REQUEST);
|
||||||
|
view.setUint16(1, interval, LITTLE_ENDIAN);
|
||||||
|
|
||||||
|
controlChar.writeValue(view)
|
||||||
|
.then(() => {
|
||||||
|
log("sent packet count: " + interval);
|
||||||
|
return controlChar.writeValue(new Uint8Array([OPCODE.RECEIVE_FIRMWARE_IMAGE]));
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
log("sent receive");
|
||||||
|
return writePacket(packetChar, arrayBuffer, 0);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
error = "error sending packet count: " + error;
|
||||||
|
log(error);
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case OPCODE.RECEIVE_FIRMWARE_IMAGE:
|
||||||
|
log('check length');
|
||||||
|
|
||||||
controlChar.writeValue(new Uint8Array([2,0]))
|
controlChar.writeValue(new Uint8Array([OPCODE.REPORT_RECEIVED_IMAGE_SIZE]))
|
||||||
.then(() => {
|
.catch(error => {
|
||||||
return packetChar.writeValue(new Uint8Array([0]));
|
error = "error checking length: " + error;
|
||||||
})
|
log(error);
|
||||||
.then(() => {
|
reject(error);
|
||||||
return controlChar.writeValue(new Uint8Array([2,1]));
|
});
|
||||||
})
|
break;
|
||||||
.catch(error => {
|
case OPCODE.REPORT_RECEIVED_IMAGE_SIZE:
|
||||||
error = "error writing init: " + error;
|
var bytesReceived = view.getUint32(3, LITTLE_ENDIAN);
|
||||||
log(error);
|
log('length: ' + bytesReceived);
|
||||||
reject(error);
|
log('validate...');
|
||||||
});
|
|
||||||
|
controlChar.writeValue(new Uint8Array([OPCODE.VALIDATE_FIRMWARE]))
|
||||||
} else if (req_opcode === 1 || req_opcode === 2) {
|
.catch(error => {
|
||||||
log('complete, send packet count');
|
error = "error validating: " + error;
|
||||||
|
log(error);
|
||||||
var buffer = new ArrayBuffer(3);
|
reject(error);
|
||||||
view = new DataView(buffer);
|
});
|
||||||
view.setUint8(0, 8);
|
break;
|
||||||
view.setUint16(1, interval, littleEndian);
|
case OPCODE.VALIDATE_FIRMWARE:
|
||||||
|
log('complete, reset...');
|
||||||
controlChar.writeValue(view)
|
|
||||||
.then(() => {
|
|
||||||
log("sent packet count: " + interval);
|
|
||||||
return controlChar.writeValue(new Uint8Array([3]));
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
log("sent receive");
|
|
||||||
return writePacket(packetChar, arrayBuffer, 0);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
error = "error sending packet count: " + error;
|
|
||||||
log(error);
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
} else if (req_opcode === 3) {
|
|
||||||
log('complete, check length');
|
|
||||||
|
|
||||||
controlChar.writeValue(new Uint8Array([7]))
|
|
||||||
.catch(error => {
|
|
||||||
error = "error checking length: " + error;
|
|
||||||
log(error);
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
} else if (req_opcode === 7) {
|
|
||||||
var byteCount = view.getUint32(3, littleEndian);
|
|
||||||
log('length: ' + byteCount);
|
|
||||||
log('complete, validate...');
|
|
||||||
|
|
||||||
controlChar.writeValue(new Uint8Array([4]))
|
|
||||||
.catch(error => {
|
|
||||||
error = "error validating: " + error;
|
|
||||||
log(error);
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
} else if (req_opcode === 4) {
|
|
||||||
log('complete, reset...');
|
|
||||||
/*
|
/*
|
||||||
// Disconnect event currently not implemented
|
// TODO: Resolve in disconnect event handler when implemented in Web Bluetooth API.
|
||||||
controlChar.service.device.addEventListener("gattserverdisconnected", () => {
|
controlChar.service.device.addEventListener("gattserverdisconnected", () => {
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
controlChar.writeValue(new Uint8Array([5]))
|
|
||||||
.then(() => {
|
|
||||||
// Hack to gracefully disconnect without disconnect event
|
|
||||||
setTimeout(() => {
|
|
||||||
chars.server.disconnect();
|
|
||||||
resolve();
|
resolve();
|
||||||
}, 3000);
|
});
|
||||||
})
|
*/
|
||||||
.catch(error => {
|
controlChar.writeValue(new Uint8Array([OPCODE.ACTIVATE_IMAGE_AND_RESET]))
|
||||||
error = "error resetting: " + error;
|
.then(() => {
|
||||||
log(error);
|
log('image activated and dfu target reset');
|
||||||
reject(error);
|
// Hack to gracefully disconnect without disconnect event
|
||||||
});
|
setTimeout(() => {
|
||||||
|
chars.server.disconnect();
|
||||||
|
resolve();
|
||||||
|
}, 2000);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
error = "error resetting: " + error;
|
||||||
|
log(error);
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log('unexpected req opCode - ERROR');
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (opCode === 17) {
|
} else if (opCode === OPCODE.PACKET_RECEIPT_NOTIFICATION) {
|
||||||
var bytes = view.getUint32(1, littleEndian);
|
var bytes = view.getUint32(1, LITTLE_ENDIAN);
|
||||||
log('transferred: ' + bytes);
|
log('transferred: ' + bytes);
|
||||||
writePacket(packetChar, arrayBuffer, 0);
|
writePacket(packetChar, arrayBuffer, 0);
|
||||||
}
|
}
|
||||||
@@ -383,4 +448,4 @@
|
|||||||
writeMode: writeMode,
|
writeMode: writeMode,
|
||||||
provision: provision
|
provision: provision
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -5,12 +5,30 @@ var fs = require('fs');
|
|||||||
var log = console.log;
|
var log = console.log;
|
||||||
dfu.addLogger(log);
|
dfu.addLogger(log);
|
||||||
|
|
||||||
var fileMask = "firmware/NRF51822_{0}_Rob_OTA.hex";
|
var fileMask = "";
|
||||||
var fileName = null;
|
var fileName = null;
|
||||||
|
|
||||||
|
var deviceType = process.argv[2];
|
||||||
|
if (!deviceType) {
|
||||||
|
deviceType = "nrf51";
|
||||||
|
log("no device-type specified, defaulting to " + deviceType);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(deviceType) {
|
||||||
|
case "nrf51":
|
||||||
|
fileMask = "firmware/nrf51_app_{0}.hex";
|
||||||
|
break;
|
||||||
|
case "nrf52":
|
||||||
|
fileMask = "firmware/nrf52_app.hex";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log("unknown device-type: " + deviceType);
|
||||||
|
process.exit();
|
||||||
|
}
|
||||||
|
|
||||||
dfu.findDevice({ services: [0x180D] })
|
dfu.findDevice({ services: [0x180D] })
|
||||||
.then(device => {
|
.then(device => {
|
||||||
fileName = fileMask.replace("{0}", device.name === "Hi_Rob" ? "Bye" : "Hi");
|
fileName = fileMask.replace("{0}", device.name === "Hi_Rob" ? "bye" : "hi");
|
||||||
log("found device: " + device.name);
|
log("found device: " + device.name);
|
||||||
log("using file name: " + fileName);
|
log("using file name: " + fileName);
|
||||||
|
|
||||||
|
|||||||
@@ -2,19 +2,35 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>web-bluetooth-dfu</title>
|
<title>web-bluetooth-dfu</title>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
|
<style>
|
||||||
|
html, body { width: 100%; padding: 0; margin: 0; font-family: sans-serif; }
|
||||||
|
input { font-size: 20px; }
|
||||||
|
button { font-size: 20px; padding: 0 20px; display: block; margin: 20px auto; }
|
||||||
|
.device-type { width: 160px; margin: 20px auto; white-space: nowrap; }
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<button onclick="setMode()" style="font-size: 20px;">Set Mode</button>
|
<div class="device-type">
|
||||||
<button onclick="findDFU()" style="font-size: 20px;">Transfer</button>
|
<input type="radio" name="device-type" id="nrf51" checked>
|
||||||
<button onclick="both()" style="font-size: 20px;">Both</button>
|
<label for="nrf51">nRF51</label>
|
||||||
|
<input type="radio" name="device-type" id="nrf52">
|
||||||
|
<label for="nrf52">nRF52</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button onclick="setMode()">Set Mode</button>
|
||||||
|
<button onclick="findDFU()">Transfer</button>
|
||||||
|
<button onclick="both()">Both</button>
|
||||||
|
|
||||||
<div id="results"></div>
|
<div id="results"></div>
|
||||||
|
|
||||||
<script src="dist/dfu.js"></script>
|
<script src="dist/dfu.js"></script>
|
||||||
<script src="dist/hex2bin.js"></script>
|
<script src="dist/hex2bin.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var urlMask = "//thegecko.github.io/web-bluetooth-dfu/firmware/NRF51822_{0}_Rob_OTA.hex";
|
|
||||||
var resultsEl = document.getElementById("results");
|
var resultsEl = document.getElementById("results");
|
||||||
|
var urlBase = "//thegecko.github.io/web-bluetooth-dfu/firmware/";
|
||||||
|
|
||||||
function log(message) {
|
function log(message) {
|
||||||
console.log(message);
|
console.log(message);
|
||||||
@@ -35,7 +51,18 @@
|
|||||||
|
|
||||||
function transfer(device) {
|
function transfer(device) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
var url = urlMask.replace("{0}", device.name === "Hi_Rob" ? "Bye" : "Hi");
|
var url = "";
|
||||||
|
var deviceType = document.querySelector('input[name="device-type"]:checked').id;
|
||||||
|
|
||||||
|
switch(deviceType) {
|
||||||
|
case "nrf51":
|
||||||
|
url = urlBase + "nrf51_app_{0}.hex";
|
||||||
|
url = url.replace("{0}", device.name === "Hi_Rob" ? "bye" : "hi");
|
||||||
|
break;
|
||||||
|
case "nrf52":
|
||||||
|
url = urlBase + "nrf52_app.hex";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
download(url)
|
download(url)
|
||||||
.then(hex => {
|
.then(hex => {
|
||||||
|
|||||||
1539
firmware/nrf52_app.hex
Normal file
1539
firmware/nrf52_app.hex
Normal file
File diff suppressed because it is too large
Load Diff
8288
firmware/nrf52_boot_s132.hex
Normal file
8288
firmware/nrf52_boot_s132.hex
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user