Fix upload page to split html
This commit is contained in:
@ -21,157 +21,6 @@
|
||||
|
||||
static const char *TAG = "OTA_SERVER";
|
||||
|
||||
// HTML page for OTA update
|
||||
static const char *ota_html =
|
||||
"<!DOCTYPE html>"
|
||||
"<html>"
|
||||
"<head>"
|
||||
"<title>ESP32 OTA Update</title>"
|
||||
"<meta name='viewport' content='width=device-width, initial-scale=1'>"
|
||||
"<style>"
|
||||
"body { font-family: Arial, sans-serif; margin: 40px; background-color: #f0f0f0; }"
|
||||
".container { max-width: 600px; margin: 0 auto; background-color: white; padding: 20px; border-radius: 10px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }"
|
||||
"h1 { color: #333; text-align: center; }"
|
||||
".info { background-color: #e7f3ff; border-left: 4px solid #2196F3; padding: 10px; margin-bottom: 20px; }"
|
||||
".upload-area { border: 2px dashed #ccc; border-radius: 5px; padding: 30px; text-align: center; margin: 20px 0; }"
|
||||
".upload-area.dragover { background-color: #e7f3ff; border-color: #2196F3; }"
|
||||
"input[type='file'] { display: none; }"
|
||||
".btn { background-color: #4CAF50; color: white; padding: 12px 24px; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; }"
|
||||
".btn:hover { background-color: #45a049; }"
|
||||
".btn:disabled { background-color: #cccccc; cursor: not-allowed; }"
|
||||
".progress { width: 100%; background-color: #f0f0f0; border-radius: 4px; margin-top: 20px; display: none; }"
|
||||
".progress-bar { width: 0%; height: 30px; background-color: #4CAF50; border-radius: 4px; text-align: center; line-height: 30px; color: white; transition: width 0.3s; }"
|
||||
".status { margin-top: 20px; padding: 10px; border-radius: 4px; display: none; }"
|
||||
".status.success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }"
|
||||
".status.error { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }"
|
||||
".file-info { margin-top: 10px; font-style: italic; color: #666; }"
|
||||
"</style>"
|
||||
"</head>"
|
||||
"<body>"
|
||||
"<div class='container'>"
|
||||
"<h1>ESP32 OTA Update</h1>"
|
||||
"<div class='info'>"
|
||||
"<strong>Current Version:</strong> <span id='version'>%s</span><br>"
|
||||
"<strong>Free Space:</strong> <span id='free-space'>%u KB</span>"
|
||||
"</div>"
|
||||
"<div class='upload-area' id='upload-area'>"
|
||||
"<p>Drag and drop firmware file here or click to select</p>"
|
||||
"<input type='file' id='file' accept='.bin'>"
|
||||
"<button class='btn' onclick='document.getElementById(\"file\").click()'>Select File</button>"
|
||||
"<div class='file-info' id='file-info'></div>"
|
||||
"</div>"
|
||||
"<button class='btn' id='upload-btn' onclick='uploadFirmware()' disabled>Upload Firmware</button>"
|
||||
"<div class='progress' id='progress'>"
|
||||
"<div class='progress-bar' id='progress-bar'>0%%</div>"
|
||||
"</div>"
|
||||
"<div class='status' id='status'></div>"
|
||||
"</div>"
|
||||
"<script>"
|
||||
"console.log('OTA page loaded');"
|
||||
"let selectedFile = null;"
|
||||
"const uploadArea = document.getElementById('upload-area');"
|
||||
"const fileInput = document.getElementById('file');"
|
||||
"const uploadBtn = document.getElementById('upload-btn');"
|
||||
"const progressDiv = document.getElementById('progress');"
|
||||
"const progressBar = document.getElementById('progress-bar');"
|
||||
"const statusDiv = document.getElementById('status');"
|
||||
"const fileInfo = document.getElementById('file-info');"
|
||||
""
|
||||
"// File input change handler"
|
||||
"fileInput.addEventListener('change', function(e) {"
|
||||
" console.log('File input changed', e.target.files);"
|
||||
" if (e.target.files.length > 0) {"
|
||||
" handleFile(e.target.files[0]);"
|
||||
" }"
|
||||
"});"
|
||||
""
|
||||
"// Drag and drop handlers"
|
||||
"uploadArea.addEventListener('dragover', function(e) {"
|
||||
" e.preventDefault();"
|
||||
" uploadArea.classList.add('dragover');"
|
||||
"});"
|
||||
""
|
||||
"uploadArea.addEventListener('dragleave', function() {"
|
||||
" uploadArea.classList.remove('dragover');"
|
||||
"});"
|
||||
""
|
||||
"uploadArea.addEventListener('drop', function(e) {"
|
||||
" e.preventDefault();"
|
||||
" uploadArea.classList.remove('dragover');"
|
||||
" const files = e.dataTransfer.files;"
|
||||
" console.log('Files dropped:', files);"
|
||||
" if (files.length > 0) {"
|
||||
" handleFile(files[0]);"
|
||||
" }"
|
||||
"});"
|
||||
""
|
||||
"function handleFile(file) {"
|
||||
" console.log('Handling file:', file.name, file.size);"
|
||||
" if (file.name.toLowerCase().endsWith('.bin')) {"
|
||||
" selectedFile = file;"
|
||||
" fileInfo.textContent = 'Selected: ' + file.name + ' (' + (file.size/1024).toFixed(2) + ' KB)';"
|
||||
" uploadBtn.disabled = false;"
|
||||
" uploadBtn.textContent = 'Upload ' + file.name;"
|
||||
" } else {"
|
||||
" alert('Please select a .bin file');"
|
||||
" selectedFile = null;"
|
||||
" fileInfo.textContent = '';"
|
||||
" uploadBtn.disabled = true;"
|
||||
" uploadBtn.textContent = 'Upload Firmware';"
|
||||
" }"
|
||||
"}"
|
||||
""
|
||||
"function showStatus(message, type) {"
|
||||
" statusDiv.textContent = message;"
|
||||
" statusDiv.className = 'status ' + type;"
|
||||
" statusDiv.style.display = 'block';"
|
||||
"}"
|
||||
""
|
||||
"function uploadFirmware() {"
|
||||
" if (!selectedFile) {"
|
||||
" console.error('No file selected');"
|
||||
" return;"
|
||||
" }"
|
||||
" "
|
||||
" console.log('Starting upload...');"
|
||||
" const xhr = new XMLHttpRequest();"
|
||||
" uploadBtn.disabled = true;"
|
||||
" progressDiv.style.display = 'block';"
|
||||
" statusDiv.style.display = 'none';"
|
||||
" "
|
||||
" xhr.upload.addEventListener('progress', function(e) {"
|
||||
" if (e.lengthComputable) {"
|
||||
" const percent = Math.round((e.loaded / e.total) * 100);"
|
||||
" progressBar.style.width = percent + '%%';"
|
||||
" progressBar.textContent = percent + '%%';"
|
||||
" console.log('Upload progress:', percent);"
|
||||
" }"
|
||||
" });"
|
||||
" "
|
||||
" xhr.addEventListener('load', function() {"
|
||||
" console.log('Upload complete, status:', xhr.status);"
|
||||
" if (xhr.status === 200) {"
|
||||
" showStatus('Firmware uploaded successfully! Device will restart...', 'success');"
|
||||
" setTimeout(function() { location.reload(); }, 5000);"
|
||||
" } else {"
|
||||
" showStatus('Upload failed: ' + xhr.responseText, 'error');"
|
||||
" uploadBtn.disabled = false;"
|
||||
" }"
|
||||
" });"
|
||||
" "
|
||||
" xhr.addEventListener('error', function() {"
|
||||
" console.error('Upload error');"
|
||||
" showStatus('Upload error occurred', 'error');"
|
||||
" uploadBtn.disabled = false;"
|
||||
" });"
|
||||
" "
|
||||
" xhr.open('POST', '/update');"
|
||||
" xhr.send(selectedFile);"
|
||||
"}"
|
||||
"</script>"
|
||||
"</body>"
|
||||
"</html>";
|
||||
|
||||
// Server handle
|
||||
static httpd_handle_t s_server = NULL;
|
||||
|
||||
@ -208,20 +57,179 @@ static esp_err_t index_handler(httpd_req_t *req)
|
||||
}
|
||||
esp_partition_iterator_release(it);
|
||||
|
||||
// Allocate buffer for response
|
||||
size_t response_size = strlen(ota_html) + 64;
|
||||
char *response = malloc(response_size);
|
||||
if (!response) {
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Memory allocation failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
snprintf(response, response_size, ota_html, s_version, free_space / 1024);
|
||||
|
||||
// Send response in chunks
|
||||
httpd_resp_set_type(req, "text/html");
|
||||
httpd_resp_send(req, response, strlen(response));
|
||||
|
||||
free(response);
|
||||
// Part 1: HTML head and styles
|
||||
const char *html_part1 =
|
||||
"<!DOCTYPE html>"
|
||||
"<html>"
|
||||
"<head>"
|
||||
"<title>ESP32 OTA Update</title>"
|
||||
"<meta name='viewport' content='width=device-width, initial-scale=1'>"
|
||||
"<style>"
|
||||
"body { font-family: Arial, sans-serif; margin: 40px; background-color: #f0f0f0; }"
|
||||
".container { max-width: 600px; margin: 0 auto; background-color: white; padding: 20px; border-radius: 10px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }"
|
||||
"h1 { color: #333; text-align: center; }"
|
||||
".info { background-color: #e7f3ff; border-left: 4px solid #2196F3; padding: 10px; margin-bottom: 20px; }"
|
||||
".upload-area { border: 2px dashed #ccc; border-radius: 5px; padding: 30px; text-align: center; margin: 20px 0; }"
|
||||
".upload-area.dragover { background-color: #e7f3ff; border-color: #2196F3; }"
|
||||
"input[type='file'] { display: none; }"
|
||||
".btn { background-color: #4CAF50; color: white; padding: 12px 24px; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; }"
|
||||
".btn:hover { background-color: #45a049; }"
|
||||
".btn:disabled { background-color: #cccccc; cursor: not-allowed; }"
|
||||
".progress { width: 100%; background-color: #f0f0f0; border-radius: 4px; margin-top: 20px; display: none; }"
|
||||
".progress-bar { width: 0%; height: 30px; background-color: #4CAF50; border-radius: 4px; text-align: center; line-height: 30px; color: white; transition: width 0.3s; }"
|
||||
".status { margin-top: 20px; padding: 10px; border-radius: 4px; display: none; }"
|
||||
".status.success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }"
|
||||
".status.error { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }"
|
||||
".file-info { margin-top: 10px; font-style: italic; color: #666; }"
|
||||
"</style>"
|
||||
"</head>"
|
||||
"<body>"
|
||||
"<div class='container'>"
|
||||
"<h1>ESP32 OTA Update</h1>";
|
||||
|
||||
httpd_resp_send_chunk(req, html_part1, strlen(html_part1));
|
||||
|
||||
// Part 2: Info section with dynamic content
|
||||
char info_buf[256];
|
||||
snprintf(info_buf, sizeof(info_buf),
|
||||
"<div class='info'>"
|
||||
"<strong>Current Version:</strong> <span id='version'>%s</span><br>"
|
||||
"<strong>Free Space:</strong> <span id='free-space'>%lu KB</span>"
|
||||
"</div>", s_version, (unsigned long)(free_space / 1024));
|
||||
|
||||
httpd_resp_send_chunk(req, info_buf, strlen(info_buf));
|
||||
|
||||
// Part 3: Upload area
|
||||
const char *html_part3 =
|
||||
"<div class='upload-area' id='upload-area'>"
|
||||
"<p>Drag and drop firmware file here or click to select</p>"
|
||||
"<input type='file' id='file' accept='.bin'>"
|
||||
"<button class='btn' onclick='document.getElementById(\"file\").click()'>Select File</button>"
|
||||
"<div class='file-info' id='file-info'></div>"
|
||||
"</div>"
|
||||
"<button class='btn' id='upload-btn' onclick='uploadFirmware()' disabled>Upload Firmware</button>"
|
||||
"<div class='progress' id='progress'>"
|
||||
"<div class='progress-bar' id='progress-bar'>0%</div>"
|
||||
"</div>"
|
||||
"<div class='status' id='status'></div>"
|
||||
"</div>";
|
||||
|
||||
httpd_resp_send_chunk(req, html_part3, strlen(html_part3));
|
||||
|
||||
// Part 4: JavaScript
|
||||
const char *html_part4 =
|
||||
"<script>"
|
||||
"console.log('OTA page loaded');"
|
||||
"let selectedFile = null;"
|
||||
"const uploadArea = document.getElementById('upload-area');"
|
||||
"const fileInput = document.getElementById('file');"
|
||||
"const uploadBtn = document.getElementById('upload-btn');"
|
||||
"const progressDiv = document.getElementById('progress');"
|
||||
"const progressBar = document.getElementById('progress-bar');"
|
||||
"const statusDiv = document.getElementById('status');"
|
||||
"const fileInfo = document.getElementById('file-info');"
|
||||
""
|
||||
"fileInput.addEventListener('change', function(e) {"
|
||||
" console.log('File input changed', e.target.files);"
|
||||
" if (e.target.files.length > 0) {"
|
||||
" handleFile(e.target.files[0]);"
|
||||
" }"
|
||||
"});"
|
||||
""
|
||||
"uploadArea.addEventListener('dragover', function(e) {"
|
||||
" e.preventDefault();"
|
||||
" uploadArea.classList.add('dragover');"
|
||||
"});"
|
||||
""
|
||||
"uploadArea.addEventListener('dragleave', function() {"
|
||||
" uploadArea.classList.remove('dragover');"
|
||||
"});"
|
||||
""
|
||||
"uploadArea.addEventListener('drop', function(e) {"
|
||||
" e.preventDefault();"
|
||||
" uploadArea.classList.remove('dragover');"
|
||||
" const files = e.dataTransfer.files;"
|
||||
" console.log('Files dropped:', files);"
|
||||
" if (files.length > 0) {"
|
||||
" handleFile(files[0]);"
|
||||
" }"
|
||||
"});"
|
||||
""
|
||||
"function handleFile(file) {"
|
||||
" console.log('Handling file:', file.name, file.size);"
|
||||
" if (file.name.toLowerCase().endsWith('.bin')) {"
|
||||
" selectedFile = file;"
|
||||
" fileInfo.textContent = 'Selected: ' + file.name + ' (' + (file.size/1024).toFixed(2) + ' KB)';"
|
||||
" uploadBtn.disabled = false;"
|
||||
" uploadBtn.textContent = 'Upload ' + file.name;"
|
||||
" } else {"
|
||||
" alert('Please select a .bin file');"
|
||||
" selectedFile = null;"
|
||||
" fileInfo.textContent = '';"
|
||||
" uploadBtn.disabled = true;"
|
||||
" uploadBtn.textContent = 'Upload Firmware';"
|
||||
" }"
|
||||
"}"
|
||||
""
|
||||
"function showStatus(message, type) {"
|
||||
" statusDiv.textContent = message;"
|
||||
" statusDiv.className = 'status ' + type;"
|
||||
" statusDiv.style.display = 'block';"
|
||||
"}"
|
||||
""
|
||||
"function uploadFirmware() {"
|
||||
" if (!selectedFile) {"
|
||||
" console.error('No file selected');"
|
||||
" return;"
|
||||
" }"
|
||||
" "
|
||||
" console.log('Starting upload...');"
|
||||
" const xhr = new XMLHttpRequest();"
|
||||
" uploadBtn.disabled = true;"
|
||||
" progressDiv.style.display = 'block';"
|
||||
" statusDiv.style.display = 'none';"
|
||||
" "
|
||||
" xhr.upload.addEventListener('progress', function(e) {"
|
||||
" if (e.lengthComputable) {"
|
||||
" const percent = Math.round((e.loaded / e.total) * 100);"
|
||||
" progressBar.style.width = percent + '%';"
|
||||
" progressBar.textContent = percent + '%';"
|
||||
" console.log('Upload progress:', percent);"
|
||||
" }"
|
||||
" });"
|
||||
" "
|
||||
" xhr.addEventListener('load', function() {"
|
||||
" console.log('Upload complete, status:', xhr.status);"
|
||||
" if (xhr.status === 200) {"
|
||||
" showStatus('Firmware uploaded successfully! Device will restart...', 'success');"
|
||||
" setTimeout(function() { location.reload(); }, 5000);"
|
||||
" } else {"
|
||||
" showStatus('Upload failed: ' + xhr.responseText, 'error');"
|
||||
" uploadBtn.disabled = false;"
|
||||
" }"
|
||||
" });"
|
||||
" "
|
||||
" xhr.addEventListener('error', function() {"
|
||||
" console.error('Upload error');"
|
||||
" showStatus('Upload error occurred', 'error');"
|
||||
" uploadBtn.disabled = false;"
|
||||
" });"
|
||||
" "
|
||||
" xhr.open('POST', '/update');"
|
||||
" xhr.send(selectedFile);"
|
||||
"}"
|
||||
"</script>"
|
||||
"</body>"
|
||||
"</html>";
|
||||
|
||||
httpd_resp_send_chunk(req, html_part4, strlen(html_part4));
|
||||
|
||||
// Send final chunk to complete response
|
||||
httpd_resp_send_chunk(req, NULL, 0);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user