Compare commits
10 Commits
1007e2ebd5
...
648331b854
| Author | SHA1 | Date | |
|---|---|---|---|
| 648331b854 | |||
|
|
678b1e3aa7 | ||
|
|
02c8dcfc9d | ||
|
|
499efb89ba | ||
|
|
73a9c02ada | ||
|
|
880b4a1126 | ||
|
|
f73fe58b14 | ||
|
|
fe9d0df1be | ||
|
|
d44338bc76 | ||
|
|
74e02504b4 |
1526
index.html
1526
index.html
File diff suppressed because it is too large
Load Diff
246
js/rnode.js
246
js/rnode.js
@@ -79,6 +79,16 @@ class RNode {
|
|||||||
ROM_UNLOCK_BYTE = 0xF8;
|
ROM_UNLOCK_BYTE = 0xF8;
|
||||||
CMD_HASHES = 0x60;
|
CMD_HASHES = 0x60;
|
||||||
CMD_FW_UPD = 0x61;
|
CMD_FW_UPD = 0x61;
|
||||||
|
CMD_DISP_ROT = 0x67;
|
||||||
|
CMD_DISP_INT = 0x45;
|
||||||
|
CMD_DISP_BLNK = 0x64;
|
||||||
|
CMD_DISP_RCND = 0x68;
|
||||||
|
CMD_WIFI_MODE = 0x6A;
|
||||||
|
CMD_WIFI_SSID = 0x6B;
|
||||||
|
CMD_WIFI_PSK = 0x6C;
|
||||||
|
CMD_WIFI_CHN = 0x6E;
|
||||||
|
CMD_WIFI_IP = 0x84;
|
||||||
|
CMD_WIFI_NM = 0x85;
|
||||||
|
|
||||||
CMD_BT_CTRL = 0x46;
|
CMD_BT_CTRL = 0x46;
|
||||||
CMD_BT_PIN = 0x62;
|
CMD_BT_PIN = 0x62;
|
||||||
@@ -144,14 +154,14 @@ class RNode {
|
|||||||
try {
|
try {
|
||||||
this.reader.releaseLock();
|
this.reader.releaseLock();
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.log("failed to release lock on serial port readable, ignoring...", e);
|
//console.log("failed to release lock on serial port readable, ignoring...", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// close serial port
|
// close serial port
|
||||||
try {
|
try {
|
||||||
await this.serialPort.close();
|
await this.serialPort.close();
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.log("failed to close serial port, ignoring...", e);
|
//console.log("failed to close serial port, ignoring...", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -608,6 +618,185 @@ class RNode {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async enableWiFiMode(wifiMode) {
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_WIFI_MODE,
|
||||||
|
wifiMode, // 0x00 = OFF, 0x01 = AP, 0x02 = STATION
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async disableWiFiMode() {
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_WIFI_MODE,
|
||||||
|
0x00, // OFF
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setWiFiChannel(wifiChannel) {
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_WIFI_CHN,
|
||||||
|
wifiChannel, // 1-14
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setWiFiSSID(wifiSSID) {
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const ssidBytes = encoder.encode(wifiSSID);
|
||||||
|
|
||||||
|
// Add null terminator
|
||||||
|
const data = new Uint8Array(ssidBytes.length + 1);
|
||||||
|
data.set(ssidBytes);
|
||||||
|
data[data.length - 1] = 0x00;
|
||||||
|
|
||||||
|
// KISS escape
|
||||||
|
const escaped = [];
|
||||||
|
for (const byte of data) {
|
||||||
|
if (byte === this.FEND) {
|
||||||
|
escaped.push(this.FESC, this.TFEND);
|
||||||
|
} else if (byte === this.FESC) {
|
||||||
|
escaped.push(this.FESC, this.TFESC);
|
||||||
|
} else {
|
||||||
|
escaped.push(byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send command
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_WIFI_SSID,
|
||||||
|
...escaped,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setWiFiPSK(wifiPSK) {
|
||||||
|
if (wifiPSK == null) {
|
||||||
|
// Clear PSK
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_WIFI_PSK,
|
||||||
|
0x00
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const pskBytes = encoder.encode(wifiPSK);
|
||||||
|
|
||||||
|
// Enforce firmware length rules (8–32 characters)
|
||||||
|
if (pskBytes.length < 8 || pskBytes.length > 32) {
|
||||||
|
throw new Error("Invalid PSK length (must be 8–32 bytes)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add null terminator
|
||||||
|
const data = new Uint8Array(pskBytes.length + 1);
|
||||||
|
data.set(pskBytes);
|
||||||
|
data[data.length - 1] = 0x00;
|
||||||
|
|
||||||
|
// KISS escape
|
||||||
|
const escaped = [];
|
||||||
|
for (const byte of data) {
|
||||||
|
if (byte === this.FEND) {
|
||||||
|
escaped.push(this.FESC, this.TFEND);
|
||||||
|
} else if (byte === this.FESC) {
|
||||||
|
escaped.push(this.FESC, this.TFESC);
|
||||||
|
} else {
|
||||||
|
escaped.push(byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send command
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_WIFI_PSK,
|
||||||
|
...escaped,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setWiFiIP(wifiIP) {
|
||||||
|
if (wifiIP == null) {
|
||||||
|
// Clear IP → 0.0.0.0
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_WIFI_IP,
|
||||||
|
0x00, 0x00, 0x00, 0x00
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof wifiIP !== "string") {
|
||||||
|
throw new TypeError("Invalid IP address (not a string)");
|
||||||
|
}
|
||||||
|
|
||||||
|
const octets = wifiIP.split(".");
|
||||||
|
if (octets.length !== 4) {
|
||||||
|
throw new Error("Invalid IP address length");
|
||||||
|
}
|
||||||
|
|
||||||
|
const ipBytes = new Uint8Array(4);
|
||||||
|
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
const octet = Number(octets[i]);
|
||||||
|
|
||||||
|
if (!Number.isInteger(octet) || octet < 0 || octet > 255) {
|
||||||
|
throw new Error("Invalid IP octet value");
|
||||||
|
}
|
||||||
|
|
||||||
|
ipBytes[i] = octet;
|
||||||
|
}
|
||||||
|
|
||||||
|
// KISS escape (same as PSK logic)
|
||||||
|
const escaped = [];
|
||||||
|
for (const byte of ipBytes) {
|
||||||
|
if (byte === this.KISS_FEND) {
|
||||||
|
escaped.push(this.KISS_FESC, this.KISS_TFEND);
|
||||||
|
} else if (byte === this.KISS_FESC) {
|
||||||
|
escaped.push(this.KISS_FESC, this.KISS_TFESC);
|
||||||
|
} else {
|
||||||
|
escaped.push(byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send command exactly like PSK does
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_WIFI_IP,
|
||||||
|
...escaped,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setWiFiNM(wifiNM) {
|
||||||
|
if (wifiNM == null) {
|
||||||
|
// Clear netmask → 0.0.0.0
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_WIFI_NM,
|
||||||
|
0x00, 0x00, 0x00, 0x00
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof wifiNM !== "string") {
|
||||||
|
throw new TypeError("Invalid netmask (not a string)");
|
||||||
|
}
|
||||||
|
|
||||||
|
const octets = wifiNM.split(".");
|
||||||
|
if (octets.length !== 4) {
|
||||||
|
throw new Error("Invalid netmask length");
|
||||||
|
}
|
||||||
|
|
||||||
|
const nmBytes = new Uint8Array(4);
|
||||||
|
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
const octet = Number(octets[i]);
|
||||||
|
|
||||||
|
if (!Number.isInteger(octet) || octet < 0 || octet > 255) {
|
||||||
|
throw new Error("Invalid netmask octet value");
|
||||||
|
}
|
||||||
|
|
||||||
|
nmBytes[i] = octet;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send using same KISS pipeline as PSK/IP
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_WIFI_NM,
|
||||||
|
...nmBytes
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
async readDisplay() {
|
async readDisplay() {
|
||||||
|
|
||||||
const response = await this.sendCommand(this.CMD_DISP_READ, [
|
const response = await this.sendCommand(this.CMD_DISP_READ, [
|
||||||
@@ -750,6 +939,34 @@ class RNode {
|
|||||||
return new ROM(rom);
|
return new ROM(rom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async setDisplayRotation(rotation) {
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_DISP_ROT,
|
||||||
|
rotation & 0xFF,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setDisplayIntensity(intensity) {
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_DISP_INT,
|
||||||
|
intensity & 0xFF,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setDisplayTimeout(timeout) {
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_DISP_BLNK,
|
||||||
|
timeout & 0xFF,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async startDisplayReconditioning() {
|
||||||
|
await this.sendKissCommand([
|
||||||
|
this.CMD_DISP_RCND,
|
||||||
|
0x01,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ROM {
|
class ROM {
|
||||||
@@ -778,6 +995,7 @@ class ROM {
|
|||||||
static MODEL_A7 = 0xA7
|
static MODEL_A7 = 0xA7
|
||||||
static MODEL_A5 = 0xA5;
|
static MODEL_A5 = 0xA5;
|
||||||
static MODEL_AA = 0xAA;
|
static MODEL_AA = 0xAA;
|
||||||
|
static MODEL_AC = 0xAC;
|
||||||
|
|
||||||
static PRODUCT_T32_10 = 0xB2
|
static PRODUCT_T32_10 = 0xB2
|
||||||
static MODEL_BA = 0xBA
|
static MODEL_BA = 0xBA
|
||||||
@@ -801,6 +1019,9 @@ class ROM {
|
|||||||
static MODEL_C5 = 0xC5
|
static MODEL_C5 = 0xC5
|
||||||
static MODEL_CA = 0xCA
|
static MODEL_CA = 0xCA
|
||||||
|
|
||||||
|
static PRODUCT_H32_V4 = 0xC3
|
||||||
|
static MODEL_C8 = 0xC8
|
||||||
|
|
||||||
static PRODUCT_HELTEC_T114 = 0xC2
|
static PRODUCT_HELTEC_T114 = 0xC2
|
||||||
static MODEL_C6 = 0xC6
|
static MODEL_C6 = 0xC6
|
||||||
static MODEL_C7 = 0xC7
|
static MODEL_C7 = 0xC7
|
||||||
@@ -820,8 +1041,8 @@ class ROM {
|
|||||||
static MODEL_D9 = 0xD9;
|
static MODEL_D9 = 0xD9;
|
||||||
|
|
||||||
static PRODUCT_TECHO = 0x15;
|
static PRODUCT_TECHO = 0x15;
|
||||||
static MODEL_T4 = 0x16;
|
static MODEL_16 = 0x16;
|
||||||
static MODEL_T9 = 0x17;
|
static MODEL_17 = 0x17;
|
||||||
|
|
||||||
static PRODUCT_HMBRW = 0xF0
|
static PRODUCT_HMBRW = 0xF0
|
||||||
static MODEL_FF = 0xFF
|
static MODEL_FF = 0xFF
|
||||||
@@ -835,12 +1056,29 @@ class ROM {
|
|||||||
static ADDR_CHKSUM = 0x0B
|
static ADDR_CHKSUM = 0x0B
|
||||||
static ADDR_SIGNATURE = 0x1B
|
static ADDR_SIGNATURE = 0x1B
|
||||||
static ADDR_INFO_LOCK = 0x9B
|
static ADDR_INFO_LOCK = 0x9B
|
||||||
|
|
||||||
static ADDR_CONF_SF = 0x9C
|
static ADDR_CONF_SF = 0x9C
|
||||||
static ADDR_CONF_CR = 0x9D
|
static ADDR_CONF_CR = 0x9D
|
||||||
static ADDR_CONF_TXP = 0x9E
|
static ADDR_CONF_TXP = 0x9E
|
||||||
static ADDR_CONF_BW = 0x9F
|
static ADDR_CONF_BW = 0x9F
|
||||||
static ADDR_CONF_FREQ = 0xA3
|
static ADDR_CONF_FREQ = 0xA3
|
||||||
static ADDR_CONF_OK = 0xA7
|
static ADDR_CONF_OK = 0xA7
|
||||||
|
static ADDR_CONF_BT = 0xB0
|
||||||
|
static ADDR_CONF_DSET = 0xB1
|
||||||
|
static ADDR_CONF_DINT = 0xB2
|
||||||
|
static ADDR_CONF_DADR = 0xB3
|
||||||
|
static ADDR_CONF_DBLK = 0xB4
|
||||||
|
static ADDR_CONF_DROT = 0xB8
|
||||||
|
static ADDR_CONF_PSET = 0xB5
|
||||||
|
static ADDR_CONF_PINT = 0xB6
|
||||||
|
static ADDR_CONF_BSET = 0xB7
|
||||||
|
static ADDR_CONF_DIA = 0xB9
|
||||||
|
static ADDR_CONF_WIFI = 0xBA
|
||||||
|
static ADDR_CONF_WCHN = 0xBB
|
||||||
|
static ADDR_CONF_SSID = 0x00
|
||||||
|
static ADDR_CONF_PSK = 0x21
|
||||||
|
static ADDR_CONF_IP = 0x42
|
||||||
|
static ADDR_CONF_NM = 0x46
|
||||||
|
|
||||||
static INFO_LOCK_BYTE = 0x73
|
static INFO_LOCK_BYTE = 0x73
|
||||||
static CONF_OK_BYTE = 0x73
|
static CONF_OK_BYTE = 0x73
|
||||||
|
|||||||
Reference in New Issue
Block a user