diff --git a/index.html b/index.html
index e6dcb92..3db1ded 100644
--- a/index.html
+++ b/index.html
@@ -60,6 +60,20 @@
+
+
4. Provision eeprom with device info, checksum and signature.
+
+
+
+
+
0. Extra Tools
+
+
+
diff --git a/rnode.js b/rnode.js
index 52ddf41..587688e 100644
--- a/rnode.js
+++ b/rnode.js
@@ -1,3 +1,49 @@
+class Utils {
+
+ /**
+ * Waits for the provided milliseconds, and then resolves.
+ * @param millis
+ * @returns {Promise}
+ */
+ static async sleepMillis(millis) {
+ await new Promise((resolve) => {
+ setTimeout(resolve, millis);
+ });
+ }
+
+ static bytesToHex(bytes) {
+ for(var hex = [], i = 0; i < bytes.length; i++){
+ var current = bytes[i] < 0 ? bytes[i] + 256 : bytes[i];
+ hex.push((current >>> 4).toString(16));
+ hex.push((current & 0xF).toString(16));
+ }
+ return hex.join("");
+ }
+
+ static md5(data) {
+ var bytes = [];
+ const hash = CryptoJS.MD5(CryptoJS.enc.Hex.parse(this.bytesToHex(data)));
+ for(var i = 0; i < hash.sigBytes; i++){
+ bytes.push((hash.words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff);
+ }
+ return bytes;
+ }
+
+ static packUInt32BE(value) {
+ const buffer = new ArrayBuffer(4);
+ const view = new DataView(buffer);
+ view.setUint32(0, value, false);
+ return new Uint8Array(buffer);
+ }
+
+ static unpackUInt32BE(byteArray) {
+ const buffer = new Uint8Array(byteArray).buffer;
+ const view = new DataView(buffer);
+ return view.getUint32(0, false);
+ }
+
+}
+
class RNode {
KISS_FEND = 0xC0;
@@ -25,18 +71,31 @@ class RNode {
CMD_DEV_HASH = 0x56;
CMD_FW_VERSION = 0x50;
CMD_ROM_READ = 0x51;
+ CMD_ROM_WRITE = 0x52;
CMD_CONF_SAVE = 0x53;
CMD_CONF_DELETE = 0x54;
+ CMD_FW_HASH = 0x58;
+ CMD_UNLOCK_ROM = 0x59;
+ ROM_UNLOCK_BYTE = 0xF8;
CMD_HASHES = 0x60;
+ CMD_FW_UPD = 0x61;
CMD_BT_CTRL = 0x46;
CMD_BT_PIN = 0x62;
CMD_DETECT = 0x08;
-
DETECT_REQ = 0x73;
DETECT_RESP = 0x46;
+ RADIO_STATE_OFF = 0x00;
+ RADIO_STATE_ON = 0x01;
+ RADIO_STATE_ASK = 0xFF;
+
+ CMD_ERROR = 0x90
+ ERROR_INITRADIO = 0x01
+ ERROR_TXFAILED = 0x02
+ ERROR_EEPROM_LOCKED = 0x03
+
PLATFORM_AVR = 0x90;
PLATFORM_ESP32 = 0x80;
PLATFORM_NRF52 = 0x70;
@@ -462,6 +521,75 @@ class RNode {
]);
}
+ async setFrequency(frequencyInHz) {
+
+ const c1 = frequencyInHz >> 24;
+ const c2 = frequencyInHz >> 16 & 0xFF;
+ const c3 = frequencyInHz >> 8 & 0xFF;
+ const c4 = frequencyInHz & 0xFF;
+
+ await this.sendKissCommand([
+ this.CMD_FREQUENCY,
+ c1,
+ c2,
+ c3,
+ c4,
+ ]);
+
+ }
+
+ async setBandwidth(bandwidthInHz) {
+
+ const c1 = bandwidthInHz >> 24;
+ const c2 = bandwidthInHz >> 16 & 0xFF;
+ const c3 = bandwidthInHz >> 8 & 0xFF;
+ const c4 = bandwidthInHz & 0xFF;
+
+ await this.sendKissCommand([
+ this.CMD_BANDWIDTH,
+ c1,
+ c2,
+ c3,
+ c4,
+ ]);
+
+ }
+
+ async setTxPower(db) {
+ await this.sendKissCommand([
+ this.CMD_TXPOWER,
+ db,
+ ]);
+ }
+
+ async setSpreadingFactor(spreadingFactor) {
+ await this.sendKissCommand([
+ this.CMD_SF,
+ spreadingFactor,
+ ]);
+ }
+
+ async setCodingRate(codingRate) {
+ await this.sendKissCommand([
+ this.CMD_CR,
+ codingRate,
+ ]);
+ }
+
+ async setRadioStateOn() {
+ await this.sendKissCommand([
+ this.CMD_RADIO_STATE,
+ this.RADIO_STATE_ON,
+ ]);
+ }
+
+ async setRadioStateOff() {
+ await this.sendKissCommand([
+ this.CMD_RADIO_STATE,
+ this.RADIO_STATE_OFF,
+ ]);
+ }
+
// setTNCMode
async saveConfig() {
await this.sendKissCommand([
@@ -478,6 +606,46 @@ class RNode {
]);
}
+ async indicateFirmwareUpdate() {
+ await this.sendKissCommand([
+ this.CMD_FW_UPD,
+ 0x01,
+ ]);
+ }
+
+ async setFirmwareHash(hash) {
+ await this.sendKissCommand([
+ this.CMD_FW_HASH,
+ ...hash,
+ ]);
+ }
+
+ async writeRom(address, value) {
+
+ // write to rom
+ await this.sendKissCommand([
+ this.CMD_ROM_WRITE,
+ address,
+ value,
+ ]);
+
+ // wait a bit to allow device to write to rom
+ await Utils.sleepMillis(85);
+
+ }
+
+ async wipeRom() {
+
+ await this.sendKissCommand([
+ this.CMD_UNLOCK_ROM,
+ this.ROM_UNLOCK_BYTE,
+ ]);
+
+ // wiping can take up to 30 seconds
+ await Utils.sleepMillis(30000);
+
+ }
+
async getRomAsObject() {
const rom = await this.getRom();
return new ROM(rom);
@@ -622,12 +790,12 @@ class ROM {
}
getCalculatedChecksum() {
- return this.md5([
+ return Utils.md5([
this.getProduct(),
this.getModel(),
this.getHardwareRevision(),
- this.getSerialNumber(),
- this.getMade(),
+ ...this.getSerialNumber(),
+ ...this.getMade(),
]);
}
@@ -665,15 +833,6 @@ class ROM {
return this.eeprom[ROM.ADDR_CONF_OK] === ROM.CONF_OK_BYTE;
}
- md5(data) {
- var bytes = [];
- const hash = CryptoJS.MD5(data);
- for(var i = 0; i < hash.sigBytes; i++){
- bytes.push((hash.words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff);
- }
- return bytes;
- }
-
parse() {
// ensure info lock byte is set
@@ -681,18 +840,23 @@ class ROM {
return null;
}
- // parse expected details
+ // convert to hex
+ const checksumHex = Utils.bytesToHex(this.getChecksum());
+ const calculatedChecksumHex = Utils.bytesToHex(this.getCalculatedChecksum());
+ const signatureHex = Utils.bytesToHex(this.getSignature());
+
+ // add details
var details = {
is_provisioned: true,
is_configured: this.isConfigured(),
product: this.getProduct(),
model: this.getModel(),
hardware_revision: this.getHardwareRevision(),
- serial_number: this.getSerialNumber(),
- made: this.getMade(),
- checksum: this.getChecksum(),
- signature: this.getSignature(),
- calculated_checksum: this.getCalculatedChecksum(),
+ serial_number: Utils.unpackUInt32BE(this.getSerialNumber()),
+ made: Utils.unpackUInt32BE(this.getMade()),
+ checksum: checksumHex,
+ calculated_checksum: calculatedChecksumHex,
+ signature: signatureHex,
}
// if configured, add configuration to details
@@ -707,7 +871,7 @@ class ROM {
};
}
- // if checksum in eeprom does not match calculated checksum, it is not provisioned
+ // if checksum in eeprom does not match checksum calculated from info, it is not provisioned
if(details.checksum !== details.calculated_checksum){
details.is_provisioned = false;
}