diff --git a/src/js/protocols/WebSerial.js b/src/js/protocols/WebSerial.js
index 3fd076f7..48354ec6 100644
--- a/src/js/protocols/WebSerial.js
+++ b/src/js/protocols/WebSerial.js
@@ -334,18 +334,26 @@ class WebSerial extends EventTarget {
// AT32 on macOS requires smaller chunks (63 bytes) to work correctly due to
// USB buffer size limitations in the macOS implementation
const batchWriteSize = 63;
- let remainingData = data;
- while (remainingData.byteLength > batchWriteSize) {
- const sliceData = remainingData.slice(0, batchWriteSize);
- remainingData = remainingData.slice(batchWriteSize);
+
+ // Ensure data is a Uint8Array for proper slicing
+ const dataArray = data instanceof Uint8Array ? data : new Uint8Array(data);
+
+ let offset = 0;
+ while (offset + batchWriteSize < dataArray.byteLength) {
+ const chunk = dataArray.slice(offset, offset + batchWriteSize);
+ offset += batchWriteSize;
try {
- await this.writer.write(sliceData);
+ await this.writer.write(chunk);
} catch (error) {
console.error(`${logHead} Error writing batch chunk:`, error);
throw error; // Re-throw to be caught by the send method
}
}
- await this.writer.write(remainingData);
+
+ // Write the remaining data
+ if (offset < dataArray.byteLength) {
+ await this.writer.write(dataArray.slice(offset));
+ }
}
async send(data, callback) {
@@ -358,14 +366,17 @@ class WebSerial extends EventTarget {
}
try {
- if (this.isNeedBatchWrite) {
- await this.batchWrite(data);
- } else {
- await this.writer.write(data);
- }
- this.bytesSent += data.byteLength;
+ // Create a buffer from the data
+ const buffer = data instanceof ArrayBuffer ? data : new Uint8Array(data).buffer;
- const result = { bytesSent: data.byteLength };
+ if (this.isNeedBatchWrite) {
+ await this.batchWrite(buffer);
+ } else {
+ await this.writer.write(new Uint8Array(buffer));
+ }
+ this.bytesSent += buffer.byteLength;
+
+ const result = { bytesSent: buffer.byteLength };
if (callback) {
callback(result);
}
diff --git a/src/js/tabs/setup.js b/src/js/tabs/setup.js
index ed962351..aec4ccf2 100644
--- a/src/js/tabs/setup.js
+++ b/src/js/tabs/setup.js
@@ -14,6 +14,7 @@ import $ from "jquery";
import { ispConnected } from "../utils/connection";
import { sensorTypes } from "../sensor_types";
import { addArrayElementsAfter, replaceArrayElement } from "../utils/array";
+import { isFirefoxBrowser } from "../utils/checkBrowserCompatibility";
const setup = {
yaw_fix: 0.0,
@@ -387,17 +388,19 @@ setup.initialize = function (callback) {
const buildConfig = `
- ${i18n.getMessage(
- "initialSetupInfoBuildConfig",
-)}`;
+
+ ${i18n.getMessage("initialSetupInfoBuildConfig")}
+
+ `;
// Creates the "Log" button
const buildLog = `
- ${i18n.getMessage(
- "initialSetupInfoBuildLog",
-)}`;
+
+ ${i18n.getMessage("initialSetupInfoBuildLog")}
+
+ `;
// Shows the "Config" and "Log" buttons
build_info_e.html(`${buildConfig} ${buildLog}`);
@@ -429,19 +432,19 @@ setup.initialize = function (callback) {
// Creates the "Options" button (if possible)
const buildOptions = buildOptionsValid
? `
- ${i18n.getMessage(
- "initialSetupInfoBuildOptions",
- )}`
+
+ ${i18n.getMessage("initialSetupInfoBuildOptions")}
+
+ `
: "";
// Creates the "Download" button (if possible)
const buildDownload = buildKeyValid
- ? `
- ${i18n.getMessage(
- "initialSetupInfoBuildDownload",
-)}`
+ ? `
+
+ ${i18n.getMessage("initialSetupInfoBuildDownload")}
+
+ `
: "";
// Shows the "Options" and/or "Download" buttons
@@ -493,13 +496,14 @@ setup.initialize = function (callback) {
}
function showNetworkStatus() {
+ const isFirefox = isFirefoxBrowser();
const networkStatus = ispConnected();
let statusText = "";
- const type = navigator.connection.effectiveType;
- const downlink = navigator.connection.downlink;
- const rtt = navigator.connection.rtt;
+ const type = isFirefox ? "NA" : navigator.connection.effectiveType;
+ const downlink = isFirefox ? "NA" : navigator.connection.downlink;
+ const rtt = isFirefox ? "NA" : navigator.connection.rtt;
if (!networkStatus || !navigator.onLine || type === "none") {
statusText = i18n.getMessage("initialSetupNetworkInfoStatusOffline");
@@ -510,9 +514,9 @@ setup.initialize = function (callback) {
}
$(".network-status").text(statusText);
- $(".network-type").text(navigator.connection.effectiveType);
- $(".network-downlink").text(`${navigator.connection.downlink} Mbps`);
- $(".network-rtt").text(navigator.connection.rtt);
+ $(".network-type").text(type);
+ $(".network-downlink").text(`${downlink} Mbps`);
+ $(".network-rtt").text(rtt);
}
prepareDisarmFlags();
diff --git a/src/js/utils/checkBrowserCompatibility.js b/src/js/utils/checkBrowserCompatibility.js
index a1a34411..5a49eacd 100644
--- a/src/js/utils/checkBrowserCompatibility.js
+++ b/src/js/utils/checkBrowserCompatibility.js
@@ -28,15 +28,26 @@ export function getOS() {
}
export function isChromiumBrowser() {
- if (navigator.userAgentData) {
- return navigator.userAgentData.brands.some((brand) => {
- return brand.brand == "Chromium";
- });
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent
+ if (!navigator.userAgentData) {
+ // Fallback to traditional userAgent string check
+ return /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);
}
- // Fallback for older browsers/Android
- const ua = navigator.userAgent.toLowerCase();
- return ua.includes("chrom") || ua.includes("edg");
+ // https://learn.microsoft.com/en-us/microsoft-edge/web-platform/user-agent-guidance
+ return navigator.userAgentData.brands.some((brand) => {
+ return brand.brand == "Chromium";
+ });
+}
+
+export function isFirefoxBrowser() {
+ if (navigator.userAgentData) {
+ return navigator.userAgentData.brands.some((brand) => {
+ return brand.brand == "Firefox";
+ });
+ }
+ // Fallback to traditional userAgent string check
+ return navigator.userAgent.includes("Firefox");
}
export function isAndroid() {
@@ -65,15 +76,18 @@ export function checkBrowserCompatibility() {
const isWebBluetooth = checkWebBluetoothSupport();
const isWebUSB = checkWebUSBSupport();
const isChromium = isChromiumBrowser();
-
+ const isFirefox = isFirefoxBrowser();
const isNative = Capacitor.isNativePlatform();
- const compatible = isNative || (isChromium && (isWebSerial || isWebBluetooth || isWebUSB));
+ const compatible = isNative || ((isChromium || isFirefox) && (isWebSerial || isWebBluetooth || isWebUSB));
console.log("User Agent: ", navigator.userAgentData);
console.log("Native: ", isNative);
console.log("Chromium: ", isChromium);
+ console.log("Firefox: ", isFirefox);
console.log("Web Serial: ", isWebSerial);
+ console.log("Web Bluetooth: ", isWebBluetooth);
+ console.log("Web USB: ", isWebUSB);
console.log("OS: ", getOS());
console.log("Android: ", isAndroid());
@@ -86,7 +100,8 @@ export function checkBrowserCompatibility() {
let errorMessage = "";
if (!isChromium) {
- errorMessage = "Betaflight app requires a Chromium based browser (Chrome, Chromium, Edge).
";
+ errorMessage =
+ "Betaflight app requires a Chromium based browser (Chrome, Chromium, Edge),
or Firefox based browser running the WebSerial extension.
";
}
if (!isWebBluetooth) {