forked from espressif/esp-launchpad
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature: Add minimal launchpad. (#30)
Co-authored-by: Rushikesh Patange <rushikesh.patange@espressif.com>
- Loading branch information
1 parent
5c52a06
commit 5fbecda
Showing
4 changed files
with
500 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
<!DOCTYPE html> | ||
<html lang="en" class="h-100"> | ||
|
||
<head> | ||
<meta charset="utf-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> | ||
<meta name="description" content="" /> | ||
<meta name="author" content="" /> | ||
<title>ESP Launchpad</title> | ||
<!-- Core theme CSS (includes Bootstrap)--> | ||
<link href="../css/styles.css" rel="stylesheet" /> | ||
<link href="../css/custom.css" rel="stylesheet" /> | ||
<link href="../css/xterm.css" rel="stylesheet" /> | ||
<link rel="stylesheet" href="./minimal_ui_styles.css" /> | ||
</head> | ||
|
||
<body class="d-flex flex-column h-100" style="align-items: center"> | ||
<!-- Modal for reset device starts--> | ||
|
||
<div id="main" class="maincontainer"> | ||
<!-- Responsive navbar--> | ||
<nav class="navbar navbar-expand-lg"> | ||
<div class="container"> | ||
<h4 class="topic" data-target-tab-panel-id="about"> | ||
<img src="../assets/logo-v1.png" class="logo" /> | ||
</h4> | ||
</div> | ||
</nav> | ||
<div class="container" id="alert-container" style="display: none;"> | ||
<div class="text-center mt-3 intro-text"> | ||
<div class="alert alert-warning alert-dismissible fade show" role="alert"> | ||
<label style="display:none" id="lblConnTo"></label> | ||
</div> | ||
</div> | ||
</div> | ||
<div style="margin-top: auto"> | ||
<button class="connectButton" id="connectButton" tabindex="0" type="button">Connect Your Device<span | ||
class="span-svg"><svg class="svg" focusable="false" aria-hidden="true" viewBox="0 0 24 24" | ||
data-testid="EastIcon"> | ||
<path fill="#FFFFFF" d="m15 5-1.41 1.41L18.17 11H2v2h16.17l-4.59 4.59L15 19l7-7-7-7z"></path> | ||
</svg></span></button> | ||
</div> | ||
<div id="spinner" style="display: none"> | ||
<div> | ||
<div class="spinner-grow text-secondary bg-opacity-25" role="status"> | ||
<span class="sr-only"></span> | ||
</div> | ||
<div class="spinner-grow text-secondary bg-opacity-50" role="status"> | ||
<span class="sr-only"></span> | ||
</div> | ||
<div class="spinner-grow text-secondary bg-opacity-75" role="status"> | ||
<span class="sr-only"></span> | ||
</div> | ||
</div> | ||
</div> | ||
<div class="container"> | ||
<div class="row split"> | ||
<div class="productinfocontainer" id="productInfoContainer"> | ||
<div id="message" style="display: none"></div> | ||
</div> | ||
<div class="col-12 terminalcontainer" style="overflow: hidden; display: none;" id="terminalContainer"> | ||
<div class="button-container text-end mb-sm-3"> | ||
<div class="field-container" data-toggle="tooltip" data-placement="right" | ||
title="Restart your device" style="display: inline-block"> | ||
<button type="button" class="app-button btn btn-outline-dark" id="consoleStartButton" | ||
disabled> | ||
Restart Device | ||
</button> | ||
</div> | ||
</div> | ||
<div id="terminal"><span class="devicelog">Device logs</span></div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<!-- Safari & FireFox not supported error message--> | ||
<div id="unsupportedBrowserErr" style="display: none"> | ||
<p align="center" style="color: red"> | ||
This tool is not supported on Safari & Firefox browsers yet! Please try | ||
using another browser like Google Chrome. | ||
<br /> | ||
<br /> | ||
ESP Launchpad makes use of WebUSB to communicate with the device. | ||
Currently Safari & Firefox browsers don't support it yet. We will add | ||
support for this browser as soon as Safari & Firefox starts supporting | ||
WebUSB. | ||
</p> | ||
</div> | ||
|
||
|
||
<!-- Third party libraries--> | ||
<script src="../node_modules/xterm/lib/xterm.js"></script> | ||
<script src="../node_modules/xterm-addon-fit/lib/xterm-addon-fit.js"></script> | ||
<script src="../node_modules/crypto-js/crypto-js.js"></script> | ||
<script src="https://cdnjs.cloudflare.com/ajax/libs/pako/2.0.3/pako.js"></script> | ||
<script type="text/javascript" src="../node_modules/toml-js/src/toml.js"></script> | ||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script> | ||
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/2.1.0/showdown.min.js"></script> | ||
|
||
<script src="./minimal_ui_index.js" type="module"></script> | ||
<!-- Bootstrap core JS--> | ||
<script src="../js/jquery.min.js"></script> | ||
<script src="../js/bootstrap.bundle.min.js"></script> | ||
<script src="../js/qrcode.min.js"></script> | ||
<script> | ||
// Safari 3.0+ "[object HTMLElementConstructor]" | ||
var isSafari = | ||
/constructor/i.test(window.HTMLElement) || | ||
(function (p) { | ||
return p.toString() === "[object SafariRemoteNotification]"; | ||
})( | ||
!window["safari"] || | ||
(typeof safari !== "undefined" && window["safari"].pushNotification) | ||
); | ||
|
||
var isFirefox = typeof InstallTrigger !== "undefined"; | ||
|
||
if (isSafari || isFirefox) { | ||
document.getElementById("unsupportedBrowserErr").style.display = | ||
"inline"; | ||
document.getElementById("main").style.display = "none"; | ||
} | ||
</script> | ||
</body> | ||
|
||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,233 @@ | ||
const connectButton = document.getElementById("connectButton"); | ||
const consoleStartButton = document.getElementById("consoleStartButton"); | ||
const terminal = document.getElementById("terminal"); | ||
const spinner = document.getElementById("spinner"); | ||
const productInfoContainer = document.getElementById("productInfoContainer"); | ||
const terminalContainer = document.getElementById("terminalContainer"); | ||
const alertContainer = document.getElementById("alert-container"); | ||
const lblConnTo = document.getElementById("lblConnTo"); | ||
const message = document.getElementById("message"); | ||
|
||
import * as utilities from "../js/utils.js" | ||
import * as esptooljs from "../node_modules/esptool-js/bundle.js"; | ||
const ESPLoader = esptooljs.ESPLoader; | ||
const Transport = esptooljs.Transport; | ||
|
||
let term = new Terminal({ cols: utilities.getTerminalColumns(), rows: 23, fontSize: 14, scrollback: 9999999 }); | ||
let fitAddon = new FitAddon.FitAddon(); | ||
term.loadAddon(fitAddon); | ||
term.open(terminal); | ||
fitAddon.fit(); | ||
|
||
let device = null; | ||
let transport; | ||
let chip = "default"; | ||
let chipDesc = "default"; | ||
let esploader; | ||
let connected = false; | ||
let resizeTimeout = false; | ||
let imagePartsArray = undefined; | ||
let imagePartsOffsetArray = undefined; | ||
let reader = undefined; | ||
var config = []; | ||
|
||
// Code for minimalLaunchpad | ||
function setImagePartsAndOffsetArray() { | ||
let application = "supported_apps"; | ||
let chipInConfToml = undefined; | ||
let imageString = undefined; | ||
let addressString = undefined; | ||
if (chip !== "default" && config["multipart"]) { | ||
chipInConfToml = config["chip"]; | ||
} | ||
if (chip !== "default" && chipInConfToml !== undefined) { | ||
imageString = "image." + chipInConfToml.toLowerCase() + ".parts"; | ||
addressString = "image." + chipInConfToml.toLowerCase() + ".addresses"; | ||
} | ||
imagePartsArray = config[config[application][0]][imageString]; | ||
imagePartsOffsetArray = config[config[application][0]][addressString]; | ||
} | ||
|
||
async function downloadAndFlash() { | ||
let fileArr = [] | ||
for (let index = 0; index < imagePartsArray.length; index++) { | ||
let data = await utilities.getImageData(imagePartsArray[index]); | ||
fileArr.push({ data: data, address: imagePartsOffsetArray[index] }); | ||
} | ||
try { | ||
const flashOptions = { | ||
fileArray: fileArr, | ||
flashSize: "keep", | ||
flashMode: undefined, | ||
flashFreq: undefined, | ||
eraseAll: false, | ||
compress: true, | ||
}; | ||
await esploader.write_flash(flashOptions); | ||
} catch (error) { | ||
} | ||
} | ||
|
||
function MDtoHtml() { | ||
let application = "supported_apps"; | ||
var converter = new showdown.Converter({ tables: true }); | ||
converter.setFlavor('github'); | ||
try { | ||
fetch(config[config[application][0]]["readme.text"]).then(response => { | ||
return response.text(); | ||
}).then(result => { | ||
let htmlText = converter.makeHtml(result); | ||
message.innerHTML = htmlText; | ||
consoleStartButton.click() | ||
message.style.display = "block"; | ||
productInfoContainer.classList.add("col-6", "slide-up"); | ||
terminalContainer.classList.remove("col-12", "fade-in"); | ||
terminalContainer.classList.add("col-6", "slide-right"); | ||
utilities.resizeTerminal(fitAddon); | ||
|
||
}) | ||
} catch (error) { | ||
message.style.display = "none"; | ||
} | ||
} | ||
// Build the Minimal Launchpad UI using the config toml file. | ||
async function buildMinimalLaunchpadUI() { | ||
let tomlFileURL = undefined; | ||
const urlParams = new URLSearchParams(window.location.search); | ||
const url = window.location.search; | ||
const parameter = "flashConfigURL"; | ||
if (url.includes("&")) { | ||
tomlFileURL = url.substring(url.search(parameter) + parameter.length + 1); | ||
} else { | ||
tomlFileURL = urlParams.get(parameter); | ||
} | ||
var xhr = new XMLHttpRequest(); | ||
xhr.open('GET', tomlFileURL, true); | ||
xhr.send(); | ||
xhr.onload = function () { | ||
if (xhr.readyState === 4 && xhr.status === 200) { | ||
config = toml.parse(xhr.responseText); | ||
return config; | ||
} | ||
} | ||
} | ||
|
||
config = await buildMinimalLaunchpadUI(); | ||
|
||
$(function () { | ||
utilities.initializeTooltips(); | ||
}) | ||
|
||
let espLoaderTerminal = { | ||
clean() { | ||
term.clear(); | ||
}, | ||
writeLine(data) { | ||
term.writeln(data); | ||
}, | ||
write(data) { | ||
term.write(data) | ||
} | ||
} | ||
|
||
async function connectToDevice() { | ||
connectButton.style.display = "none"; | ||
if (device === null) { | ||
device = await navigator.serial.requestPort({ | ||
filters: utilities.usbPortFilters | ||
}); | ||
transport = new Transport(device); | ||
} | ||
spinner.style.display = "flex"; | ||
spinner.style.flexDirection = "column"; | ||
spinner.style.alignItems = "center"; | ||
|
||
try { | ||
const loaderOptions = { | ||
transport: transport, | ||
baudrate: 921600, | ||
terminal: espLoaderTerminal | ||
}; | ||
esploader = new ESPLoader(loaderOptions); | ||
connected = true; | ||
|
||
chipDesc = await esploader.main_fn(); | ||
chip = esploader.chip.CHIP_NAME; | ||
|
||
await esploader.flash_id(); | ||
} catch (e) { | ||
} | ||
spinner.style.display = "none"; | ||
} | ||
|
||
connectButton.onclick = async () => { | ||
try { | ||
if (!connected) | ||
await connectToDevice(); | ||
if (chipDesc !== "default") { | ||
terminalContainer.classList.add("fade-in"); | ||
terminalContainer.style.display = 'initial' | ||
setImagePartsAndOffsetArray(); | ||
await downloadAndFlash(); | ||
consoleStartButton.disabled = false; | ||
MDtoHtml(); | ||
setTimeout(() => { | ||
productInfoContainer.classList.add("bounce"); | ||
}, 2500) | ||
} else { | ||
alertContainer.style.display = "initial"; | ||
lblConnTo.innerHTML = "<b><span style='color:red'>Unable to detect device. Please ensure the device is not connected in another application</span></b>"; | ||
lblConnTo.style.display = "block"; | ||
setTimeout(() => { | ||
alertContainer.style.display = "none"; | ||
connectButton.style.display = "initial"; | ||
connected = false; | ||
}, 5000) | ||
} | ||
} catch (error) { | ||
if (error.message === "Failed to execute 'requestPort' on 'Serial': No port selected by the user.") { | ||
connectButton.style.display = "initial"; | ||
} | ||
} | ||
|
||
} | ||
|
||
consoleStartButton.onclick = async () => { | ||
if (transport) { | ||
if (reader !== undefined) { | ||
reader.releaseLock(); | ||
} | ||
if (device) { | ||
await device.close(); | ||
} | ||
} | ||
await transport.connect(); | ||
await transport.setDTR(false); | ||
await new Promise(resolve => setTimeout(resolve, 100)); | ||
await transport.setDTR(true); | ||
while (device.readable) { | ||
|
||
if (!device.readable.locked) { | ||
reader = device.readable.getReader(); | ||
} | ||
|
||
try { | ||
while (true) { | ||
const { value, done } = await reader.read(); | ||
if (done) { | ||
// Allow the serial port to be closed later. | ||
reader.releaseLock(); | ||
break; | ||
} | ||
if (value) { | ||
term.write(value); | ||
} | ||
} | ||
} catch (error) { } | ||
} | ||
} | ||
|
||
$(window).resize(function () { | ||
clearTimeout(resizeTimeout); | ||
resizeTimeout = setTimeout(() => utilities.resizeTerminal(fitAddon), 300); | ||
}); |
Oops, something went wrong.