Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,52 @@

</div>

<div class="border bg-gray-50 rounded shadow">

<div class="border-b px-2 py-1">
Configure Wi-Fi (optional)
</div>

<div class="p-3">

<div class="flex mb-1 space-x-1">
<div class="min-w-[125px] my-auto text-right">SSID</div>
<input v-model="wifiSSID" maxlength="33" type="text" class="w-full sm:w-min sm:min-w-[250px] bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block px-2">
</div>
<div class="flex mb-1 space-x-1">
<div class="min-w-[125px] my-auto text-right">PSK</div>
<input v-model="wifiPSK" @input="validatePSK(wifiPSK,'psk')" minlength="8" maxlength="33" type="text" class="w-full sm:w-min sm:min-w-[250px] bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block px-2">
<p class="my-auto text-red-500 px-2 text-sm">{{ errors['psk'] }}</p>
</div>
<div class="flex mb-1 space-x-1">
<div class="min-w-[125px] my-auto text-right">Channel</div>
<input v-model="wifiChannel" type="number" min="1" max="14" class="w-full sm:w-min sm:min-w-[250px] bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block px-2">
</div>
<div class="flex mb-1 space-x-1">
<div class="min-w-[125px] my-auto text-right">IP</div>
<input v-model="wifiIP" @input="validateIP(wifiIP,'ip')" type="text" placeholder="192.168.0.100" class="w-full sm:w-min sm:min-w-[250px] bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block px-2">
<p class="my-auto text-red-500 px-2 text-sm">{{ errors['ip'] }}</p>
</div>
<div class="flex mb-1 space-x-1">
<div class="min-w-[125px] my-auto text-right">Netmask</div>
<input v-model="wifiNetmask" @input="validateIP(wifiNetmask,'netmask')" placeholder="255.255.255.0" type="text" class="w-full sm:w-min sm:min-w-[250px] bg-white border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block px-2">
<p class="my-auto text-red-500 px-2 text-sm">{{ errors['netmask'] }}</p>
</div>
<div class="space-x-1">
<button @click="enableWifiStation" class="border border-gray-500 px-2 bg-gray-100 hover:bg-gray-200 rounded">
Enable Station
</button>
<button @click="enableWifiAP" class="border border-gray-500 px-2 bg-gray-100 hover:bg-gray-200 rounded">
Enable Access Point
</button>
<button @click="disableWifi" class="border border-gray-500 px-2 bg-gray-100 hover:bg-gray-200 rounded">
Disable
</button>
</div>

</div>

</div>
</div>

<!-- setup web-serial-polyfill -->
Expand Down Expand Up @@ -942,6 +988,14 @@
],
},

// Wi-Fi
wifiChannel: "", // means "don't change"
wifiSSID: "Reticulum_WiFi",
wifiPSK: "password",
wifiIP: "",
wifiNetmask: "",

errors: {},
};
},
mounted() {
Expand Down Expand Up @@ -1849,6 +1903,83 @@
reader.readAsBinaryString(blob);
});
},
// mode = 0: disable Wi-Fi
// mode = 1: enable Wi-Fi in station mode
// mode = 2: enable Wi-Fi in AP mode
async configureWifi(mode) {
// Check for validation errors
for (const key in this.errors) {
if (this.errors[key]!=="") {
return
}
}

// ask for rnode
const rnode = await this.askForRNode();
if(!rnode){
return;
}

// check if device has been provisioned
const rom = await rnode.getRomAsObject();
const details = rom.parse();
if(!details || !details.is_provisioned){
alert("Eeprom is not provisioned. You must do this first!");
await rnode.close();
return;
}

// todo check if firmware hashes match, as config will not save if device has invalid target hash, because radio must be able to init

// configure
console.log("configuring Wi-Fi");
if (this.wifiChannel !== "") {
await rnode.setChannel(this.wifiChannel);
}
await rnode.setSSID(this.wifiSSID);
await rnode.setPSK(this.wifiPSK);
await rnode.setIP(this.wifiIP);
await rnode.setNetmask(this.wifiNetmask);
await rnode.setWifiMode(mode);
console.log("configuring Wi-Fi: done");

// save config
console.log("saving config");
await Utils.sleepMillis(500);
await rnode.saveConfig();
console.log("saving config: done");

await rnode.close();
alert("Wi-Fi has been " + (mode === 0 ? "disabled!" : "enabled!"));
},
async disableWifi() {
await this.configureWifi(0);
},
async enableWifiStation() {
await this.configureWifi(1);
},
async enableWifiAP() {
await this.configureWifi(2);
},
validateIP(value, name) {
// Regex for validating an IPv4 address (0.0.0.0 to 255.255.255.255)
const ipv4Pattern = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;

if (value === '') {
this.errors[name] = '';
} else if (ipv4Pattern.test(value)) {
this.errors[name] = '';
} else {
this.errors[name] = 'Invalid IPv4 address format';
}
},
validatePSK(value, name) {
if (value.length < 8) {
this.errors[name] = 'PSK length should be between 8 and 33';
} else {
this.errors[name] = '';
}
}
},
computed: {
recommendedFirmwareFilename() {
Expand Down
76 changes: 76 additions & 0 deletions js/rnode.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ class RNode {
CMD_STAT_RSSI = 0x23;
CMD_STAT_SNR = 0x24;

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_BOARD = 0x47;
CMD_PLATFORM = 0x48;
CMD_MCU = 0x49;
Expand Down Expand Up @@ -766,6 +773,61 @@ class RNode {
]);
}

async setChannel(channel) {
await this.sendKissCommand([
this.CMD_WIFI_CHN,
channel
]);
}

async setSSID(ssid) {
// empty ssid is valid, and means "delete"
const encoder = new TextEncoder();
let data = encoder.encode(ssid);

await this.sendKissCommand([
this.CMD_WIFI_SSID,
...data,
0x00
]);
}

async setPSK(psk) {
// empty psk is valid, and means "delete"
const encoder = new TextEncoder();
let data = encoder.encode(psk);

await this.sendKissCommand([
this.CMD_WIFI_PSK,
...data,
0x00
]);
}

async setIP(ip) {
let data = ipStrToBytes(ip);

await this.sendKissCommand([
this.CMD_WIFI_IP,
...data,
]);
}

async setNetmask(netmask) {
let data = ipStrToBytes(netmask);

await this.sendKissCommand([
this.CMD_WIFI_NM,
...data,
]);
}

async setWifiMode(mode) {
await this.sendKissCommand([
this.CMD_WIFI_MODE,
mode
]);
}
}

class ROM {
Expand Down Expand Up @@ -1018,3 +1080,17 @@ class ROM {
}

}

// ipStrToBytes converts IPv4 address in string format to array of bytes
function ipStrToBytes(ip) {
if (ip.length === 0) {
// means "delete"
ip = "0.0.0.0"
}
let octets = ip.split(".")
let data = []
for (const octet of octets) {
data.push(+octet)
}
return data;
}