DS Edge: add experimental support

This commit is contained in:
dualshock-tools 2025-04-06 01:47:50 +02:00
parent 41208d2727
commit dbcf62dc76
2 changed files with 170 additions and 35 deletions

135
core.js
View File

@ -197,7 +197,19 @@ async function ds5_flash() {
show_popup(l("Changes saved successfully")); show_popup(l("Changes saved successfully"));
} catch(error) { } catch(error) {
show_popup(l("Error while saving changes: ") + str(error)); show_popup(l("Error while saving changes: ") + toString(error));
}
}
async function ds5_edge_flash() {
la("ds5_edge_flash");
try {
await ds5_edge_flash_modules();
await ds5_reset();
show_popup("<b>" + l("Changes saved successfully") + "</b>.<br><br>" + l("If the calibration is not stored permanently, please double-check the wirings of the hardware mod.") + "<br>" + l("Please disconnect and reconnect the controller."), true);
await disconnect();
} catch(error) {
show_popup(l("Error while saving changes: ") + toString(error));
} }
} }
@ -212,7 +224,7 @@ async function ds4_reset() {
async function ds5_reset() { async function ds5_reset() {
la("ds5_reset"); la("ds5_reset");
try { try {
await device.sendFeatureReport(0x80, alloc_req(0x80, [1,1,0])) await device.sendFeatureReport(0x80, alloc_req(0x80, [1,1]))
} catch(error) { } catch(error) {
} }
} }
@ -441,6 +453,9 @@ async function ds5_nvstatus() {
await device.sendFeatureReport(0x80, alloc_req(0x80, [3,3])) await device.sendFeatureReport(0x80, alloc_req(0x80, [3,3]))
data = lf("ds5_nvstatus", await device.receiveFeatureReport(0x81)) data = lf("ds5_nvstatus", await device.receiveFeatureReport(0x81))
ret = data.getUint32(1, false); ret = data.getUint32(1, false);
if(ret == 0x15010100) {
return 4;
}
if(ret == 0x03030201) { if(ret == 0x03030201) {
$("#d-nvstatus").html("<font color='green'>" + l("locked") + "</font>"); $("#d-nvstatus").html("<font color='green'>" + l("locked") + "</font>");
return 1; // temporary return 1; // temporary
@ -639,11 +654,30 @@ async function ds5_calibrate_sticks_end() {
await device.sendFeatureReport(0x82, alloc_req(0x82, [2,1,1])) await device.sendFeatureReport(0x82, alloc_req(0x82, [2,1,1]))
data = await device.receiveFeatureReport(0x83) data = await device.receiveFeatureReport(0x83)
if(mode == 2) {
if(data.getUint32(0, false) != 0x83010102) { if(data.getUint32(0, false) != 0x83010102) {
d1 = dec2hex32(data.getUint32(0, false)); d1 = dec2hex32(data.getUint32(0, false));
la("ds5_calibrate_sticks_end_failed", {"d1": d1}); la("ds5_calibrate_sticks_failed", {"s": 3, "d1": d1});
show_popup(err + l("Error 3") + " (" + d1 + ")."); close_calibrate_window();
return false; 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 && data.getUint32(0, false) != 0x83010312) {
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 + ").");
}
} }
update_nvs_changes_status(1); update_nvs_changes_status(1);
@ -702,12 +736,32 @@ async function ds5_calibrate_sticks() {
await device.sendFeatureReport(0x82, alloc_req(0x82, [2,1,1])) await device.sendFeatureReport(0x82, alloc_req(0x82, [2,1,1]))
data = await device.receiveFeatureReport(0x83) data = await device.receiveFeatureReport(0x83)
if(mode == 2) {
if(data.getUint32(0, false) != 0x83010102) { if(data.getUint32(0, false) != 0x83010102) {
d1 = dec2hex32(data.getUint32(0, false)); d1 = dec2hex32(data.getUint32(0, false));
la("ds5_calibrate_sticks_failed", {"s": 3, "d1": d1}); la("ds5_calibrate_sticks_failed", {"s": 3, "d1": d1});
close_calibrate_window(); close_calibrate_window();
return show_popup(err + l("Error 3") + " (" + d1 + ")."); 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); set_progress(100);
update_nvs_changes_status(1); update_nvs_changes_status(1);
@ -756,11 +810,30 @@ async function ds5_calibrate_range_end() {
// Assert // Assert
data = await device.receiveFeatureReport(0x83) data = await device.receiveFeatureReport(0x83)
if(mode == 2) {
if(data.getUint32(0, false) != 0x83010202) { if(data.getUint32(0, false) != 0x83010202) {
d1 = dec2hex32(data.getUint32(0, false)); d1 = dec2hex32(data.getUint32(0, false));
la("ds5_calibrate_range_end_failed", {"d1": d1}); la("ds5_calibrate_range_end_failed", {"d1": d1});
close_calibrate_window(); close_calibrate_window();
return show_popup(err + l("Error 1") + " (" + d1 + ")."); return show_popup(err + l("Error 3") + " (" + d1 + ").");
}
} else {
if(data.getUint32(0, false) != 0x83010201) {
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 + ").");
}
await device.sendFeatureReport(0x82, alloc_req(0x82, [2,1,2]))
data = await device.receiveFeatureReport(0x83)
if(data.getUint32(0, false) != 0x83010203) {
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 + ").");
}
} }
update_nvs_changes_status(1); update_nvs_changes_status(1);
@ -778,7 +851,7 @@ async function ds5_nvlock() {
la("ds5_nvlock"); la("ds5_nvlock");
try { try {
await device.sendFeatureReport(0x80, alloc_req(0x80, [3,1])) await device.sendFeatureReport(0x80, alloc_req(0x80, [3,1]))
data = await device.receiveFeatureReport(0x83) data = await device.receiveFeatureReport(0x81)
} catch(e) { } catch(e) {
await new Promise(r => setTimeout(r, 500)); await new Promise(r => setTimeout(r, 500));
close_calibrate_window(); close_calibrate_window();
@ -786,11 +859,36 @@ async function ds5_nvlock() {
} }
} }
async function ds5_edge_flash_modules() {
la("ds5_edge_flash_modules");
var modal = null;
try {
modal = new bootstrap.Modal(document.getElementById('edgeProgressModal'), {})
modal.show();
if(device != null) {
await device.sendFeatureReport(0x80, alloc_req(0x80, [21, 1, 1]))
}
await new Promise(r => setTimeout(r, 500));
if(device != null) {
data = await device.receiveFeatureReport(0x81)
}
await new Promise(r => setTimeout(r, 1000));
modal.hide();
await ds5_reset();
} catch(e) {
modal.hide();
await new Promise(r => setTimeout(r, 500));
return show_popup(l("NVS Unlock failed: ") + e);
}
}
async function ds5_nvunlock() { async function ds5_nvunlock() {
la("ds5_nvunlock"); la("ds5_nvunlock");
try { try {
await device.sendFeatureReport(0x80, alloc_req(0x80, [3,2, 101, 50, 64, 12])) await device.sendFeatureReport(0x80, alloc_req(0x80, [3,2, 101, 50, 64, 12]))
data = await device.receiveFeatureReport(0x83) data = await device.receiveFeatureReport(0x81)
} catch(e) { } catch(e) {
await new Promise(r => setTimeout(r, 500)); await new Promise(r => setTimeout(r, 500));
close_calibrate_window(); close_calibrate_window();
@ -1515,9 +1613,17 @@ async function continue_connection(report) {
$("#ds5finetune").hide() $("#ds5finetune").hide()
if(await ds5_info()) { if(await ds5_info()) {
connected = true; connected = true;
mode = 0; mode = 3;
devname = l("Sony DualSense Edge"); devname = l("Sony DualSense Edge");
disable_btn |= 8; }
n = await ds5_nvstatus();
if(n == 4) {
// 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;
} }
} else { } else {
$("#btnconnect").prop("disabled", false); $("#btnconnect").prop("disabled", false);
@ -1543,6 +1649,9 @@ async function continue_connection(report) {
return; return;
} }
if(mode == 3)
show_edge_modal();
if(disable_btn != 0) if(disable_btn != 0)
update_disable_btn(); update_disable_btn();
@ -1573,8 +1682,6 @@ function update_disable_btn() {
show_popup(l("The device appears to be a DS4 clone. All functionalities are disabled.")); show_popup(l("The device appears to be a DS4 clone. All functionalities are disabled."));
} else if(disable_btn & 2 && !(last_disable_btn & 2)) { } else if(disable_btn & 2 && !(last_disable_btn & 2)) {
show_popup(l("This DualSense controller has outdated firmware.") + "<br>" + l("Please update the firmware and try again."), true); show_popup(l("This DualSense controller has outdated firmware.") + "<br>" + l("Please update the firmware and try again."), true);
} else if(disable_btn & 8 && !(last_disable_btn & 8)) {
show_edge_modal();
} else if(disable_btn & 4 && !(last_disable_btn & 4)) { } else if(disable_btn & 4 && !(last_disable_btn & 4)) {
show_popup(l("Please charge controller battery over 30% to use this tool.")); show_popup(l("Please charge controller battery over 30% to use this tool."));
} }
@ -1640,8 +1747,10 @@ var curModal = null
async function multi_flash() { async function multi_flash() {
if(mode == 1) if(mode == 1)
ds4_flash(); ds4_flash();
else else if(mode == 2)
ds5_flash(); ds5_flash();
else if(mode == 3)
ds5_edge_flash();
update_nvs_changes_status(0); update_nvs_changes_status(0);
} }

View File

@ -110,7 +110,7 @@ dl.row dd { font-family: monospace; }
</div> </div>
<div id="offlinebar" class="vstack p-2" style="display: none;"> <div id="offlinebar" class="vstack p-2" style="display: none;">
<p class="ds-i18n">Connect a DualShock 4 or a DualSense controller to your computer and press Connect.</p> <p class="ds-i18n">Connect a DualShock 4, a DualSense or DualSense Edge controller to your computer and press Connect.</p>
<button id="btnconnect" type="button" class="btn btn-outline-primary" onclick="connect()"> <button id="btnconnect" type="button" class="btn btn-outline-primary" onclick="connect()">
<span class="spinner-border spinner-border-sm" style="display: none;" id="connectspinner" aria-hidden="true"></span> <span class="spinner-border spinner-border-sm" style="display: none;" id="connectspinner" aria-hidden="true"></span>
<span class="ds-i18n">Connect</span> <span class="ds-i18n">Connect</span>
@ -554,25 +554,51 @@ dl.row dd { font-family: monospace; }
</div> </div>
</div> </div>
<!-- Edge in progress Modal -->
<div class="modal fade" id="edgeProgressModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="edgeProgressLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5 ds-i18n" id="edgeProgressLabel">Storing calibration</h1>
</div>
<div class="modal-body">
<p class="ds-i18n">Calibration is being stored in the stick modules...</p>
<p class="ds-i18n">Please wait.</p>
</div>
</div>
</div>
</div>
<div class="modal fade" id="edgeModal" tabindex="-1" aria-labelledby="modal-title" aria-hidden="true"> <div class="modal fade" id="edgeModal" tabindex="-1" aria-labelledby="modal-title" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered"> <div class="modal-dialog modal-dialog-centered">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title ds-i18n">Dualsense Edge Compatibility</h5> <h5 class="modal-title ds-i18n">DualSense Edge Calibration (Experimental)</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<div class="modal-body p-4" id="donateBody"> <div class="modal-body p-4" id="donateBody">
<p class="ds-i18n">The DualShock Calibration GUI does not currently support the DualSense Edge.</p> <p>Support for calibrating DualSense Edge stick modules is now available as an <b>experimental feature</b>.</p>
<p class="ds-i18n">I'm actively working on adding compatibility, the primary challenge lies in storing data into the stick modules.</p> <p>Please note: the stick modules on the DS Edge <b>cannot be calibrated via software alone</b>.</p>
<p><span class="ds-i18n">If this tool has been helpful to you or you want to see DualSense Edge support arrive faster, please consider supporting the project with a</span> <p>To store a custom calibration on the stick's internal memory, a <b>hardware modification</b> is required. This involves temporarily disabling write protection by applying <b>+1.8V</b> to a specific test point on each module.</p>
<a href="https://paypal.me/alaincarlucci" target="_blank" class="text-body-secondary ds-i18n">donation</a>.</p> <p></p>
<p class="ds-i18n">Thank you for your generosity and support!</p>
<p>You can do this in two ways:
<ul><li><b>Internally</b>: by soldering a wire from a +1.8V source to the write-protect TP, as shown here</li>
<li><b>Externally</b>: by applying +1.8V directly to the visible test point without opening the controller</li>
</ul>
</p>
<p><b>This is only for advanced users. If you're not sure what you're doing, please dont attempt it.</b></p>
<p>More details and images <a href="https://github.com/lewy20041/Dualsense_Edge_Modules_Callibration">here</a>.</p>
<p>We are not responsible for any damage caused by attempting this modification.</p>
<p>For more info or help, feel free to reach out on Discord.</p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button onclick="window.open('https://paypal.me/alaincarlucci')" type="button" class="btn btn-success" data-bs-dismiss="modal"> <button type="button" class="btn btn-primary ds-i18n" onclick="welcome_accepted();">Understood</button>
<svg class="bi" width="18" height="18"><use xlink:href="#paypal"/></svg>&nbsp;&nbsp;<span class="ds-i18n">Support this project</span>
</button>
</div> </div>
</div> </div>
</div> </div>
@ -797,7 +823,7 @@ dl.row dd { font-family: monospace; }
<div class="container"> <div class="container">
<footer> <footer>
<div class="d-flex flex-column flex-sm-row justify-content-between py-4 my-4 border-top" id="footbody"> <div class="d-flex flex-column flex-sm-row justify-content-between py-4 my-4 border-top" id="footbody">
<p><a target="_blank" href="https://github.com/dualshock-tools/dualshock-tools.github.io/commits/main/"><span class="ds-i18n">Version</span> 2.1</a> (2025-01-25) - <a href="#" class="ds-i18n" onclick="show_donate_modal();">Support this project</a>&nbsp;<span id="authorMsg"></span></p> <p><a target="_blank" href="https://github.com/dualshock-tools/dualshock-tools.github.io/commits/main/"><span class="ds-i18n">Version</span> 2.2<i>beta</i></a> (2025-04-06) - <a href="#" class="ds-i18n" onclick="show_donate_modal();">Support this project</a>&nbsp;<span id="authorMsg"></span></p>
<ul class="list-unstyled d-flex"> <ul class="list-unstyled d-flex">
<li class="ms-3"><a class="link-body-emphasis" href="mailto:ds4@the.al" target="_blank"><svg class="bi" width="24" height="24"><use xlink:href="#mail"/></svg></a></li> <li class="ms-3"><a class="link-body-emphasis" href="mailto:ds4@the.al" target="_blank"><svg class="bi" width="24" height="24"><use xlink:href="#mail"/></svg></a></li>