sp / index.html
drbaker171's picture
Add 1 files
2274cce verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ESP32 Serial Communication</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.serial-console {
height: 300px;
overflow-y: auto;
background-color: #1a202c;
color: #e2e8f0;
padding: 1rem;
border-radius: 0.375rem;
font-family: monospace;
white-space: pre-wrap;
}
.blink {
animation: blink 1s step-end infinite;
}
@keyframes blink {
from, to { opacity: 1; }
50% { opacity: 0.5; }
}
.toast {
animation: fadeIn 0.3s, fadeOut 0.3s 2.7s;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes fadeOut {
from { opacity: 1; transform: translateY(0); }
to { opacity: 0; transform: translateY(20px); }
}
</style>
</head>
<body class="bg-gray-100 min-h-screen">
<div class="container mx-auto px-4 py-8 max-w-4xl">
<div class="bg-white rounded-xl shadow-md overflow-hidden p-6">
<div class="flex items-center mb-8">
<div class="p-3 rounded-full bg-blue-100 text-blue-600 mr-4">
<i class="fas fa-microchip text-2xl"></i>
</div>
<div>
<h1 class="text-3xl font-bold text-gray-800">ESP32 Serial Communication</h1>
<p class="text-gray-600">Connect to your CYD ESP32-2432S028 device via Web Serial API</p>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
<div class="bg-blue-50 rounded-lg p-4 border border-blue-200">
<h3 class="font-semibold text-blue-800 mb-2">Connection Status</h3>
<div id="connectionStatus" class="flex items-center">
<div class="h-3 w-3 rounded-full bg-gray-400 mr-2"></div>
<span>Disconnected</span>
</div>
<div id="portInfo" class="mt-2 text-sm text-gray-600 hidden">
<div>Device: <span id="portName" class="font-medium">-</span></div>
<div>Baud Rate: <span id="currentBaudRate" class="font-medium">-</span></div>
</div>
</div>
<div class="bg-gray-50 rounded-lg p-4 border border-gray-200">
<h3 class="font-semibold text-gray-800 mb-2">Data Statistics</h3>
<div class="grid grid-cols-2 gap-2 text-sm">
<div>Bytes Sent: <span id="bytesSent" class="font-medium">0</span></div>
<div>Bytes Received: <span id="bytesReceived" class="font-medium">0</span></div>
</div>
</div>
</div>
<div class="mb-6">
<div class="flex flex-wrap gap-3 mb-4">
<button id="connectBtn" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition flex items-center">
<i class="fas fa-plug mr-2"></i> Connect
</button>
<button id="disconnectBtn" disabled class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition flex items-center">
<i class="fas fa-unlink mr-2"></i> Disconnect
</button>
<div class="flex items-center ml-auto">
<label for="baudRate" class="mr-2 text-sm text-gray-700">Baud Rate:</label>
<select id="baudRate" class="border border-gray-300 rounded px-3 py-1">
<option>9600</option>
<option>19200</option>
<option selected>115200</option>
<option>57600</option>
<option>38400</option>
</select>
</div>
</div>
<div class="mb-4">
<label for="dataInput" class="block text-sm font-medium text-gray-700 mb-1">Send Command</label>
<div class="flex">
<input type="text" id="dataInput" placeholder="Enter command to send"
class="flex-1 border border-gray-300 rounded-l-lg px-4 py-2 focus:ring-blue-500 focus:border-blue-500">
<button id="sendBtn" disabled class="px-4 py-2 bg-blue-600 text-white rounded-r-lg hover:bg-blue-700">
<i class="fas fa-paper-plane mr-1"></i> Send
</button>
</div>
</div>
<div>
<div class="flex justify-between items-center mb-1">
<label class="block text-sm font-medium text-gray-700">Serial Console</label>
<button id="clearConsoleBtn" class="text-sm text-blue-600 hover:text-blue-800">
<i class="fas fa-trash-alt mr-1"></i> Clear
</button>
</div>
<div id="serialConsole" class="serial-console">
<div class="text-gray-400">Waiting for connection...</div>
</div>
</div>
</div>
<div class="bg-yellow-50 border-l-4 border-yellow-400 p-4 mb-6">
<div class="flex">
<div class="flex-shrink-0">
<i class="fas fa-info-circle text-yellow-400 mt-1 mr-3"></i>
</div>
<div>
<h3 class="text-sm font-medium text-yellow-800">Important Notes</h3>
<div class="mt-2 text-sm text-yellow-700">
<p>• The Web Serial API requires Chrome/Edge 89+ or Opera 76+</p>
<p>• Must be used in a secure context (HTTPS or localhost)</p>
<p>• Requires user interaction to initiate connection</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="toastContainer" class="fixed bottom-4 right-4 space-y-2"></div>
<script>
// DOM elements
const connectBtn = document.getElementById('connectBtn');
const disconnectBtn = document.getElementById('disconnectBtn');
const sendBtn = document.getElementById('sendBtn');
const dataInput = document.getElementById('dataInput');
const baudRate = document.getElementById('baudRate');
const serialConsole = document.getElementById('serialConsole');
const clearConsoleBtn = document.getElementById('clearConsoleBtn');
const bytesSent = document.getElementById('bytesSent');
const bytesReceived = document.getElementById('bytesReceived');
const connectionStatus = document.getElementById('connectionStatus');
const portInfo = document.getElementById('portInfo');
const portName = document.getElementById('portName');
const currentBaudRate = document.getElementById('currentBaudRate');
const toastContainer = document.getElementById('toastContainer');
// Serial port variables
let port = null;
let reader = null;
let writer = null;
let keepReading = true;
let sentBytes = 0;
let receivedBytes = 0;
// Check Web Serial API support
function checkSupport() {
if (!('serial' in navigator)) {
showToast('Web Serial API not supported in this browser', 'error');
connectBtn.disabled = true;
connectBtn.innerHTML = '<i class="fas fa-times-circle mr-2"></i> Not Supported';
}
}
// Connect to serial port
async function connect() {
try {
// Request port access
port = await navigator.serial.requestPort();
// Open the port
const selectedBaudRate = parseInt(baudRate.value);
await port.open({ baudRate: selectedBaudRate });
// Update UI
updateConnectionStatus(true);
portName.textContent = port.getInfo().usbProductId ?
`USB Device (${port.getInfo().usbProductId})` : 'Serial Port';
currentBaudRate.textContent = selectedBaudRate;
portInfo.classList.remove('hidden');
// Clear console and add welcome message
clearConsole();
addToConsole('Connected to serial port', 'info');
addToConsole(`Baud rate: ${selectedBaudRate}`, 'info');
// Start reading from the port
readFromPort();
showToast('Successfully connected to device', 'success');
} catch (err) {
if (err.name === 'NotFoundError') {
showToast('No port selected by user', 'warning');
} else {
showToast('Connection error: ' + err.message, 'error');
console.error('Connection error:', err);
}
}
}
// Disconnect from serial port
async function disconnect() {
try {
keepReading = false;
if (reader) {
await reader.cancel();
}
if (port) {
await port.close();
}
// Update UI
updateConnectionStatus(false);
addToConsole('Disconnected from serial port', 'info');
showToast('Disconnected from device', 'info');
} catch (err) {
showToast('Disconnection error: ' + err.message, 'error');
console.error('Disconnection error:', err);
}
}
// Read data from serial port
async function readFromPort() {
keepReading = true;
try {
while (port.readable && keepReading) {
reader = port.readable.getReader();
try {
while (keepReading) {
const { value, done } = await reader.read();
if (done) {
break;
}
// Process received data
const textDecoder = new TextDecoder();
const text = textDecoder.decode(value);
receivedBytes += text.length;
bytesReceived.textContent = receivedBytes;
// Add to console with timestamp
addToConsole(text, 'received');
}
} catch (err) {
showToast('Error reading data: ' + err.message, 'error');
console.error('Read error:', err);
} finally {
reader.releaseLock();
}
}
} catch (err) {
showToast('Port error: ' + err.message, 'error');
console.error('Port error:', err);
disconnect();
}
}
// Send data to serial port
async function sendData() {
if (!port || !port.writable) {
showToast('Port is not connected', 'error');
return;
}
const data = dataInput.value.trim();
if (!data) {
showToast('Please enter data to send', 'warning');
return;
}
try {
writer = port.writable.getWriter();
const textEncoder = new TextEncoder();
const encodedData = textEncoder.encode(data + '\n');
await writer.write(encodedData);
sentBytes += encodedData.length;
bytesSent.textContent = sentBytes;
// Add to console as sent data
addToConsole(data, 'sent');
dataInput.value = '';
showToast('Data sent successfully', 'success');
} catch (err) {
showToast('Error sending data: ' + err.message, 'error');
console.error('Send error:', err);
} finally {
if (writer) {
writer.releaseLock();
}
}
}
// Add message to console
function addToConsole(message, type) {
const now = new Date();
const timestamp = `[${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}] `;
// Create message element
const messageElement = document.createElement('div');
// Style based on message type
if (type === 'info') {
messageElement.className = 'text-blue-400';
messageElement.innerHTML = timestamp + message;
} else if (type === 'sent') {
messageElement.className = 'text-green-400';
messageElement.innerHTML = timestamp + '&gt; ' + message;
} else { // received
messageElement.className = 'text-white';
messageElement.innerHTML = timestamp + message.replace(/\n/g, '<br>');
}
// Remove "waiting for connection" message if it's there
if (serialConsole.firstChild && serialConsole.firstChild.textContent === 'Waiting for connection...') {
serialConsole.removeChild(serialConsole.firstChild);
}
// Add message to console
serialConsole.appendChild(messageElement);
serialConsole.scrollTop = serialConsole.scrollHeight;
}
// Clear console
function clearConsole() {
serialConsole.innerHTML = '<div class="text-gray-400">Waiting for connection...</div>';
}
// Update connection status UI
function updateConnectionStatus(isConnected) {
if (isConnected) {
connectionStatus.innerHTML = '<div class="h-3 w-3 rounded-full bg-green-500 mr-2"></div><span>Connected</span>';
connectBtn.disabled = true;
disconnectBtn.disabled = false;
sendBtn.disabled = false;
} else {
connectionStatus.innerHTML = '<div class="h-3 w-3 rounded-full bg-gray-400 mr-2"></div><span>Disconnected</span>';
connectBtn.disabled = false;
disconnectBtn.disabled = true;
sendBtn.disabled = true;
portInfo.classList.add('hidden');
port = null;
}
}
// Show toast notification
function showToast(message, type) {
const toast = document.createElement('div');
toast.className = `toast px-4 py-2 rounded-md shadow-md text-white ${
type === 'success' ? 'bg-green-500' :
type === 'error' ? 'bg-red-500' :
type === 'warning' ? 'bg-yellow-500' : 'bg-blue-500'
}`;
toast.textContent = message;
// Add to container and auto-remove after animation
toastContainer.appendChild(toast);
setTimeout(() => {
toast.remove();
}, 3000);
}
// Event listeners
connectBtn.addEventListener('click', connect);
disconnectBtn.addEventListener('click', disconnect);
sendBtn.addEventListener('click', sendData);
clearConsoleBtn.addEventListener('click', clearConsole);
dataInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
sendData();
}
});
// Initialize
checkSupport();
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=drbaker171/sp" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>