You've already forked ESP32-DAPLink
mirror of
https://github.com/m5stack/ESP32-DAPLink.git
synced 2026-05-20 11:37:17 -07:00
416 lines
13 KiB
HTML
416 lines
13 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
<title>WebSerial</title>
|
|
<link rel="icon" href="favicon.ico" type="image/x-icon">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<style type="text/css">
|
|
div {
|
|
display: block;
|
|
}
|
|
|
|
a {
|
|
margin: 0.4rem;
|
|
text-decoration: none;
|
|
}
|
|
|
|
*,
|
|
::after,
|
|
::before {
|
|
-webkit-box-sizing: inherit;
|
|
box-sizing: inherit;
|
|
}
|
|
|
|
p {
|
|
margin: 0 0 1rem;
|
|
}
|
|
|
|
body {
|
|
overscroll-behavior: none;
|
|
height: 100%;
|
|
width: 100%;
|
|
margin: 0;
|
|
padding: 0;
|
|
font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI",
|
|
Roboto, "Helvetica Neue", sans-serif;
|
|
}
|
|
|
|
.button-width {
|
|
width: 10ch;
|
|
/* 设置为你想要的宽度 */
|
|
}
|
|
|
|
.text-center {
|
|
text-align: center;
|
|
}
|
|
|
|
.gray {
|
|
color: #667189;
|
|
}
|
|
|
|
.shadow {
|
|
filter: drop-shadow(0 4px 3px rgb(0 0 0 / 0.07)) drop-shadow(0 2px 2px rgb(0 0 0 / 0.06));
|
|
}
|
|
|
|
.w-full {
|
|
width: 100%;
|
|
}
|
|
|
|
.grid {
|
|
display: grid;
|
|
}
|
|
|
|
.gap-2 {
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.flex {
|
|
display: flex;
|
|
}
|
|
|
|
.grow {
|
|
flex-grow: 1;
|
|
}
|
|
|
|
.justify-items-end {
|
|
justify-items: end;
|
|
}
|
|
|
|
.rounded {
|
|
border-radius: 0.5rem;
|
|
}
|
|
|
|
.section {
|
|
box-sizing: border-box;
|
|
background-color: #f8f9fa;
|
|
}
|
|
|
|
.main {
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: center;
|
|
height: 100dvh;
|
|
}
|
|
|
|
.main .pannel {
|
|
position: relative;
|
|
border: #fff;
|
|
border-style: solid;
|
|
border-width: 0.5rem;
|
|
border-radius: 1rem;
|
|
background-color: #fff;
|
|
max-width: 100rem;
|
|
width: calc(100% - 1rem);
|
|
font-size: medium;
|
|
}
|
|
|
|
.pannel button {
|
|
cursor: pointer;
|
|
padding: 8px 10px 8px;
|
|
font-size: medium;
|
|
outline-style: none;
|
|
border: 0px;
|
|
color: #fff;
|
|
background-color: #0067f4;
|
|
}
|
|
|
|
.pannel button:active {
|
|
background-color: #004cb3;
|
|
/* Darker blue */
|
|
color: #fff;
|
|
}
|
|
|
|
.pannel button:disabled {
|
|
background-color: #5a6169;
|
|
}
|
|
|
|
.pannel #record {
|
|
min-height: 3.25rem;
|
|
padding: 0.5rem;
|
|
resize: vertical;
|
|
overscroll-behavior: none;
|
|
}
|
|
|
|
#control-button {
|
|
position: absolute;
|
|
top: 0.5rem;
|
|
right: 0.5rem;
|
|
}
|
|
|
|
#control-button button {
|
|
background-color: #5a6169;
|
|
}
|
|
|
|
#baudrate-select,
|
|
#command-text {
|
|
border: 2px solid #0067f4;
|
|
border-radius: 4px;
|
|
padding: 4px;
|
|
}
|
|
|
|
.alert span {
|
|
user-select: none;
|
|
background: #ffe14d;
|
|
padding: 2px 10px;
|
|
display: block;
|
|
color: black;
|
|
}
|
|
|
|
.footer {
|
|
padding: 1rem 0.5rem;
|
|
background-color: white;
|
|
}
|
|
|
|
.copyright {
|
|
padding-bottom: 1rem;
|
|
}
|
|
|
|
.float-left {
|
|
float: left;
|
|
}
|
|
|
|
.float-right {
|
|
float: right;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div class="section main text-center">
|
|
<h1>WebSerial</h1>
|
|
<div class="pannel shadow grid gap-2">
|
|
<div id="control-button">
|
|
<button class="rounded shadow" onclick="terminalClean()" id="clean-button">
|
|
<svg viewBox="64 64 896 896" focusable="false" data-icon="delete" width="1em" height="1em"
|
|
fill="currentColor" aria-hidden="true">
|
|
<path
|
|
d="M864 256H736v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zm-200 0H360v-72h304v72z">
|
|
</path>
|
|
</svg>
|
|
</button>
|
|
<button class="rounded shadow" id="lock-button">
|
|
<svg viewBox="64 64 896 896" focusable="false" data-icon="lock" width="1em" height="1em"
|
|
fill="currentColor" aria-hidden="true">
|
|
<path
|
|
d="M832 464h-68V240c0-70.7-57.3-128-128-128H388c-70.7 0-128 57.3-128 128v224h-68c-17.7 0-32 14.3-32 32v384c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V496c0-17.7-14.3-32-32-32zM332 240c0-30.9 25.1-56 56-56h248c30.9 0 56 25.1 56 56v224H332V240zm460 600H232V536h560v304zM484 701v53c0 4.4 3.6 8 8 8h40c4.4 0 8-3.6 8-8v-53a48.01 48.01 0 10-56 0z">
|
|
</path>
|
|
</svg>
|
|
</button>
|
|
<button class="rounded shadow" id="timestamp-button">
|
|
<svg viewBox="64 64 896 896" focusable="false" data-icon="clock-circle" width="1em" height="1em"
|
|
fill="currentColor" aria-hidden="true">
|
|
<path
|
|
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z">
|
|
</path>
|
|
<path
|
|
d="M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z">
|
|
</path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<textarea class="w-full rounded" title="record" id="record" cols="40" rows="50" disabled></textarea>
|
|
<div class="flex w-full grid gap-2" id="terminal">
|
|
<input type="text" name="cmd" id="command-text" class="grow rounded" />
|
|
<button class="grid justify-items-center rounded shadow button-width" id="command-button"> SEND
|
|
</button>
|
|
</div>
|
|
<div class="flex w-full grid gap-2" id="terminal">
|
|
<select name="baudrate" id="baudrate-select" class="grow rounded">
|
|
<option value="115200">115200</option>
|
|
<option value="460800">460800</option>
|
|
<option value="256000">256000</option>
|
|
<option value="230400">230400</option>
|
|
<option value="57600">57600</option>
|
|
<option value="38400">38400</option>
|
|
<option value="19200">19200</option>
|
|
<option value="9600">9600</option>
|
|
<option value="4800">4800</option>
|
|
<option value="2400">2400</option>
|
|
<option value="1200">1200</option>
|
|
</select>
|
|
<button class="grid justify-items-center rounded shadow button-width" id="baudrate-button">
|
|
SET
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<h1 />
|
|
</div>
|
|
<div class="section footer">
|
|
<div class="copyright">
|
|
<div class="float-left"></div>
|
|
<div class="float-right gray">
|
|
Powered By<a href="https://www.homeboyc.cn/" class="gray">Atom</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
<script>
|
|
let websocket;
|
|
let textArea = document.getElementById("record");
|
|
let enableFlowLock = false;
|
|
let enableTimestamp = true;
|
|
let cache = '';
|
|
let timeoutId = null;
|
|
let timestamp = '';
|
|
|
|
initWebPage();
|
|
|
|
function lockClicked(btn) {
|
|
enableFlowLock = !enableFlowLock;
|
|
|
|
if (enableFlowLock) {
|
|
this.style.backgroundColor = '#0067f4';
|
|
} else {
|
|
this.style.backgroundColor = '#5a6169';
|
|
}
|
|
}
|
|
|
|
function timestampClicked(btn) {
|
|
enableTimestamp = !enableTimestamp;
|
|
|
|
if (enableTimestamp) {
|
|
this.style.backgroundColor = '#0067f4';
|
|
} else {
|
|
this.style.backgroundColor = '#5a6169';
|
|
}
|
|
}
|
|
|
|
function initWebPage() {
|
|
document.getElementById("command-button").disabled = true;
|
|
document.getElementById("command-text").disabled = true;
|
|
document.getElementById("baudrate-select").disabled = true;
|
|
document.getElementById("baudrate-button").disabled = true;
|
|
document.getElementById("lock-button").addEventListener('click', lockClicked);
|
|
|
|
document.getElementById("timestamp-button").addEventListener('click', timestampClicked);
|
|
if (enableTimestamp)
|
|
document.getElementById("timestamp-button").style.backgroundColor = '#0067f4';
|
|
|
|
document.getElementById("clean-button").style.backgroundColor = '#0067f4';
|
|
terminalWrite("[WebSerial] Connecting...\n");
|
|
initButton();
|
|
trapEnterKeyPress();
|
|
initWebSocket();
|
|
}
|
|
|
|
function initWebSocket() {
|
|
console.log("Trying to open a WebSocket connection...");
|
|
websocket = new WebSocket('ws://' + location.hostname + ':80/webserial_socket');
|
|
websocket.onopen = onOpen;
|
|
websocket.onclose = onClose;
|
|
websocket.onmessage = onMessage;
|
|
}
|
|
|
|
function initButton() {
|
|
document
|
|
.getElementById("command-button")
|
|
.addEventListener("click", sendCommand);
|
|
document.
|
|
getElementById("baudrate-button")
|
|
.addEventListener("click", sendCommand);
|
|
}
|
|
|
|
function trapEnterKeyPress() {
|
|
document
|
|
.getElementById("command-text")
|
|
.addEventListener("keypress", enterPressed);
|
|
}
|
|
|
|
function enterPressed(event) {
|
|
if (event.key === "Enter") {
|
|
event.preventDefault();
|
|
document.getElementById("command-button").click();
|
|
}
|
|
}
|
|
|
|
function onOpen(event) {
|
|
console.log("Connection opened");
|
|
terminalWrite("[WebSerial] Connected...\n");
|
|
document.getElementById("command-button").disabled = false;
|
|
document.getElementById("command-text").disabled = false;
|
|
document.getElementById("baudrate-select").disabled = false;
|
|
document.getElementById("baudrate-button").disabled = false;
|
|
}
|
|
|
|
function onClose(event) {
|
|
console.log("Connection closed");
|
|
setTimeout(initWebSocket, 2000);
|
|
}
|
|
|
|
function onMessage(event) {
|
|
terminalWrite(event.data);
|
|
}
|
|
|
|
function terminamWriteLine(line) {
|
|
|
|
if (line.trim() !== '') {
|
|
line = timestamp + line + "\n";
|
|
textArea.value += line;
|
|
|
|
if (!enableFlowLock) {
|
|
textArea.scrollTop = textArea.scrollHeight;
|
|
}
|
|
}
|
|
}
|
|
|
|
function terminalWrite(raw) {
|
|
let now = new Date();
|
|
timestamp = "[" + now.toLocaleTimeString() + "." + now.getMilliseconds().toString().padStart(3, '0') + "] ";
|
|
|
|
if (enableTimestamp) {
|
|
cache += raw;
|
|
|
|
if (cache.includes('\n')) {
|
|
let lines = cache.split('\n');
|
|
cache = lines.pop();
|
|
|
|
lines.forEach(line => {
|
|
terminamWriteLine(line);
|
|
});
|
|
}
|
|
|
|
if (cache !== '') {
|
|
clearTimeout(timeoutId);
|
|
timeoutId = setTimeout(() => {
|
|
terminamWriteLine(cache);
|
|
cache = '';
|
|
}, 100);
|
|
}
|
|
} else {
|
|
textArea.value += raw;
|
|
if (!enableFlowLock) {
|
|
textArea.scrollTop = textArea.scrollHeight;
|
|
}
|
|
}
|
|
}
|
|
|
|
function terminalClean() {
|
|
textArea.value = "";
|
|
textArea.scrollTop = textArea.scrollHeight;
|
|
}
|
|
|
|
function sendCommand() {
|
|
websocket.send(document.getElementById("command-text").value);
|
|
}
|
|
|
|
function setBaudrate() {
|
|
var baudrateSelect = document.getElementById("baudrate-select");
|
|
var selectedBaudrate = baudrateSelect.value;
|
|
|
|
var xhr = new XMLHttpRequest();
|
|
xhr.open("POST", "/api/set-baudrate", true);
|
|
xhr.setRequestHeader("Content-Type", "application/json");
|
|
xhr.onreadystatechange = function () {
|
|
if (xhr.readyState === 4 && xhr.status === 200) {
|
|
console.log("Baudrate set successfully");
|
|
}
|
|
};
|
|
xhr.send(JSON.stringify({ baudrate: selectedBaudrate }));
|
|
}
|
|
</script>
|
|
|
|
</html> |