diff --git a/core.js b/core.js
index 268cf48..c5129bc 100644
--- a/core.js
+++ b/core.js
@@ -57,6 +57,10 @@ const available_langs = {
"zh_tw": { "name": "中文(繁)", "file": "zh_tw.json", "direction": "ltr"}
};
+async function sleep(ms) {
+ await new Promise(r => setTimeout(r, ms));
+}
+
function buf2hex(buffer) {
return [...new Uint8Array(buffer)].map(x => x.toString(16).padStart(2, '0')) .join('');
}
@@ -140,10 +144,10 @@ function is_rare(hw_ver) {
return ((b == 7 && a > 0x74) || (b == 9 && a != 0x93 && a != 0x90));
}
-// [DEVICE_AND_DOM]
async function ds4_info() {
+ // Device-only: collect info and return a common structure; do not touch the DOM
try {
- let ooc = l("unknown");
+ let deviceTypeText = l("unknown");
let is_clone = false;
const view = lf("ds4_info", await device.receiveFeatureReport(0xa3));
@@ -152,7 +156,7 @@ async function ds4_info() {
if(cmd != 0xa3 || view.buffer.byteLength < 49) {
if(view.buffer.byteLength != 49) {
- ooc = l("clone");
+ deviceTypeText = l("clone");
is_clone = true;
}
}
@@ -166,54 +170,50 @@ async function ds4_info() {
const sw_ver_minor= view.getUint16(0x25+4, true)
try {
if(!is_clone) {
- const view = await device.receiveFeatureReport(0x81);
- ooc = l("original");
+ // If this feature report succeeds, it's an original device
+ await device.receiveFeatureReport(0x81);
+ deviceTypeText = l("original");
}
} catch(e) {
la("clone");
is_clone = true;
- ooc = "" + l("clone") + "";
- disable_btn |= 1;
+ deviceTypeText = l("clone");
}
- clear_info();
- append_info(l("Build Date"), k1 + " " + k2);
- append_info(l("HW Version"), "" + dec2hex(hw_ver_major) + ":" + dec2hex(hw_ver_minor));
- append_info(l("SW Version"), dec2hex32(sw_ver_major) + ":" + dec2hex(sw_ver_minor));
- append_info(l("Device Type"), ooc);
+ const infoItems = [];
+ infoItems.push({ key: l("Build Date"), value: k1 + " " + k2, cat: "fw" });
+ infoItems.push({ key: l("HW Version"), value: "" + dec2hex(hw_ver_major) + ":" + dec2hex(hw_ver_minor), cat: "hw" });
+ infoItems.push({ key: l("SW Version"), value: dec2hex32(sw_ver_major) + ":" + dec2hex(sw_ver_minor), cat: "fw" });
+ infoItems.push({ key: l("Device Type"), value: deviceTypeText, cat: "hw", severity: is_clone ? 'danger' : undefined });
+
+ const nv = await query_nvstatus_ds4();
+
if(!is_clone) {
- const b_info = ' ' +
- '';
- append_info(l("Board Model"), ds4_hw_to_bm(hw_ver_minor) + b_info);
+ // Add Board Model (UI will append the info icon)
+ infoItems.push({ key: l("Board Model"), value: ds4_hw_to_bm(hw_ver_minor), cat: "hw", addInfoIcon: 'board' });
- // All ok, safe to lock NVS, query it and get BD Addr
- const nv = await query_nvstatus_ds4();
- render_nvstatus_to_dom(nv);
-
- if(nv.locked === false)
- await ds4_nvlock();
const bd_addr = await ds4_getbdaddr();
- append_info(l("Bluetooth Address"), bd_addr);
-
- if(is_rare(hw_ver_minor)) {
- show_popup("Wow, this is a rare/weird controller! Please write me an email at ds4@the.al or contact me on Discord (the_al)");
- }
+ infoItems.push({ key: l("Bluetooth Address"), value: bd_addr, cat: "hw" });
}
+
+ const rare = is_rare(hw_ver_minor);
+ const disable_bits = is_clone ? 1 : 0; // 1: clone
+
+ return { ok: true, infoItems, nv, disable_bits, rare };
} catch(e) {
- ooc = "" + l("clone") + "";
- disable_btn |= 1;
+ // Return error but do not touch DOM
+ return { ok: false, error: e, disable_bits: 1 };
}
- return true;
}
async function ds4_flash() {
la("ds4_flash");
try {
- await ds4_nvunlock();
- await ds4_nvlock();
+ await ds4_nvsunlock();
+ const lockRes4 = await multi_nvslock();
+ if(!lockRes4.ok) throw (lockRes4.error || new Error("NVS lock failed"));
show_popup(l("Changes saved successfully"));
-
} catch(error) {
show_popup(l("Error while saving changes:") + " " + String(error));
}
@@ -222,8 +222,9 @@ async function ds4_flash() {
async function ds5_flash() {
la("ds5_flash");
try {
- await ds5_nvunlock();
- await ds5_nvlock();
+ await ds5_nvsunlock();
+ const lockRes5 = await multi_nvslock();
+ if(!lockRes5.ok) throw (lockRes5.error || new Error("NVS lock failed"));
show_popup(l("Changes saved successfully"));
} catch(error) {
@@ -259,14 +260,12 @@ async function ds5_reset() {
}
}
-// [DEVICE_AND_DOM]
async function ds4_calibrate_range_begin() {
la("ds4_calibrate_range_begin");
- const err = l("Range calibration failed: ");
try {
// Begin
await device.sendFeatureReport(0x90, alloc_req(0x90, [1,1,2]))
- await new Promise(r => setTimeout(r, 200));
+ await sleep(200);
// Assert
const data = await device.receiveFeatureReport(0x91)
@@ -275,25 +274,21 @@ async function ds4_calibrate_range_begin() {
const d2 = data2.getUint32(0, false);
if(d1 != 0x91010201 || d2 != 0x920102ff) {
la("ds4_calibrate_range_begin_failed", {"d1": d1, "d2": d2});
- close_calibrate_window();
- return show_popup(err + l("Error 1"));
+ return { ok: false, code: 1, d1, d2 };
}
+ return { ok: true };
} catch(e) {
la("ds4_calibrate_range_begin_failed", {"r": e});
- await new Promise(r => setTimeout(r, 500));
- close_calibrate_window();
- return show_popup(err + e);
+ return { ok: false, error: String(e) };
}
}
-// [DEVICE_AND_DOM]
async function ds4_calibrate_range_end() {
la("ds4_calibrate_range_end");
- const err = l("Range calibration failed: ");
try {
// Write
await device.sendFeatureReport(0x90, alloc_req(0x90, [2,1,2]))
- await new Promise(r => setTimeout(r, 200));
+ await sleep(200);
const data = await device.receiveFeatureReport(0x91)
const data2 = await device.receiveFeatureReport(0x92)
@@ -301,29 +296,22 @@ async function ds4_calibrate_range_end() {
const d2 = data2.getUint32(0, false);
if(d1 != 0x91010202 || d2 != 0x92010201) {
la("ds4_calibrate_range_end_failed", {"d1": d1, "d2": d2});
- close_calibrate_window();
- return show_popup(err + l("Error 3"));
+ return { ok: false, code: 3, d1, d2 };
}
- update_nvs_changes_status(1);
- close_calibrate_window();
- show_popup(l("Range calibration completed"));
+ return { ok: true };
} catch(e) {
la("ds4_calibrate_range_end_failed", {"r": e});
- await new Promise(r => setTimeout(r, 500));
- close_calibrate_window();
- return show_popup(err + e);
+ return { ok: false, error: String(e) };
}
}
-// [DEVICE_AND_DOM]
async function ds4_calibrate_sticks_begin() {
la("ds4_calibrate_sticks_begin");
- const err = l("Stick calibration failed: ");
try {
// Begin
await device.sendFeatureReport(0x90, alloc_req(0x90, [1,1,1]))
- await new Promise(r => setTimeout(r, 200));
+ await sleep(200);
// Assert
const data = await device.receiveFeatureReport(0x91);
@@ -332,55 +320,44 @@ async function ds4_calibrate_sticks_begin() {
const d2 = data2.getUint32(0, false);
if(d1 != 0x91010101 || d2 != 0x920101ff) {
la("ds4_calibrate_sticks_begin_failed", {"d1": d1, "d2": d2});
- show_popup(err + l("Error 1"));
- return false;
+ return { ok: false, code: 1, d1, d2 };
}
- return true;
+ return { ok: true };
} catch(e) {
la("ds4_calibrate_sticks_begin_failed", {"r": e});
- await new Promise(r => setTimeout(r, 500));
- show_popup(err + e);
- return false;
+ return { ok: false, error: String(e) };
}
}
-// [DEVICE_AND_DOM]
async function ds4_calibrate_sticks_sample() {
la("ds4_calibrate_sticks_sample");
- const err = l("Stick calibration failed: ");
try {
// Sample
await device.sendFeatureReport(0x90, alloc_req(0x90, [3,1,1]))
- await new Promise(r => setTimeout(r, 200));
+ await sleep(200);
// Assert
const data = await device.receiveFeatureReport(0x91);
const data2 = await device.receiveFeatureReport(0x92);
if(data.getUint32(0, false) != 0x91010101 || data2.getUint32(0, false) != 0x920101ff) {
- close_calibrate_window();
const d1 = dec2hex32(data.getUint32(0, false));
const d2 = dec2hex32(data2.getUint32(0, false));
la("ds4_calibrate_sticks_sample_failed", {"d1": d1, "d2": d2});
- show_popup(err + l("Error 2") + " (" + d1 + ", " + d2 + ")");
- return false;
+ return { ok: false, code: 2, d1, d2 };
}
- return true;
+ return { ok: true };
} catch(e) {
- await new Promise(r => setTimeout(r, 500));
- show_popup(err + e);
- return false;
+ return { ok: false, error: String(e) };
}
}
-// [DEVICE_AND_DOM]
async function ds4_calibrate_sticks_end() {
la("ds4_calibrate_sticks_end");
- const err = l("Stick calibration failed: ");
try {
// Write
await device.sendFeatureReport(0x90, alloc_req(0x90, [2,1,1]))
- await new Promise(r => setTimeout(r, 200));
+ await sleep(200);
const data = await device.receiveFeatureReport(0x91);
const data2 = await device.receiveFeatureReport(0x92);
@@ -388,87 +365,16 @@ async function ds4_calibrate_sticks_end() {
const d1 = dec2hex32(data.getUint32(0, false));
const d2 = dec2hex32(data2.getUint32(0, false));
la("ds4_calibrate_sticks_end_failed", {"d1": d1, "d2": d2});
- show_popup(err + l("Error 3") + " (" + d1 + ", " + d2 + ")");
- return false;
+ return { ok: false, code: 3, d1, d2 };
}
- update_nvs_changes_status(1);
- return true;
+ return { ok: true };
} catch(e) {
la("ds4_calibrate_sticks_end_failed", {"r": e});
- await new Promise(r => setTimeout(r, 500));
- show_popup(err + e);
- return false;
+ return { ok: false, error: String(e) };
}
}
-// [DEVICE_AND_DOM]
-async function ds4_calibrate_sticks() {
- la("ds4_calibrate_sticks");
- const err = l("Stick calibration failed: ");
- try {
- set_progress(0);
-
- // Begin
- await device.sendFeatureReport(0x90, alloc_req(0x90, [1,1,1]))
- await new Promise(r => setTimeout(r, 200));
-
- // Assert
- const data = await device.receiveFeatureReport(0x91);
- const data2 = await device.receiveFeatureReport(0x92);
- const d1 = data.getUint32(0, false);
- const d2 = data2.getUint32(0, false);
- if(d1 != 0x91010101 || d2 != 0x920101ff) {
- la("ds4_calibrate_sticks_failed", {"s": 1, "d1": d1, "d2": d2});
- close_calibrate_window();
- return show_popup(err + l("Error 1"));
- }
-
- set_progress(10);
- await new Promise(r => setTimeout(r, 200));
-
- for(let i=0;i<3;i++) {
- // Sample
- await device.sendFeatureReport(0x90, alloc_req(0x90, [3,1,1]))
- await new Promise(r => setTimeout(r, 200));
-
- // Assert
- const data = await device.receiveFeatureReport(0x91);
- const data2 = await device.receiveFeatureReport(0x92);
- if(data.getUint32(0, false) != 0x91010101 || data2.getUint32(0, false) != 0x920101ff) {
- const d1 = dec2hex32(data.getUint32(0, false));
- const d2 = dec2hex32(data2.getUint32(0, false));
- la("ds4_calibrate_sticks_failed", {"s": 2, "i": i, "d1": d1, "d2": d2});
- close_calibrate_window();
- return show_popup(err + l("Error 2") + " (" + d1 + ", " + d2 + ")");
- }
-
- await new Promise(r => setTimeout(r, 500));
- set_progress(20 + i * 30);
- }
-
- // Write
- await device.sendFeatureReport(0x90, alloc_req(0x90, [2,1,1]))
- await new Promise(r => setTimeout(r, 200));
- if(data.getUint32(0, false) != 0x91010101 || data2.getUint32(0, false) != 0x920101FF) {
- const d1 = dec2hex32(data.getUint32(0, false));
- const d2 = dec2hex32(data2.getUint32(0, false));
- la("ds4_calibrate_sticks_failed", {"s": 3, "d1": d1, "d2": d2});
- close_calibrate_window();
- return show_popup(err + l("Error 3") + " (" + d1 + ", " + d2 + ")");
- }
-
- set_progress(100);
- await new Promise(r => setTimeout(r, 500));
- close_calibrate_window()
- show_popup(l("Stick calibration completed"));
- } catch(e) {
- la("ds4_calibrate_sticks_failed", {"r": e});
- await new Promise(r => setTimeout(r, 500));
- close_calibrate_window();
- return show_popup(err + e);
- }
-}
// Unified NV status helpers
async function query_nvstatus_ds4() {
@@ -560,7 +466,7 @@ async function ds4_getbdaddr() {
async function ds5_edge_get_barcode() {
try {
await device.sendFeatureReport(0x80, alloc_req(0x80, [21,34]));
- await new Promise(r => setTimeout(r, 100));
+ await sleep(100);
const data = lf("ds5_edge_get_barcode", await device.receiveFeatureReport(0x81));
const td = new TextDecoder()
@@ -583,13 +489,18 @@ async function ds5_getbdaddr() {
}
}
-async function ds4_nvlock() {
- la("ds4_nvlock");
- await device.sendFeatureReport(0xa0, alloc_req(0xa0, [10,1,0]))
+async function ds4_nvslock() {
+ la("ds4_nvslock");
+ try {
+ await device.sendFeatureReport(0xa0, alloc_req(0xa0, [10,1,0]));
+ return { ok: true };
+ } catch (e) {
+ return { ok: false, error: e };
+ }
}
-async function ds4_nvunlock() {
- la("ds4_nvunlock");
+async function ds4_nvsunlock() {
+ la("ds4_nvsunlock");
await device.sendFeatureReport(0xa0, alloc_req(0xa0, [10,2,0x3e,0x71,0x7f,0x89]))
}
@@ -640,15 +551,14 @@ function reverse_str(s) {
return s.split('').reverse().join('');
}
-
-// [DEVICE_AND_DOM]
async function ds5_info(is_edge) {
+ // Device-only: collect info and return a common structure; do not touch the DOM
try {
const view = lf("ds5_info", await device.receiveFeatureReport(0x20));
const cmd = view.getUint8(0, true);
if(cmd != 0x20 || view.buffer.byteLength != 64)
- return false;
+ return { ok: false, error: new Error("Invalid response for ds5_info") };
const build_date = new TextDecoder().decode(view.buffer.slice(1, 1+11));
const build_time = new TextDecoder().decode(view.buffer.slice(12, 20));
@@ -666,62 +576,67 @@ async function ds5_info(is_edge) {
const fwversion2 = view.getUint32(52, true);
const fwversion3 = view.getUint32(56, true);
- clear_info();
-
- const b_info = ' ' +
- '';
- const c_info = ' ' +
- '';
+ const infoItems = [];
const serial_number = await ds5_system_info(1, 19, 17);
- append_info(l("Serial Number"), serial_number, "hw");
- append_info_extra(l("MCU Unique ID"), await ds5_system_info(1, 9, 9, false), "hw");
- append_info_extra(l("PCBA ID"), reverse_str(await ds5_system_info(1, 17, 14)), "hw");
- append_info_extra(l("Battery Barcode"), await ds5_system_info(1, 24, 23), "hw");
- append_info_extra(l("VCM Left Barcode"), await ds5_system_info(1, 26, 16), "hw");
- append_info_extra(l("VCM Right Barcode"), await ds5_system_info(1, 28, 16), "hw");
+ infoItems.push({ key: l("Serial Number"), value: serial_number, cat: "hw" });
+ infoItems.push({ key: l("MCU Unique ID"), value: await ds5_system_info(1, 9, 9, false), cat: "hw", extra: true });
+ infoItems.push({ key: l("PCBA ID"), value: reverse_str(await ds5_system_info(1, 17, 14)), cat: "hw", extra: true });
+ infoItems.push({ key: l("Battery Barcode"), value: await ds5_system_info(1, 24, 23), cat: "hw", extra: true });
+ infoItems.push({ key: l("VCM Left Barcode"), value: await ds5_system_info(1, 26, 16), cat: "hw", extra: true });
+ infoItems.push({ key: l("VCM Right Barcode"), value: await ds5_system_info(1, 28, 16), cat: "hw", extra: true });
const color = ds5_color(serial_number);
- append_info(l("Color"), color + c_info, "hw");
+ infoItems.push({ key: l("Color"), value: color, cat: "hw", addInfoIcon: 'color' });
if(!is_edge) {
- append_info(l("Board Model"), ds5_hw_to_bm(hwinfo) + b_info, "hw");
+ infoItems.push({ key: l("Board Model"), value: ds5_hw_to_bm(hwinfo), cat: "hw", addInfoIcon: 'board' });
}
- append_info(l("FW Build Date"), build_date + " " + build_time, "fw");
- append_info_extra(l("FW Type"), "0x" + dec2hex(fwtype), "fw");
- append_info_extra(l("FW Series"), "0x" + dec2hex(swseries), "fw");
- append_info_extra(l("HW Model"), "0x" + dec2hex32(hwinfo), "hw");
- append_info(l("FW Version"), "0x" + dec2hex32(fwversion), "fw");
- append_info(l("FW Update"), "0x" + dec2hex(updversion), "fw");
- append_info_extra(l("FW Update Info"), "0x" + dec2hex8(unk), "fw");
- append_info_extra(l("SBL FW Version"), "0x" + dec2hex32(fwversion1), "fw");
- append_info_extra(l("Venom FW Version"), "0x" + dec2hex32(fwversion2), "fw");
- append_info_extra(l("Spider FW Version"), "0x" + dec2hex32(fwversion3), "fw");
+ infoItems.push({ key: l("FW Build Date"), value: build_date + " " + build_time, cat: "fw" });
+ infoItems.push({ key: l("FW Type"), value: "0x" + dec2hex(fwtype), cat: "fw", extra: true });
+ infoItems.push({ key: l("FW Series"), value: "0x" + dec2hex(swseries), cat: "fw", extra: true });
+ infoItems.push({ key: l("HW Model"), value: "0x" + dec2hex32(hwinfo), cat: "hw", extra: true });
+ infoItems.push({ key: l("FW Version"), value: "0x" + dec2hex32(fwversion), cat: "fw" });
+ infoItems.push({ key: l("FW Update"), value: "0x" + dec2hex(updversion), cat: "fw" });
+ infoItems.push({ key: l("FW Update Info"), value: "0x" + dec2hex8(unk), cat: "fw", extra: true });
+ infoItems.push({ key: l("SBL FW Version"), value: "0x" + dec2hex32(fwversion1), cat: "fw", extra: true });
+ infoItems.push({ key: l("Venom FW Version"), value: "0x" + dec2hex32(fwversion2), cat: "fw", extra: true });
+ infoItems.push({ key: l("Spider FW Version"), value: "0x" + dec2hex32(fwversion3), cat: "fw", extra: true });
- append_info_extra(l("Touchpad ID"), await ds5_system_info(5, 2, 8, false), "hw");
- append_info_extra(l("Touchpad FW Version"), await ds5_system_info(5, 4, 8, false), "fw");
+ infoItems.push({ key: l("Touchpad ID"), value: await ds5_system_info(5, 2, 8, false), cat: "hw", extra: true });
+ infoItems.push({ key: l("Touchpad FW Version"), value: await ds5_system_info(5, 4, 8, false), cat: "fw", extra: true });
const old_controller = build_date.search(/ 2020| 2021/);
+ let disable_bits = 0;
if(old_controller != -1) {
la("ds5_info_error", {"r": "old"})
- disable_btn |= 2;
- return true;
+ disable_bits |= 2; // 2: outdated firmware
}
const nv = await query_nvstatus_ds5();
- render_nvstatus_to_dom(nv);
- if(nv.locked === false)
- await ds5_nvlock();
-
const bd_addr = await ds5_getbdaddr();
- append_info(l("Bluetooth Address"), bd_addr, "hw");
+ infoItems.push({ key: l("Bluetooth Address"), value: bd_addr, cat: "hw" });
+
+ // DS Edge extra module info
+ if (is_edge) {
+ const empty = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00';
+ try {
+ const sticks_barcode = (await ds5_edge_get_barcode()).map(barcode => barcode === empty ? l("Unknown") : barcode);
+ infoItems.push({ key: l("Left Module Barcode"), value: sticks_barcode[1], cat: "fw" });
+ infoItems.push({ key: l("Right Module Barcode"), value: sticks_barcode[0], cat: "fw" });
+ } catch(_e) {
+ // ignore module read errors here
+ }
+ }
+
+ const pending_reboot = (nv && nv.status === 'pending_reboot');
+
+ return { ok: true, infoItems, nv, disable_bits, pending_reboot };
} catch(e) {
la("ds5_info_error", {"r": e})
- show_popup(l("Cannot read controller information"));
- return false;
+ return { ok: false, error: e };
}
- return true;
}
async function ds5_load_modules_info() {
@@ -735,10 +650,8 @@ async function ds5_load_modules_info() {
append_info(l("Right Module Barcode"), sticks_barcode[0], "fw");
}
-// [DEVICE_AND_DOM]
async function ds5_calibrate_sticks_begin() {
la("ds5_calibrate_sticks_begin");
- const err = l("Range calibration failed: ");
try {
// Begin
await device.sendFeatureReport(0x82, alloc_req(0x82, [1,1,1]))
@@ -748,22 +661,17 @@ async function ds5_calibrate_sticks_begin() {
if(data.getUint32(0, false) != 0x83010101) {
const d1 = dec2hex32(data.getUint32(0, false));
la("ds5_calibrate_sticks_begin_failed", {"d1": d1});
- show_popup(err + l("Error 1") + " (" + d1 + ").");
- return false;
+ return { ok: false, code: 1, d1 };
}
- return true;
+ return { ok: true };
} catch(e) {
la("ds5_calibrate_sticks_begin_failed", {"r": e});
- await new Promise(r => setTimeout(r, 500));
- show_popup(err + e);
- return false;
+ return { ok: false, error: String(e) };
}
}
-// [DEVICE_AND_DOM]
async function ds5_calibrate_sticks_sample() {
la("ds5_calibrate_sticks_sample");
- const err = l("Stick calibration failed: ");
try {
// Sample
await device.sendFeatureReport(0x82, alloc_req(0x82, [3,1,1]))
@@ -773,22 +681,17 @@ async function ds5_calibrate_sticks_sample() {
if(data.getUint32(0, false) != 0x83010101) {
const d1 = dec2hex32(data.getUint32(0, false));
la("ds5_calibrate_sticks_sample_failed", {"d1": d1});
- show_popup(err + l("Error 2") + " (" + d1 + ").");
- return false;
+ return { ok: false, code: 2, d1 };
}
- return true;
+ return { ok: true };
} catch(e) {
la("ds5_calibrate_sticks_sample_failed", {"r": e});
- await new Promise(r => setTimeout(r, 500));
- show_popup(err + e);
- return false;
+ return { ok: false, error: String(e) };
}
}
-// [DEVICE_AND_DOM]
async function ds5_calibrate_sticks_end() {
la("ds5_calibrate_sticks_end");
- const err = l("Stick calibration failed: ");
try {
// Write
await device.sendFeatureReport(0x82, alloc_req(0x82, [2,1,1]))
@@ -799,15 +702,13 @@ async function ds5_calibrate_sticks_end() {
if(data.getUint32(0, false) != 0x83010102) {
const d1 = dec2hex32(data.getUint32(0, false));
la("ds5_calibrate_sticks_failed", {"s": 3, "d1": d1});
- close_calibrate_window();
- return show_popup(err + l("Error 3") + " (" + d1 + ").");
+ return { ok: false, code: 3, d1 };
}
} else if(mode == 3) {
if(data.getUint32(0, false) != 0x83010101) {
const d1 = dec2hex32(data.getUint32(0, false));
la("ds5_calibrate_sticks_failed", {"s": 3, "d1": d1});
- close_calibrate_window();
- return show_popup(err + l("Error 4") + " (" + d1 + ").");
+ return { ok: false, code: 4, d1 };
}
await device.sendFeatureReport(0x82, alloc_req(0x82, [2,1,1]))
@@ -815,115 +716,20 @@ async function ds5_calibrate_sticks_end() {
if(data.getUint32(0, false) != 0x83010103 && data.getUint32(0, false) != 0x83010312) {
const d1 = dec2hex32(data.getUint32(0, false));
la("ds5_calibrate_sticks_failed", {"s": 3, "d1": d1});
- close_calibrate_window();
- return show_popup(err + l("Error 5") + " (" + d1 + ").");
+ return { ok: false, code: 5, d1 };
}
}
- update_nvs_changes_status(1);
- return true;
+ return { ok: true };
} catch(e) {
la("ds5_calibrate_sticks_end_failed", {"r": e});
- await new Promise(r => setTimeout(r, 500));
- show_popup(err + e);
- return false;
+ return { ok: false, error: String(e) };
}
}
-// [DEVICE_AND_DOM]
-async function ds5_calibrate_sticks() {
- la("ds5_fast_calibrate_sticks");
- const err = l("Stick calibration failed: ");
- try {
- set_progress(0);
- // Begin
- await device.sendFeatureReport(0x82, alloc_req(0x82, [1,1,1]))
-
- // Assert
- let data = await device.receiveFeatureReport(0x83)
- let d1;
- if(data.getUint32(0, false) != 0x83010101) {
- d1 = dec2hex32(data.getUint32(0, false));
- la("ds5_calibrate_sticks_failed", {"s": 1, "d1": d1});
- close_calibrate_window();
- return show_popup(err + l("Error 1") + " (" + d1 + ").");
- }
-
- set_progress(10);
-
- await new Promise(r => setTimeout(r, 100));
-
- for(let i=0;i<3;i++) {
- // Sample
- await device.sendFeatureReport(0x82, alloc_req(0x82, [3,1,1]))
-
- // Assert
- data = await device.receiveFeatureReport(0x83)
- if(data.getUint32(0, false) != 0x83010101) {
- d1 = dec2hex32(data.getUint32(0, false));
- la("ds5_calibrate_sticks_failed", {"s": 2, "i": i, "d1": d1});
- close_calibrate_window();
- return show_popup(err + l("Error 2") + " (" + d1 + ").");
- }
-
- await new Promise(r => setTimeout(r, 500));
- set_progress(20 + i * 20);
- }
-
- await new Promise(r => setTimeout(r, 200));
- set_progress(80);
-
- // Write
- await device.sendFeatureReport(0x82, alloc_req(0x82, [2,1,1]))
-
- data = await device.receiveFeatureReport(0x83)
-
- if(mode == 2) {
- if(data.getUint32(0, false) != 0x83010102) {
- d1 = dec2hex32(data.getUint32(0, false));
- la("ds5_calibrate_sticks_failed", {"s": 3, "d1": d1});
- close_calibrate_window();
- return show_popup(err + l("Error 3") + " (" + d1 + ").");
- }
- } else if(mode == 3) {
- if(data.getUint32(0, false) != 0x83010101) {
- d1 = dec2hex32(data.getUint32(0, false));
- la("ds5_calibrate_sticks_failed", {"s": 3, "d1": d1});
- close_calibrate_window();
- return show_popup(err + l("Error 4") + " (" + d1 + ").");
- }
-
- await device.sendFeatureReport(0x82, alloc_req(0x82, [2,1,1]))
- data = await device.receiveFeatureReport(0x83)
- if(data.getUint32(0, false) != 0x83010103) {
- d1 = dec2hex32(data.getUint32(0, false));
- la("ds5_calibrate_sticks_failed", {"s": 3, "d1": d1});
- close_calibrate_window();
- return show_popup(err + l("Error 5") + " (" + d1 + ").");
- }
-
- }
-
- set_progress(100);
- update_nvs_changes_status(1);
-
- await new Promise(r => setTimeout(r, 500));
- close_calibrate_window()
-
- show_popup(l("Stick calibration completed"));
- } catch(e) {
- la("ds5_calibrate_sticks_failed", {"r": e});
- await new Promise(r => setTimeout(r, 500));
- close_calibrate_window();
- return show_popup(err + e);
- }
-}
-
-// [DEVICE_AND_DOM]
async function ds5_calibrate_range_begin() {
la("ds5_calibrate_range_begin");
- const err = l("Range calibration failed: ");
try {
// Begin
await device.sendFeatureReport(0x82, alloc_req(0x82, [1,1,2]))
@@ -933,21 +739,17 @@ async function ds5_calibrate_range_begin() {
if(data.getUint32(0, false) != 0x83010201) {
const d1 = dec2hex32(data.getUint32(0, false));
la("ds5_calibrate_range_begin_failed", {"d1": d1});
- close_calibrate_window();
- return show_popup(err + l("Error 1") + " (" + d1 + ").");
+ return { ok: false, code: 1, d1 };
}
+ return { ok: true };
} catch(e) {
la("ds5_calibrate_range_begin_failed", {"r": e});
- await new Promise(r => setTimeout(r, 500));
- close_calibrate_window();
- return show_popup(err + e);
+ return { ok: false, error: String(e) };
}
}
-// [DEVICE_AND_DOM]
async function ds5_calibrate_range_end() {
la("ds5_calibrate_range_end");
- const err = l("Range calibration failed: ");
try {
// Write
await device.sendFeatureReport(0x82, alloc_req(0x82, [2,1,2]))
@@ -959,15 +761,13 @@ async function ds5_calibrate_range_end() {
if(data.getUint32(0, false) != 0x83010202) {
const d1 = dec2hex32(data.getUint32(0, false));
la("ds5_calibrate_range_end_failed", {"d1": d1});
- close_calibrate_window();
- return show_popup(err + l("Error 3") + " (" + d1 + ").");
+ return { ok: false, code: 3, d1 };
}
} else {
if(data.getUint32(0, false) != 0x83010201) {
const d1 = dec2hex32(data.getUint32(0, false));
la("ds5_calibrate_range_end_failed", {"d1": d1});
- close_calibrate_window();
- return show_popup(err + l("Error 4") + " (" + d1 + ").");
+ return { ok: false, code: 4, d1 };
}
await device.sendFeatureReport(0x82, alloc_req(0x82, [2,1,2]))
@@ -975,32 +775,25 @@ async function ds5_calibrate_range_end() {
if(data.getUint32(0, false) != 0x83010203) {
const d1 = dec2hex32(data.getUint32(0, false));
la("ds5_calibrate_range_end_failed", {"d1": d1});
- close_calibrate_window();
- return show_popup(err + l("Error 5") + " (" + d1 + ").");
+ return { ok: false, code: 5, d1 };
}
}
- update_nvs_changes_status(1);
- close_calibrate_window();
- show_popup(l("Range calibration completed"));
+ return { ok: true };
} catch(e) {
la("ds5_calibrate_range_end_failed", {"r": e});
- await new Promise(r => setTimeout(r, 500));
- close_calibrate_window();
- return show_popup(err + e);
+ return { ok: false, error: String(e) };
}
}
-// [DEVICE_AND_DOM]
-async function ds5_nvlock() {
- la("ds5_nvlock");
+async function ds5_nvslock() {
+ la("ds5_nvslock");
try {
- await device.sendFeatureReport(0x80, alloc_req(0x80, [3,1]))
- const data = await device.receiveFeatureReport(0x81)
+ await device.sendFeatureReport(0x80, alloc_req(0x80, [3,1]));
+ await device.receiveFeatureReport(0x81);
+ return { ok: true };
} catch(e) {
- await new Promise(r => setTimeout(r, 500));
- close_calibrate_window();
- return show_popup(l("NVS Lock failed: ") + e);
+ return { ok: false, error: e };
}
}
@@ -1018,7 +811,7 @@ async function wait_until_written(expected) {
if(!again) {
return true;
}
- await new Promise(r => setTimeout(r, 50));
+ await sleep(50);
}
return false;
}
@@ -1031,7 +824,7 @@ async function ds5_edge_unlock_module(i) {
const m_name = i == 0 ? "left module" : "right module";
await device.sendFeatureReport(0x80, alloc_req(0x80, [21, 6, i, 11]))
- await new Promise(r => setTimeout(r, 200));
+ await sleep(200);
const ret = await wait_until_written([21, 6, 2])
if(!ret) {
throw new Error(l("Cannot unlock") + " " + l(m_name));
@@ -1042,7 +835,7 @@ async function ds5_edge_lock_module(i) {
const m_name = i == 0 ? "left module" : "right module";
await device.sendFeatureReport(0x80, alloc_req(0x80, [21, 4, i, 8]))
- await new Promise(r => setTimeout(r, 200));
+ await sleep(200);
const ret = await wait_until_written([21, 4, 2])
if(!ret) {
throw new Error(l("Cannot lock") + " " + l(m_name));
@@ -1053,7 +846,7 @@ async function ds5_edge_store_data_into(i) {
const m_name = i == 0 ? "left module" : "right module";
await device.sendFeatureReport(0x80, alloc_req(0x80, [21, 5, i]))
- await new Promise(r => setTimeout(r, 200));
+ await sleep(200);
const ret = await wait_until_written([21, 5, 2])
if(!ret) {
throw new Error(l("Cannot store data into") + " " + l(m_name));
@@ -1074,7 +867,7 @@ async function ds5_edge_flash_modules() {
set_edge_progress(0);
// Reload data, this ensures correctly writing data in the controller
- await new Promise(r => setTimeout(r, 100));
+ await sleep(100);
set_edge_progress(10);
// Unlock modules
@@ -1084,18 +877,18 @@ async function ds5_edge_flash_modules() {
set_edge_progress(30);
// Unlock NVS
- await ds5_nvunlock()
- await new Promise(r => setTimeout(r, 50));
+ await ds5_nvsunlock()
+ await sleep(50);
set_edge_progress(45);
// This should trigger write into modules
const data = await ds5_get_inmemory_module_data()
- await new Promise(r => setTimeout(r, 50));
+ await sleep(50);
set_edge_progress(60);
await write_finetune_data(data)
// Extra delay
- await new Promise(r => setTimeout(r, 100));
+ await sleep(100);
// Lock back modules
await ds5_edge_lock_module(0);
@@ -1104,32 +897,33 @@ async function ds5_edge_flash_modules() {
set_edge_progress(100);
// Lock back NVS
- await new Promise(r => setTimeout(r, 100));
- await ds5_nvlock()
+ await sleep(100);
+ const lockRes = await multi_nvslock();
+ if(!lockRes.ok) throw new Error(l("NVS Lock failed"));
- await new Promise(r => setTimeout(r, 250));
+ await sleep(250);
modal.hide();
modal = null;
- await new Promise(r => setTimeout(r, 300));
+ await sleep(300);
return true;
} catch(e) {
modal.hide();
modal = null;
- await new Promise(r => setTimeout(r, 500));
+ await sleep(500);
show_popup("Error: " + e);
return false;
}
}
// [DEVICE_AND_DOM]
-async function ds5_nvunlock() {
- la("ds5_nvunlock");
+async function ds5_nvsunlock() {
+ la("ds5_nvsunlock");
try {
await device.sendFeatureReport(0x80, alloc_req(0x80, [3,2, 101, 50, 64, 12]))
const data = await device.receiveFeatureReport(0x81)
} catch(e) {
- await new Promise(r => setTimeout(r, 500));
+ await sleep(500);
close_calibrate_window();
return show_popup(l("NVS Unlock failed: ") + e);
}
@@ -1311,7 +1105,10 @@ async function ds5_finetune() {
const nv = await query_nvstatus_ds5();
render_nvstatus_to_dom(nv);
if(nv.locked === false) {
- await ds5_nvlock();
+ const res = await multi_nvslock();
+ if(!res.ok) {
+ return;
+ }
const nv2 = await query_nvstatus_ds5();
render_nvstatus_to_dom(nv2);
if(!nv2.locked) {
@@ -1393,7 +1190,7 @@ async function ds5_get_inmemory_module_data() {
await device.sendFeatureReport(0x80, alloc_req(0x80, [12, 4]))
}
- await new Promise(r => setTimeout(r, 100));
+ await sleep(100);
const data = await device.receiveFeatureReport(0x81)
const cmd = data.getUint8(0, true);
const p1 = data.getUint8(1, true);
@@ -2573,67 +2370,43 @@ async function continue_connection(report) {
return;
}
+ // Helper to apply basic UI visibility based on device type
+ function applyDeviceUI({ showInfo, showFinetune, showMute, showInfoTab }) {
+ if (showInfo) { $("#infoshowall").show(); } else { $("#infoshowall").hide(); }
+ if (showFinetune) { $("#ds5finetune").show(); } else { $("#ds5finetune").hide(); }
+ set_mute_visibility(!!showMute);
+ if (showInfoTab) { $("#info-tab").show(); } else { $("#info-tab").hide(); }
+ }
+
+ let ret = null;
+ let ui = { showInfo: false, showFinetune: false, showMute: false, showInfoTab: false };
+ let targetMode = 0;
+ let targetDevName = "";
+
if(device.productId == 0x05c4) {
- $("#infoshowall").hide()
- $("#ds5finetune").hide()
- // Hide mute button for DS4
- set_mute_visibility(false);
- $("#info-tab").hide()
- if(await ds4_info()) {
- connected = true;
- mode = 1;
- devname = l("Sony DualShock 4 V1");
- device.oninputreport = process_ds4_input;
- }
+ // DS4 v1
+ ui = { showInfo: false, showFinetune: false, showMute: false, showInfoTab: false };
+ targetMode = 1;
+ targetDevName = l("Sony DualShock 4 V1");
+ ret = await ds4_info();
} else if(device.productId == 0x09cc) {
- $("#infoshowall").hide()
- $("#ds5finetune").hide()
- // Hide mute button for DS4
- set_mute_visibility(false);
- $("#info-tab").hide()
- if(await ds4_info()) {
- connected = true;
- mode = 1;
- devname = l("Sony DualShock 4 V2");
- device.oninputreport = process_ds4_input;
- }
+ // DS4 v2
+ ui = { showInfo: false, showFinetune: false, showMute: false, showInfoTab: false };
+ targetMode = 1;
+ targetDevName = l("Sony DualShock 4 V2");
+ ret = await ds4_info();
} else if(device.productId == 0x0ce6) {
- $("#infoshowall").show()
- $("#ds5finetune").show()
- // Show mute button for DS5
- set_mute_visibility(true);
- $("#info-tab").show()
- if(await ds5_info(false)) {
- connected = true;
- mode = 2;
- devname = l("Sony DualSense");
- device.oninputreport = process_ds_input;
- }
+ // DS5
+ ui = { showInfo: true, showFinetune: true, showMute: true, showInfoTab: true };
+ targetMode = 2;
+ targetDevName = l("Sony DualSense");
+ ret = await ds5_info(false);
} else if(device.productId == 0x0df2) {
- $("#infoshowall").show()
- $("#ds5finetune").show()
- // Show mute button for DS5 Edge
- set_mute_visibility(true);
- $("#info-tab").show()
- if(await ds5_info(true)) {
- connected = true;
- mode = 3;
- devname = l("Sony DualSense Edge");
- device.oninputreport = process_ds_input;
- await ds5_load_modules_info();
- }
-
-
- const nv_edge = await query_nvstatus_ds5();
- render_nvstatus_to_dom(nv_edge);
- if(nv_edge.status === 'pending_reboot') {
- // dualsense edge with pending reboot
- $("#btnconnect").prop("disabled", false);
- $("#connectspinner").hide();
- disconnect();
- show_popup(l("A reboot is needed to continue using this DualSense Edge. Please disconnect and reconnect your controller."));
- return;
- }
+ // DS5 Edge
+ ui = { showInfo: true, showFinetune: true, showMute: true, showInfoTab: true };
+ targetMode = 3;
+ targetDevName = l("Sony DualSense Edge");
+ ret = await ds5_info(true);
} else {
$("#btnconnect").prop("disabled", false);
$("#connectspinner").hide();
@@ -2642,7 +2415,19 @@ async function continue_connection(report) {
return;
}
- if(connected) {
+ if(ret && ret.ok) {
+ connected = true;
+ // Apply UI now that we know the device family
+ applyDeviceUI(ui);
+
+ // Update globals
+ mode = targetMode;
+ devname = targetDevName;
+
+ // Assign input processor for stream
+ device.oninputreport = (mode === 1) ? process_ds4_input : process_ds_input;
+
+ // Show main connected UI
$("#devname").text(devname + " (" + dec2hex(device.vendorId) + ":" + dec2hex(device.productId) + ")");
$("#offlinebar").hide();
$("#onlinebar").show();
@@ -2655,23 +2440,84 @@ async function continue_connection(report) {
if (calibTab) {
new bootstrap.Tab(calibTab).show();
}
+
+ // Edge-specific: pending reboot check (from nv)
+ if (mode === 3 && ret.pending_reboot) {
+ $("#btnconnect").prop("disabled", false);
+ $("#connectspinner").hide();
+ disconnect();
+ show_popup(l("A reboot is needed to continue using this DualSense Edge. Please disconnect and reconnect your controller."));
+ return;
+ }
+
+ // Render info collected from device
+ clear_info();
+ if (Array.isArray(ret.infoItems)) {
+ ret.infoItems.forEach(item => {
+ if (item && item.key !== undefined) {
+ // Compose value with optional info icon
+ let valueHtml = String(item.value ?? "");
+ if (item.addInfoIcon === 'board') {
+ const icon = ' ' +
+ '';
+ valueHtml += icon;
+ } else if (item.addInfoIcon === 'color') {
+ const icon = ' ' +
+ '';
+ valueHtml += icon;
+ }
+
+ // Apply severity formatting if requested
+ if (item.severity === 'danger') {
+ valueHtml = "" + valueHtml + "";
+ } else if (item.severity === 'success') {
+ valueHtml = "" + valueHtml + "";
+ }
+
+ if (item.extra) {
+ append_info_extra(item.key, valueHtml, item.cat || "hw");
+ } else {
+ append_info(item.key, valueHtml, item.cat || "hw");
+ }
+ }
+ });
+ }
+
+ // Render NV status
+ if (ret.nv) {
+ render_nvstatus_to_dom(ret.nv);
+ // Optionally try to lock NVS if unlocked
+ if (ret.nv.locked === false) {
+ await multi_nvslock();
+ }
+ }
+
+ // Apply disable button flags
+ if (typeof ret.disable_bits === 'number' && ret.disable_bits) {
+ disable_btn |= ret.disable_bits;
+ }
+ if(disable_btn != 0) update_disable_btn();
+
+ // DS4 rare notice
+ if (mode === 1 && ret.rare) {
+ show_popup("Wow, this is a rare/weird controller! Please write me an email at ds4@the.al or contact me on Discord (the_al)");
+ }
+
+ // Edge onboarding modal
+ if(mode == 3) {
+ show_edge_modal();
+ }
+
+ $("#btnconnect").prop("disabled", false);
+ $("#connectspinner").hide();
} else {
+ // Not connected/failed to fetch info
show_popup(l("Connected invalid device: ") + l("Error 1"));
$("#btnconnect").prop("disabled", false);
$("#connectspinner").hide();
disconnect();
return;
}
-
- if(mode == 3) {
- show_edge_modal();
- }
-
- if(disable_btn != 0)
- update_disable_btn();
-
- $("#btnconnect").prop("disabled", false);
- $("#connectspinner").hide();
} catch(error) {
$("#btnconnect").prop("disabled", false);
$("#connectspinner").hide();
@@ -2716,7 +2562,7 @@ async function connect() {
try {
$("#btnconnect").prop("disabled", true);
$("#connectspinner").show();
- await new Promise(r => setTimeout(r, 100));
+ await sleep(100);
const ds4v1 = { vendorId: 0x054c, productId: 0x05c4 };
const ds4v2 = { vendorId: 0x054c, productId: 0x09cc };
@@ -2778,44 +2624,77 @@ async function multi_reset() {
async function multi_nvsunlock() {
if(mode == 1) {
- await ds4_nvunlock();
- await refresh_nvstatus();
+ await ds4_nvsunlock();
} else {
- await ds5_nvunlock();
- await refresh_nvstatus();
+ await ds5_nvsunlock();
}
+ await refresh_nvstatus();
}
async function multi_nvslock() {
- if(mode == 1) {
- await ds4_nvlock();
- await refresh_nvstatus();
- } else if (mode == 2) {
- await ds5_nvlock();
- await refresh_nvstatus();
+ const res =
+ mode == 1 ? await ds4_nvslock() :
+ mode == 2 || mode == 3 ? await ds5_nvslock() :
+ { ok: false, error: new Error("Unsupported mode") };
+
+ await refresh_nvstatus();
+ if(!res.ok) {
+ show_popup(l("NVS Lock failed: ") + String(res.error));
}
+ return res;
}
async function multi_calib_sticks_begin() {
- if(mode == 1)
- return ds4_calibrate_sticks_begin();
- else
- return ds5_calibrate_sticks_begin();
+ if(mode == 1) {
+ const res = await ds4_calibrate_sticks_begin();
+ if(!res.ok) {
+ const detail = res.code ? (l("Error ") + String(res.code)) : String(res.error || "");
+ show_popup(l("Stick calibration failed: ") + detail);
+ return false;
+ }
+ return true;
+ } else {
+ const res = await ds5_calibrate_sticks_begin();
+ if(!res.ok) {
+ await sleep(500);
+ const detail = res.code ? (l("Error ") + String(res.code)) : String(res.error || "");
+ show_popup(l("Stick calibration failed: ") + detail);
+ return false;
+ }
+ return true;
+ }
}
async function multi_calib_sticks_end() {
+ let res;
if(mode == 1)
- await ds4_calibrate_sticks_end();
+ res = await ds4_calibrate_sticks_end();
else
- await ds5_calibrate_sticks_end();
+ res = await ds5_calibrate_sticks_end();
+
+ if(!res.ok) {
+ await sleep(500);
+ const detail = res.code ? (l("Error ") + String(res.code)) : String(res.error || "");
+ show_popup(l("Stick calibration failed: ") + detail);
+ on_stick_mode_change();
+ return false;
+ }
+
+ update_nvs_changes_status(1);
on_stick_mode_change();
+ return true;
}
async function multi_calib_sticks_sample() {
- if(mode == 1)
- return ds4_calibrate_sticks_sample();
- else
- return ds5_calibrate_sticks_sample();
+ const res = (mode == 1) ? await ds4_calibrate_sticks_sample() : await ds5_calibrate_sticks_sample();
+ if(!res.ok) {
+ await sleep(500);
+ close_calibrate_window();
+ const detail = res.code ? (l("Error ") + String(res.code)) : String(res.error || "");
+ show_popup(l("Stick calibration failed: ") + detail);
+ return false;
+ }
+ return true;
}
async function multi_calibrate_range() {
@@ -2826,19 +2705,39 @@ async function multi_calibrate_range() {
curModal = new bootstrap.Modal(document.getElementById('rangeModal'), {})
curModal.show();
- await new Promise(r => setTimeout(r, 1000));
+ await sleep(1000);
+ let res;
if(mode == 1)
- ds4_calibrate_range_begin();
+ res = await ds4_calibrate_range_begin();
else
- ds5_calibrate_range_begin();
+ res = await ds5_calibrate_range_begin();
+
+ if(!res.ok) {
+ await sleep(500);
+ close_calibrate_window();
+ const msg = res.code ? (l("Range calibration failed: ") + l("Error ") + String(res.code)) : (l("Range calibration failed: ") + String(res.error || ""));
+ show_popup(msg);
+ return;
+ }
}
async function multi_calibrate_range_on_close() {
+ let res;
if(mode == 1)
- await ds4_calibrate_range_end();
+ res = await ds4_calibrate_range_end();
else
- await ds5_calibrate_range_end();
+ res = await ds5_calibrate_range_end();
+
+ close_calibrate_window();
+ if(res && res.ok) {
+ update_nvs_changes_status(1);
+ show_popup(l("Range calibration completed"));
+ } else {
+ await sleep(500);
+ const msg = res && res.code ? (l("Range calibration failed: ") + l("Error ") + String(res.code)) : (l("Range calibration failed: ") + String((res && res.error) || ""));
+ show_popup(msg);
+ }
on_stick_mode_change();
}
@@ -2851,12 +2750,57 @@ async function multi_calibrate_sticks() {
curModal = new bootstrap.Modal(document.getElementById('calibrateModal'), {})
curModal.show();
- await new Promise(r => setTimeout(r, 1000));
+ await sleep(1000);
- if(mode == 1)
- ds4_calibrate_sticks();
- else
- ds5_calibrate_sticks();
+ const err = l("Stick calibration failed: ");
+ try {
+ // Begin
+ const okBegin = await multi_calib_sticks_begin();
+ if (!okBegin) {
+ await sleep(500);
+ close_calibrate_window();
+ return;
+ }
+
+ set_progress(10);
+ await sleep(100);
+
+ // Sample 3 times
+ for (let i = 0; i < 3; i++) {
+ const okSample = await multi_calib_sticks_sample();
+ if (!okSample) {
+ await sleep(500);
+ close_calibrate_window();
+ return;
+ }
+
+ await sleep(500);
+ set_progress(20 + i * 20); // 20, 40, 60
+ }
+
+ await sleep(200);
+ set_progress(80);
+
+ // End / write
+ const okEnd = await multi_calib_sticks_end();
+ if (!okEnd) {
+ await sleep(500);
+ close_calibrate_window();
+ return;
+ }
+
+ set_progress(100);
+ update_nvs_changes_status(1);
+
+ await sleep(500);
+ close_calibrate_window()
+ show_popup(l("Stick calibration completed"));
+ } catch(e) {
+ la("multi_calibrate_sticks_failed", {"r": e});
+ await sleep(500);
+ close_calibrate_window();
+ return show_popup(err + e);
+ }
}
function close_calibrate_window() {
@@ -2962,23 +2906,23 @@ async function calib_step(i) {
if(i == 2) {
$("#calibNextText").text(l("Initializing..."));
- await new Promise(r => setTimeout(r, 100));
+ await sleep(100);
ret = await multi_calib_sticks_begin();
} else if(i == 6) {
$("#calibNextText").text(l("Sampling..."));
- await new Promise(r => setTimeout(r, 100));
+ await sleep(100);
ret = await multi_calib_sticks_sample();
- await new Promise(r => setTimeout(r, 100));
+ await sleep(100);
$("#calibNextText").text(l("Storing calibration..."));
- await new Promise(r => setTimeout(r, 100));
+ await sleep(100);
ret = await multi_calib_sticks_end();
} else if(i > 2 && i < 6){
$("#calibNextText").text(l("Sampling..."));
- await new Promise(r => setTimeout(r, 100));
+ await sleep(100);
ret = await multi_calib_sticks_sample();
}
if(i >= 2 && i <= 6) {
- await new Promise(r => setTimeout(r, 200));
+ await sleep(200);
$("#calibNext").prop("disabled", false);
$("#btnSpinner").hide();
}