start implementing rnode management over web serial
This commit is contained in:
81
index.html
Normal file
81
index.html
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>RNode Flasher</title>
|
||||||
|
|
||||||
|
<script src="./rnode.js"></script>
|
||||||
|
<script src="https://unpkg.com/vue@3"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="app">
|
||||||
|
<button @click="connect">Connect</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
Vue.createApp({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async connect() {
|
||||||
|
|
||||||
|
if(!navigator.serial){
|
||||||
|
alert("Serial is not supported in this browser");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ask user to select device
|
||||||
|
const serialPort = await navigator.serial.requestPort({
|
||||||
|
filters: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
await serialPort.open({
|
||||||
|
baudRate: RNode.BAUD_RATE,
|
||||||
|
});
|
||||||
|
|
||||||
|
const rnode = RNode.fromSerialPort(serialPort);
|
||||||
|
|
||||||
|
if(!await rnode.detect()){
|
||||||
|
console.log("device is not an rnode");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// dump node info
|
||||||
|
console.log({
|
||||||
|
firmware_version: await rnode.getFirmwareVersion(),
|
||||||
|
platform: await rnode.getPlatform(),
|
||||||
|
mcu: await rnode.getMcu(),
|
||||||
|
board: await rnode.getBoard(),
|
||||||
|
device_hash: await rnode.getDeviceHash(),
|
||||||
|
firmware_hash_target: await rnode.getTargetFirmwareHash(),
|
||||||
|
firmware_hash: await rnode.getFirmwareHash(),
|
||||||
|
// rom: await rnode.getRom(),
|
||||||
|
frequency: await rnode.getFrequency(),
|
||||||
|
bandwidth: await rnode.getBandwidth(),
|
||||||
|
tx_power: await rnode.getTxPower(),
|
||||||
|
spreading_factor: await rnode.getSpreadingFactor(),
|
||||||
|
coding_rate: await rnode.getCodingRate(),
|
||||||
|
radio_state: await rnode.getRadioState(),
|
||||||
|
rx_stat: await rnode.getRxStat(),
|
||||||
|
tx_stat: await rnode.getTxStat(),
|
||||||
|
rssi_stat: await rnode.getRssiStat(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// console.log(response.map(x => x.toString(16).padStart(2, '0')).join(''));
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).mount('#app');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
474
rnode.js
Normal file
474
rnode.js
Normal file
@@ -0,0 +1,474 @@
|
|||||||
|
class RNode {
|
||||||
|
|
||||||
|
static BAUD_RATE = 115200;
|
||||||
|
|
||||||
|
KISS_FEND = 0xC0;
|
||||||
|
KISS_FESC = 0xDB;
|
||||||
|
KISS_TFEND = 0xDC;
|
||||||
|
KISS_TFESC = 0xDD;
|
||||||
|
|
||||||
|
CMD_FREQUENCY = 0x01;
|
||||||
|
CMD_BANDWIDTH = 0x02;
|
||||||
|
CMD_TXPOWER = 0x03;
|
||||||
|
CMD_SF = 0x04;
|
||||||
|
CMD_CR = 0x05;
|
||||||
|
CMD_RADIO_STATE = 0x06;
|
||||||
|
|
||||||
|
CMD_STAT_RX = 0x21;
|
||||||
|
CMD_STAT_TX = 0x22
|
||||||
|
CMD_STAT_RSSI = 0x23;
|
||||||
|
CMD_STAT_SNR = 0x24;
|
||||||
|
|
||||||
|
CMD_BOARD = 0x47;
|
||||||
|
CMD_PLATFORM = 0x48;
|
||||||
|
CMD_MCU = 0x49;
|
||||||
|
CMD_RESET = 0x55;
|
||||||
|
CMD_RESET_BYTE = 0xF8;
|
||||||
|
CMD_DEV_HASH = 0x56;
|
||||||
|
CMD_FW_VERSION = 0x50;
|
||||||
|
CMD_ROM_READ = 0x51;
|
||||||
|
CMD_CONF_SAVE = 0x53;
|
||||||
|
CMD_CONF_DELETE = 0x54;
|
||||||
|
CMD_HASHES = 0x60;
|
||||||
|
|
||||||
|
CMD_BT_CTRL = 0x46;
|
||||||
|
CMD_BT_PIN = 0x62;
|
||||||
|
|
||||||
|
CMD_DETECT = 0x08;
|
||||||
|
|
||||||
|
DETECT_REQ = 0x73;
|
||||||
|
DETECT_RESP = 0x46;
|
||||||
|
|
||||||
|
PLATFORM_AVR = 0x90;
|
||||||
|
PLATFORM_ESP32 = 0x80;
|
||||||
|
PLATFORM_NRF52 = 0x70;
|
||||||
|
|
||||||
|
MCU_1284P = 0x91;
|
||||||
|
MCU_2560 = 0x92;
|
||||||
|
MCU_ESP32 = 0x81;
|
||||||
|
MCU_NRF52 = 0x71;
|
||||||
|
|
||||||
|
BOARD_RNODE = 0x31;
|
||||||
|
BOARD_HMBRW = 0x32;
|
||||||
|
BOARD_TBEAM = 0x33;
|
||||||
|
BOARD_HUZZAH32 = 0x34;
|
||||||
|
BOARD_GENERIC_ESP32 = 0x35;
|
||||||
|
BOARD_LORA32_V2_0 = 0x36;
|
||||||
|
BOARD_LORA32_V2_1 = 0x37;
|
||||||
|
BOARD_RAK4631 = 0x51;
|
||||||
|
|
||||||
|
HASH_TYPE_TARGET_FIRMWARE = 0x01;
|
||||||
|
HASH_TYPE_FIRMWARE = 0x02;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.readable = null;
|
||||||
|
this.writable = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromSerialPort(port) {
|
||||||
|
const rnode = new RNode();
|
||||||
|
rnode.readable = port.readable;
|
||||||
|
rnode.writable = port.writable;
|
||||||
|
return rnode;
|
||||||
|
}
|
||||||
|
|
||||||
|
async write(bytes) {
|
||||||
|
const writer = this.writable.getWriter();
|
||||||
|
try {
|
||||||
|
await writer.write(new Uint8Array(bytes));
|
||||||
|
} finally {
|
||||||
|
writer.releaseLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async readFromSerialPort() {
|
||||||
|
const reader = this.readable.getReader();
|
||||||
|
try {
|
||||||
|
let buffer = [];
|
||||||
|
while(true){
|
||||||
|
const { value, done } = await reader.read();
|
||||||
|
if(done){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(value){
|
||||||
|
for(let byte of value){
|
||||||
|
buffer.push(byte);
|
||||||
|
if(byte === this.KISS_FEND){
|
||||||
|
if(buffer.length > 1){
|
||||||
|
return this.handleKISSFrame(buffer);
|
||||||
|
}
|
||||||
|
buffer = [this.KISS_FEND]; // Start new frame
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error reading from serial port: ', error);
|
||||||
|
} finally {
|
||||||
|
reader.releaseLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKISSFrame(frame) {
|
||||||
|
|
||||||
|
let data = [];
|
||||||
|
let escaping = false;
|
||||||
|
|
||||||
|
// Skip the initial 0xC0 and process the rest
|
||||||
|
for(let i = 1; i < frame.length; i++){
|
||||||
|
let byte = frame[i];
|
||||||
|
if (escaping) {
|
||||||
|
if (byte === this.KISS_TFEND) {
|
||||||
|
data.push(this.KISS_FEND);
|
||||||
|
} else if (byte === this.KISS_TFESC) {
|
||||||
|
data.push(this.KISS_FESC);
|
||||||
|
}
|
||||||
|
escaping = false;
|
||||||
|
} else {
|
||||||
|
if (byte === this.KISS_FESC) {
|
||||||
|
escaping = true;
|
||||||
|
} else if (byte === this.KISS_FEND) {
|
||||||
|
// Ignore the end frame delimiter
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
data.push(byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Received KISS frame data:', new Uint8Array(data));
|
||||||
|
return data;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
createKissFrame(data) {
|
||||||
|
let frame = [this.KISS_FEND];
|
||||||
|
for(let byte of data){
|
||||||
|
if(byte === this.KISS_FEND){
|
||||||
|
frame.push(this.KISS_FESC, this.KISS_TFEND);
|
||||||
|
} else if(byte === this.KISS_FESC){
|
||||||
|
frame.push(this.KISS_FESC, this.KISS_TFESC);
|
||||||
|
} else {
|
||||||
|
frame.push(byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frame.push(this.KISS_FEND);
|
||||||
|
return new Uint8Array(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendKissCommand(data) {
|
||||||
|
await this.write(this.createKissFrame(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
async reset() {
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_RESET,
|
||||||
|
this.CMD_RESET_BYTE,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async detect() {
|
||||||
|
|
||||||
|
// ask if device is rnode
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_DETECT,
|
||||||
|
this.DETECT_REQ,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// read response from device
|
||||||
|
const [ command, responseByte ] = await this.readFromSerialPort();
|
||||||
|
|
||||||
|
// device is an rnode if response is as expected
|
||||||
|
return command === this.CMD_DETECT && responseByte === this.DETECT_RESP;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFirmwareVersion() {
|
||||||
|
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_FW_VERSION,
|
||||||
|
0x00,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// read response from device
|
||||||
|
var [ command, majorVersion, minorVersion ] = await this.readFromSerialPort();
|
||||||
|
if(minorVersion.length === 1){
|
||||||
|
minorVersion = "0" + minorVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.23
|
||||||
|
return majorVersion + "." + minorVersion;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPlatform() {
|
||||||
|
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_PLATFORM,
|
||||||
|
0x00,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// read response from device
|
||||||
|
const [ command, platformByte ] = await this.readFromSerialPort();
|
||||||
|
return platformByte;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async getMcu() {
|
||||||
|
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_MCU,
|
||||||
|
0x00,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// read response from device
|
||||||
|
const [ command, mcuByte ] = await this.readFromSerialPort();
|
||||||
|
return mcuByte;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async getBoard() {
|
||||||
|
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_BOARD,
|
||||||
|
0x00,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// read response from device
|
||||||
|
const [ command, boardByte ] = await this.readFromSerialPort();
|
||||||
|
return boardByte;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async getDeviceHash() {
|
||||||
|
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_DEV_HASH,
|
||||||
|
0x01, // anything != 0x00
|
||||||
|
]);
|
||||||
|
|
||||||
|
// read response from device
|
||||||
|
const [ command, ...deviceHash ] = await this.readFromSerialPort();
|
||||||
|
return deviceHash;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTargetFirmwareHash() {
|
||||||
|
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_HASHES,
|
||||||
|
this.HASH_TYPE_TARGET_FIRMWARE,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// read response from device
|
||||||
|
const [ command, hashType, ...targetFirmwareHash ] = await this.readFromSerialPort();
|
||||||
|
return targetFirmwareHash;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFirmwareHash() {
|
||||||
|
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_HASHES,
|
||||||
|
this.HASH_TYPE_FIRMWARE,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// read response from device
|
||||||
|
const [ command, hashType, ...firmwareHash ] = await this.readFromSerialPort();
|
||||||
|
return firmwareHash;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRom() {
|
||||||
|
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_ROM_READ,
|
||||||
|
0x00,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// read response from device
|
||||||
|
const [ command, ...eepromBytes ] = await this.readFromSerialPort();
|
||||||
|
return eepromBytes;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFrequency() {
|
||||||
|
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_FREQUENCY,
|
||||||
|
// request frequency by sending zero as 4 bytes
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// read response from device
|
||||||
|
const [ command, ...frequencyBytes ] = await this.readFromSerialPort();
|
||||||
|
|
||||||
|
// convert 4 bytes to 32bit integer representing frequency in hertz
|
||||||
|
const frequencyInHz = frequencyBytes[0] << 24 | frequencyBytes[1] << 16 | frequencyBytes[2] << 8 | frequencyBytes[3];
|
||||||
|
return frequencyInHz;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async getBandwidth() {
|
||||||
|
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_BANDWIDTH,
|
||||||
|
// request bandwidth by sending zero as 4 bytes
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// read response from device
|
||||||
|
const [ command, ...bandwidthBytes ] = await this.readFromSerialPort();
|
||||||
|
|
||||||
|
// convert 4 bytes to 32bit integer representing bandwidth in hertz
|
||||||
|
const bandwidthInHz = bandwidthBytes[0] << 24 | bandwidthBytes[1] << 16 | bandwidthBytes[2] << 8 | bandwidthBytes[3];
|
||||||
|
return bandwidthInHz;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTxPower() {
|
||||||
|
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_TXPOWER,
|
||||||
|
0xFF, // request tx power
|
||||||
|
]);
|
||||||
|
|
||||||
|
// read response from device
|
||||||
|
const [ command, txPower ] = await this.readFromSerialPort();
|
||||||
|
|
||||||
|
return txPower;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async getSpreadingFactor() {
|
||||||
|
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_SF,
|
||||||
|
0xFF, // request spreading factor
|
||||||
|
]);
|
||||||
|
|
||||||
|
// read response from device
|
||||||
|
const [ command, spreadingFactor ] = await this.readFromSerialPort();
|
||||||
|
|
||||||
|
return spreadingFactor;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCodingRate() {
|
||||||
|
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_CR,
|
||||||
|
0xFF, // request coding rate
|
||||||
|
]);
|
||||||
|
|
||||||
|
// read response from device
|
||||||
|
const [ command, codingRate ] = await this.readFromSerialPort();
|
||||||
|
|
||||||
|
return codingRate;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRadioState() {
|
||||||
|
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_RADIO_STATE,
|
||||||
|
0xFF, // request radio state
|
||||||
|
]);
|
||||||
|
|
||||||
|
// read response from device
|
||||||
|
const [ command, radioState ] = await this.readFromSerialPort();
|
||||||
|
|
||||||
|
return radioState;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRxStat() {
|
||||||
|
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_STAT_RX,
|
||||||
|
0x00,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// read response from device
|
||||||
|
const [ command, ...statBytes ] = await this.readFromSerialPort();
|
||||||
|
|
||||||
|
// convert 4 bytes to 32bit integer
|
||||||
|
const stat = statBytes[0] << 24 | statBytes[1] << 16 | statBytes[2] << 8 | statBytes[3];
|
||||||
|
return stat;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTxStat() {
|
||||||
|
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_STAT_TX,
|
||||||
|
0x00,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// read response from device
|
||||||
|
const [ command, ...statBytes ] = await this.readFromSerialPort();
|
||||||
|
|
||||||
|
// convert 4 bytes to 32bit integer
|
||||||
|
const stat = statBytes[0] << 24 | statBytes[1] << 16 | statBytes[2] << 8 | statBytes[3];
|
||||||
|
return stat;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRssiStat() {
|
||||||
|
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_STAT_RSSI,
|
||||||
|
0x00,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// read response from device
|
||||||
|
const [ command, rssi ] = await this.readFromSerialPort();
|
||||||
|
|
||||||
|
return rssi;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async disableBluetooth() {
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_BT_CTRL,
|
||||||
|
0x00, // stop
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async enableBluetooth() {
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_BT_CTRL,
|
||||||
|
0x01, // start
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async startBluetoothPairing() {
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_BT_CTRL,
|
||||||
|
0x02, // enable pairing
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// setTNCMode
|
||||||
|
async saveConfig() {
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_CONF_SAVE,
|
||||||
|
0x00,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// setNormalMode
|
||||||
|
async deleteConfig() {
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_CONF_DELETE,
|
||||||
|
0x00,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user