initial commit. basic structure. untested.

This commit is contained in:
2023-11-15 20:29:18 +00:00
commit d6e9a110b4
23 changed files with 2222 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

10
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

13
data/css/materialize.min.css vendored Normal file

File diff suppressed because one or more lines are too long

9
data/css/style.css Normal file
View File

@@ -0,0 +1,9 @@
#header {
padding-top:20px;
}
.logo img {
width:200px;
}
input[type=number] {
width:200px;
}

BIN
data/eMAKER.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

629
data/edit.htm Normal file
View File

@@ -0,0 +1,629 @@
<!--This is the plain html source of the hex encoded Editor-Page embedded in SPIFFSEditor.cpp -->
<!DOCTYPE html>
<html lang="en">
<head>
<title>ESP Editor...</title>
<style type="text/css" media="screen">
.cm {
z-index: 300;
position: absolute;
left: 5px;
border: 1px solid #444;
background-color: #F5F5F5;
display: none;
box-shadow: 0 0 10px rgba( 0, 0, 0, .4 );
font-size: 12px;
font-family: sans-serif;
font-weight:bold;
}
.cm ul {
list-style: none;
top: 0;
left: 0;
margin: 0;
padding: 0;
}
.cm li {
position: relative;
min-width: 60px;
cursor: pointer;
}
.cm span {
color: #444;
display: inline-block;
padding: 6px;
}
.cm li:hover { background: #444; }
.cm li:hover span { color: #EEE; }
.tvu ul, .tvu li {
padding: 0;
margin: 0;
list-style: none;
}
.tvu input {
position: absolute;
opacity: 0;
}
.tvu {
font: normal 12px Verdana, Arial, Sans-serif;
-moz-user-select: none;
-webkit-user-select: none;
user-select: none;
color: #444;
line-height: 16px;
}
.tvu span {
margin-bottom:5px;
padding: 0 0 0 18px;
cursor: pointer;
display: inline-block;
height: 16px;
vertical-align: middle;
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAADoSURBVBgZBcExblNBGAbA2ceegTRBuIKOgiihSZNTcC5LUHAihNJR0kGKCDcYJY6D3/77MdOinTvzAgCw8ysThIvn/VojIyMjIyPP+bS1sUQIV2s95pBDDvmbP/mdkft83tpYguZq5Jh/OeaYh+yzy8hTHvNlaxNNczm+la9OTlar1UdA/+C2A4trRCnD3jS8BB1obq2Gk6GU6QbQAS4BUaYSQAf4bhhKKTFdAzrAOwAxEUAH+KEM01SY3gM6wBsEAQB0gJ+maZoC3gI6iPYaAIBJsiRmHU0AALOeFC3aK2cWAACUXe7+AwO0lc9eTHYTAAAAAElFTkSuQmCC') no-repeat;
background-position: 0px 0px;
}
.tvu span:hover {
text-decoration: underline;
}
@media screen and (-webkit-min-device-pixel-ratio:0){
.tvu{
-webkit-animation: webkit-adjacent-element-selector-bugfix infinite 1s;
}
@-webkit-keyframes webkit-adjacent-element-selector-bugfix {
from {
padding: 0;
}
to {
padding: 0;
}
}
}
#uploader {
position: absolute;
top: 0;
right: 0;
left: 0;
height:28px;
line-height: 24px;
padding-left: 10px;
background-color: #444;
color:#EEE;
}
#tree {
position: absolute;
top: 28px;
bottom: 0;
left: 0;
width:160px;
padding: 8px;
}
#editor, #preview {
position: absolute;
top: 28px;
right: 0;
bottom: 0;
left: 160px;
border-left:1px solid #EEE;
}
#preview {
background-color: #EEE;
padding:5px;
}
#loader {
position: absolute;
top: 36%;
right: 40%;
}
.loader {
z-index: 10000;
border: 8px solid #b5b5b5; /* Grey */
border-top: 8px solid #3498db; /* Blue */
border-bottom: 8px solid #3498db; /* Blue */
border-radius: 50%;
width: 240px;
height: 240px;
animation: spin 2s linear infinite;
display:none;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
<script>
if (typeof XMLHttpRequest === "undefined") {
XMLHttpRequest = function () {
try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e) {}
try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e) {}
try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {}
throw new Error("This browser does not support XMLHttpRequest.");
};
}
function ge(a){
return document.getElementById(a);
}
function ce(a){
return document.createElement(a);
}
function sortByKey(array, key) {
return array.sort(function(a, b) {
var x = a[key]; var y = b[key];
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
});
}
var QueuedRequester = function () {
this.queue = [];
this.running = false;
this.xmlhttp = null;
}
QueuedRequester.prototype = {
_request: function(req){
this.running = true;
if(!req instanceof Object) return;
var that = this;
function ajaxCb(x,d){ return function(){
if (x.readyState == 4){
ge("loader").style.display = "none";
d.callback(x.status, x.responseText);
if(that.queue.length === 0) that.running = false;
if(that.running) that._request(that.queue.shift());
}
}}
ge("loader").style.display = "block";
var p = "";
if(req.params instanceof FormData){
p = req.params;
} else if(req.params instanceof Object){
for (var key in req.params) {
if(p === "")
p += (req.method === "GET")?"?":"";
else
p += "&";
p += encodeURIComponent(key)+"="+encodeURIComponent(req.params[key]);
};
}
this.xmlhttp = new XMLHttpRequest();
this.xmlhttp.onreadystatechange = ajaxCb(this.xmlhttp, req);
if(req.method === "GET"){
this.xmlhttp.open(req.method, req.url+p, true);
this.xmlhttp.send();
} else {
this.xmlhttp.open(req.method, req.url, true);
if(p instanceof String)
this.xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
this.xmlhttp.send(p);
}
},
stop: function(){
if(this.running) this.running = false;
if(this.xmlhttp && this.xmlhttp.readyState < 4){
this.xmlhttp.abort();
}
},
add: function(method, url, params, callback){
this.queue.push({url:url,method:method,params:params,callback:callback});
if(!this.running){
this._request(this.queue.shift());
}
}
}
var requests = new QueuedRequester();
function createFileUploader(element, tree, editor){
var xmlHttp;
var refresh = ce("button");
refresh.innerHTML = 'Refresh List';
ge(element).appendChild(refresh);
var input = ce("input");
input.type = "file";
input.multiple = false;
input.name = "data";
input.id="upload-select";
ge(element).appendChild(input);
var path = ce("input");
path.id = "upload-path";
path.type = "text";
path.name = "path";
path.defaultValue = "/";
ge(element).appendChild(path);
var button = ce("button");
button.innerHTML = 'Upload';
ge(element).appendChild(button);
var mkfile = ce("button");
mkfile.innerHTML = 'Create';
ge(element).appendChild(mkfile);
var filename = ce("input");
filename.id = "editor-filename";
filename.type = "text";
filename.disabled= true;
filename.size = 20;
ge(element).appendChild(filename);
var savefile = ce("button");
savefile.innerHTML = ' Save ' ;
ge(element).appendChild(savefile);
function httpPostProcessRequest(status, responseText){
if(status != 200)
alert("ERROR["+status+"]: "+responseText);
else
tree.refreshPath(path.value);
}
function createPath(p){
var formData = new FormData();
formData.append("path", p);
requests.add("PUT", "/edit", formData, httpPostProcessRequest);
}
mkfile.onclick = function(e){
createPath(path.value);
editor.loadUrl(path.value);
path.value="/";
};
savefile.onclick = function(e){
editor.execCommand('saveCommand');
};
refresh.onclick = function(e){
tree.refreshPath(path.value);
};
button.onclick = function(e){
if(input.files.length === 0){
return;
}
var formData = new FormData();
formData.append("data", input.files[0], path.value);
requests.add("POST", "/edit", formData, httpPostProcessRequest);
var uploadPath= ge("upload-path");
uploadPath.value="/";
var uploadSelect= ge("upload-select");
uploadSelect.value="";
};
input.onchange = function(e){
if(input.files.length === 0) return;
var filename = input.files[0].name;
var ext = /(?:\.([^.]+))?$/.exec(filename)[1];
var name = /(.*)\.[^.]+$/.exec(filename)[1];
if(typeof name !== undefined){
filename = name;
}
path.value = "/"+filename+"."+ext;
};
}
function createTree(element, editor){
var preview = ge("preview");
var treeRoot = ce("div");
treeRoot.className = "tvu";
ge(element).appendChild(treeRoot);
function loadDownload(path){
ge('download-frame').src = "/edit?download="+path;
}
function loadPreview(path){
var edfname = ge("editor-filename");
edfname.value=path;
ge("editor").style.display = "none";
preview.style.display = "block";
preview.innerHTML = '<img src="/edit?edit='+path+'&_cb='+Date.now()+'" style="max-width:100%; max-height:100%; margin:auto; display:block;" />';
}
function fillFileMenu(el, path){
var list = ce("ul");
el.appendChild(list);
var action = ce("li");
list.appendChild(action);
if(isImageFile(path)){
action.innerHTML = "<span>Preview</span>";
action.onclick = function(e){
loadPreview(path);
if(document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(el);
};
} else if(isTextFile(path)){
action.innerHTML = "<span>Edit</span>";
action.onclick = function(e){
editor.loadUrl(path);
if(document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(el);
};
}
var download = ce("li");
list.appendChild(download);
download.innerHTML = "<span>Download</span>";
download.onclick = function(e){
loadDownload(path);
if(document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(el);
};
var delFile = ce("li");
list.appendChild(delFile);
delFile.innerHTML = "<span>Delete</span>";
delFile.onclick = function(e){
httpDelete(path);
if(document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(el);
};
}
function showContextMenu(event, path, isfile){
var divContext = ce("div");
var scrollTop = document.body.scrollTop ? document.body.scrollTop : document.documentElement.scrollTop;
var scrollLeft = document.body.scrollLeft ? document.body.scrollLeft : document.documentElement.scrollLeft;
var left = event.clientX + scrollLeft;
var top = event.clientY + scrollTop;
divContext.className = 'cm';
divContext.style.display = 'block';
divContext.style.left = left + 'px';
divContext.style.top = top + 'px';
fillFileMenu(divContext, path);
document.body.appendChild(divContext);
var width = divContext.offsetWidth;
var height = divContext.offsetHeight;
divContext.onmouseout = function(e){
if(e.clientX < left || e.clientX > (left + width) || e.clientY < top || e.clientY > (top + height)){
if(document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(divContext);
}
};
}
function createTreeLeaf(path, name, size){
var leaf = ce("li");
leaf.id = name;
var label = ce("span");
label.innerHTML = name;
leaf.appendChild(label);
leaf.onclick = function(e){
if(isTextFile(leaf.id.toLowerCase())){
editor.loadUrl(leaf.id);
} else if(isImageFile(leaf.id.toLowerCase())){
loadPreview(leaf.id);
}
};
leaf.oncontextmenu = function(e){
e.preventDefault();
e.stopPropagation();
showContextMenu(e, leaf.id, true);
};
return leaf;
}
function addList(parent, path, items){
sortByKey(items, 'name');
var list = ce("ul");
parent.appendChild(list);
var ll = items.length;
for(var i = 0; i < ll; i++){
if(items[i].type === "file")
list.appendChild(createTreeLeaf(path, items[i].name, items[i].size));
}
}
function isTextFile(path){
var ext = /(?:\.([^.]+))?$/.exec(path)[1];
if(typeof ext !== undefined){
switch(ext){
case "txt":
case "htm":
case "html":
case "js":
case "css":
case "xml":
case "json":
case "conf":
case "ini":
case "h":
case "c":
case "cpp":
case "php":
case "hex":
case "ino":
case "pde":
return true;
}
}
return false;
}
function isImageFile(path){
var ext = /(?:\.([^.]+))?$/.exec(path)[1];
if(typeof ext !== undefined){
switch(ext){
case "png":
case "jpg":
case "gif":
case "bmp":
return true;
}
}
return false;
}
this.refreshPath = function(path){
treeRoot.removeChild(treeRoot.childNodes[0]);
httpGet(treeRoot, "/");
};
function delCb(path){
return function(status, responseText){
if(status != 200){
alert("ERROR["+status+"]: "+responseText);
} else {
treeRoot.removeChild(treeRoot.childNodes[0]);
httpGet(treeRoot, "/");
}
}
}
function httpDelete(filename){
var formData = new FormData();
formData.append("path", filename);
requests.add("DELETE", "/edit", formData, delCb(filename));
}
function getCb(parent, path){
return function(status, responseText){
if(status == 200)
addList(parent, path, JSON.parse(responseText));
}
}
function httpGet(parent, path){
requests.add("GET", "/edit", { list: path }, getCb(parent, path));
}
httpGet(treeRoot, "/");
return this;
}
function createEditor(element, file, lang, theme, type){
function getLangFromFilename(filename){
var lang = "plain";
var ext = /(?:\.([^.]+))?$/.exec(filename)[1];
if(typeof ext !== undefined){
switch(ext){
case "txt": lang = "plain"; break;
case "hex": lang = "plain"; break;
case "conf": lang = "plain"; break;
case "htm": lang = "html"; break;
case "js": lang = "javascript"; break;
case "h": lang = "c_cpp"; break;
case "c": lang = "c_cpp"; break;
case "cpp": lang = "c_cpp"; break;
case "css":
case "scss":
case "php":
case "html":
case "json":
case "xml":
case "ini": lang = ext;
}
}
return lang;
}
if(typeof file === "undefined") file = "/index.html";
if(typeof lang === "undefined"){
lang = getLangFromFilename(file);
}
if(typeof theme === "undefined") theme = "textmate";
if(typeof type === "undefined"){
type = "text/"+lang;
if(lang === "c_cpp") type = "text/plain";
}
var editor = ace.edit(element);
function httpPostProcessRequest(status, responseText){
if(status != 200) alert("ERROR["+status+"]: "+responseText);
}
function httpPost(filename, data, type){
var formData = new FormData();
formData.append("data", new Blob([data], { type: type }), filename);
requests.add("POST", "/edit", formData, httpPostProcessRequest);
}
function httpGetProcessRequest(status, responseText){
ge("preview").style.display = "none";
ge("editor").style.display = "block";
if(status == 200)
editor.setValue(responseText);
else
editor.setValue("");
editor.clearSelection();
}
function httpGet(theUrl){
requests.add("GET", "/edit", { edit: theUrl }, httpGetProcessRequest);
}
if(lang !== "plain") editor.getSession().setMode("ace/mode/"+lang);
editor.setTheme("ace/theme/"+theme);
editor.$blockScrolling = Infinity;
editor.getSession().setUseSoftTabs(true);
editor.getSession().setTabSize(2);
editor.setHighlightActiveLine(true);
editor.setShowPrintMargin(false);
editor.commands.addCommand({
name: 'saveCommand',
bindKey: {win: 'Ctrl-S', mac: 'Command-S'},
exec: function(editor) {
//httpPost("/" + file, editor.getValue()+"", type);
httpPost(file, editor.getValue()+"", type);
},
readOnly: false
});
editor.commands.addCommand({
name: 'undoCommand',
bindKey: {win: 'Ctrl-Z', mac: 'Command-Z'},
exec: function(editor) {
editor.getSession().getUndoManager().undo(false);
},
readOnly: false
});
editor.commands.addCommand({
name: 'redoCommand',
bindKey: {win: 'Ctrl-Shift-Z', mac: 'Command-Shift-Z'},
exec: function(editor) {
editor.getSession().getUndoManager().redo(false);
},
readOnly: false
});
editor.loadUrl = function(filename){
var edfname = ge("editor-filename");
edfname.value=filename;
file = filename;
lang = getLangFromFilename(file);
type = "text/"+lang;
if(lang !== "plain") editor.getSession().setMode("ace/mode/"+lang);
// httpGet("/"+file);
httpGet(file);
};
return editor;
}
function onBodyLoad(){
var vars = {};
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) { vars[key] = value; });
var editor = createEditor("editor", vars.file, vars.lang, vars.theme);
var tree = createTree("tree", editor);
createFileUploader("uploader", tree, editor);
if(typeof vars.file === "undefined") vars.file = "/index.htm";
editor.loadUrl(vars.file);
};
</script>
<script id='ace' src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.6/ace.js" type="text/javascript" charset="utf-8"></script>
<script>
if (typeof ace.edit == "undefined") {
var script = document.createElement('script');
script.src = "/ace.js";
script.async = false;
document.head.appendChild(script);
}
</script>
</head>
<body onload="onBodyLoad();">
<div id="loader" class="loader"></div>
<div id="uploader"></div>
<div id="tree"></div>
<div id="editor"></div>
<div id="preview" style="display:none;"></div>
<iframe id=download-frame style='display:none;'></iframe>
</body>
</html>

BIN
data/emaker.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

1
data/gateway.txt Normal file
View File

@@ -0,0 +1 @@
192.168.0.254

165
data/index.htm Normal file
View File

@@ -0,0 +1,165 @@
<!DOCTYPE html>
<html>
<head>
<title>eMAKER Printer Climate Control</title>
<!--Import Google Icon Font-->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
<!--Import materialize.css-->
<link type="text/css" rel="stylesheet" href="css/materialize.min.css" media="screen,projection" />
<!--Let browser know website is optimized for mobile-->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="css/style.css" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
</head>
<body>
<div class="container">
<!-- Header row-->
<div id="header" class="row">
<div class="col 6 logo">
<img src="eMAKER.png" alt="eMAKER logo" />
</div>
<div class="col 6">
<h5>Printer Climate Control</h5>
</div>
</div>
<!-- Data row-->
<div class="row">
<div class="card light-blue darken-3">
<div class="card-tabs">
<ul class="tabs tabs-transparent">
<li class="tab col s12 m6 l3"><a href="#tabValues">Values</a>
</li>
<li class="tab col s12 m6 l3"><a href="#tabSettings">Settings</a>
</li>
</ul>
</div>
<div class="card-content white">
<div id="tabValues" class="active">
<div class="row">
<div class="col s12 m6 l3">
<div class="card light-blue darken-3 z-depth-3">
<div class="card-content white-text">
<span class="card-title">Temperature</span>
<div class="input-field">
<input disabled id="tempinfo" type="number" value="-1" />
</div>
</div>
</div>
</div>
<div class="col s12 m6 l3">
<div class="card light-blue darken-3 z-depth-3">
<div class="card-content white-text">
<span class="card-title">Humidity</span>
<div class="input-field">
<input disabled id="humidityinfo" type="number" value="-1" />
</div>
</div>
</div>
</div>
<div class="col s12 m6 l3">
<div class="card light-blue darken-3 z-depth-3">
<div class="card-content white-text">
<span class="card-title">Pressure</span>
<div class="input-field">
<input disabled id="pressureinfo" type="number" value="-1" />
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col s12 m6 l3">
<div class="card light-blue darken-3 z-depth-3">
<div class="card-content white-text">
<span class="card-title">Layers</span>
<div class="input-field">
<input disabled id="layersinfo" type="number" value="-1" />
<label for="layersinfo">Layer count</label>
</div>
</div>
<div class="card-action">
<a id="resetLayers" href="#">Reset</a>
</div>
</div>
</div>
</div>
</div>
<div id="tabSettings">
<div class="row">
<div class="col s12 m6 l3"><!-- Process settings -->
<div class="card light-blue darken-3 z-depth-3">
<div class="card-content white-text">
<span class="card-title">Settings</span>
<div class="input-field">
<input id="Iim" type="number" value="-1" />
<label for="Iim">Lower current</label>
</div>
<div class="input-field">
<input id="Imp" type="number" value="-1" />
<label for="Imp">Upper Current</label>
</div>
<div class="input-field">
<input id="Tu" type="number" value="-1" />
<label for="Tu">Upper Temperature</label>
</div>
<div class="input-field">
<input id="Tl" type="number" value="-1" />
<label for="Tl">Lower Temperature</label>
</div>
</div>
<div class="card-action">
<a class="waves-effect waves-light btn" id="btnSetParams" onclick="setParams()">Submit</a>
</div>
</div>
</div>
<div class="col s12 m6 l3"><!-- Connect to network-->
<div class="card light-blue darken-3 z-depth-3">
<div class="card-content white-text">
<span class="card-title">Network</span>
<div class="input-field">
<input id="ssid" type="text" value="" />
<label for="ssid">SSID</label>
</div>
<div class="input-field">
<input id="pass" type="text" value="" />
<label for="pass">Password</label>
</div>
<div class="input-field">
<input id="ip" type="text" value="192.168.0.2" />
<label for="ip">IP Address</label>
</div>
<div class="input-field">
<input id="gateway" type="text" value="192.168.0.1" />
<label for="gateway">Gateway IP</label>
</div>
<div class="card-action">
<a class="waves-effect waves-light btn" id="btnSetParams" onclick="joinNetwork()">Submit</a>
</div>
</div>
</div>
</div>
<div class="col s12 m6 l3"><!-- Connect to network-->
<div class="card light-blue darken-3 z-depth-3">
<div class="card-content white-text">
<span class="card-title">Networks</span>
<table id="networks">
</table>
<div class="card-action">
<a class="waves-effect waves-light btn" id="btnSetParams" onclick="scanNetworks()">Scan</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="js/aja.min.js"></script>
<script type="text/javascript" src="js/materialize.min.js"></script>
<script type="text/javascript" src="js/scripts.js"></script>
</body>
</html>

1
data/ip.txt Normal file
View File

@@ -0,0 +1 @@
192.168.0.34

9
data/js/aja.min.js vendored Normal file

File diff suppressed because one or more lines are too long

985
data/js/dragData.min.js vendored Normal file

File diff suppressed because one or more lines are too long

6
data/js/materialize.min.js vendored Normal file

File diff suppressed because one or more lines are too long

65
data/js/scripts.js Normal file
View File

@@ -0,0 +1,65 @@
var _elapsedTime = 0;
function getParams() {
aja()
.url('/params')
.on('success', function (data) {
document.getElementById('Iim').value = data.Iim || -1;
document.getElementById('Imp').value = data.Imp || -1;
document.getElementById('Tu').value = data.Tu || -1;
document.getElementById('Tl').value = data.Tl || -1;
M.updateTextFields();
})
.go();
}
function setParams() {
aja()
.method('post')
.url('/params')
.data({ Iim: document.getElementById('Iim').value, Imp: document.getElementById('Imp').value, Tu: document.getElementById('Tu').value, Tl: document.getElementById('Tl').value})
.go();
}
function joinNetwork() {
aja()
.method('post')
.url('/setWiFi')
.data({ ssid: document.getElementById('ssid').value, pass: document.getElementById('pass').value, ip: document.getElementById('ip').value, gateway: document.getElementById('gateway').value})
.go();
}
function scanNetworks() {
aja()
.url('/scanNetworks')
.on('success', function (data) {
var listHTML = "";
data.forEach((network)=>{
listHTML += '<tr><td>'+network.ssid+'</td><td>'+network.channel+'</td><td>'+network.rssi+'</td></tr>';
});
document.getElementById("networks").innerHTML = listHTML;
})
.go();
}
setInterval(function () {
aja()
.url('/sensors')
.on('success', function (data) {
document.getElementById("tempinfo").value = data.temperature || -1;
document.getElementById("humidityinfo").value = data.humidity || -1;
document.getElementById("pressureinfo").value = data.pressure || -1;
//document.getElementById("targetinfo").value = data.target.toFixed(2) || -1;
_run = data.runProfile || 0;
_elapsedTime = data.elapsedTime || 0;
M.updateTextFields();
})
.go();
}, 4000);
document.addEventListener('DOMContentLoaded', function() {
M.AutoInit();
getParams();
// var elems = document.querySelectorAll('.tooltipped');
// var instances = M.Tooltip.init(elems, ttOptions);
});

1
data/pass.txt Normal file
View File

@@ -0,0 +1 @@
redweazel2365!

1
data/settings.json Normal file
View File

@@ -0,0 +1 @@
[]

1
data/ssid.txt Normal file
View File

@@ -0,0 +1 @@
LEDE

33
data/wifimanager.html Normal file
View File

@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<head>
<title>ESP Wi-Fi Manager</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="topnav">
<h1>ESP Wi-Fi Manager</h1>
</div>
<div class="content">
<div class="card-grid">
<div class="card">
<form action="/setWiFi/" method="POST">
<p>
<label for="ssid">SSID</label>
<input type="text" id ="ssid" name="ssid"><br>
<label for="pass">Password</label>
<input type="text" id ="pass" name="pass"><br>
<label for="ip">IP Address</label>
<input type="text" id ="ip" name="ip" value="192.168.1.200"><br>
<label for="gateway">Gateway Address</label>
<input type="text" id ="gateway" name="gateway" value="192.168.1.1"><br>
<input type ="submit" value ="Submit">
</p>
</form>
</div>
</div>
</div>
</body>
</html>

39
include/README Normal file
View File

@@ -0,0 +1,39 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

46
lib/README Normal file
View File

@@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

19
platformio.ini Normal file
View File

@@ -0,0 +1,19 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:esp32cam]
platform = espressif32
board = esp32cam
framework = arduino
lib_deps =
https://github.com/me-no-dev/ESPAsyncWebServer.git
me-no-dev/AsyncTCP@^1.1.1
ayushsharma82/AsyncElegantOTA@^2.2.7
arduino-libraries/Arduino_JSON@^0.1.0

173
src/main.cpp Normal file
View File

@@ -0,0 +1,173 @@
#include <Arduino.h>
#include <AsyncElegantOTA.h>
//Peripherals includes
#include <Wire.h>
#include <SPI.h>
//Web server includes
#include <AsyncElegantOTA.h>
#include <FS.h>
#include "SPIFFS.h"
#include <ESPmDNS.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <SPIFFSEditor.h>
#include <Arduino_JSON.h>
#include <Preferences.h>
Preferences settings;
#define LED_PIN 2
//*********************************************************
// Web server variable declarations
//*********************************************************
// Create AsyncWebServer object on port 80
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
AsyncEventSource events("/events");
//Variables to save values from HTML form
JSONVar scannedSSIDs;
String ssid;
String pass;
String ip;
String gateway;
int timesConnected = 0;
const String http_username = "admin";
const String http_password = "admin";
IPAddress localIP;
//IPAddress localIP(192, 168, 1, 200); // hardcoded
// Set your Gateway IP address
IPAddress localGateway;
//IPAddress localGateway(192, 168, 1, 1); //hardcoded
IPAddress subnet(255, 255, 0, 0);
// Timer variables
unsigned long previousMillis = 0;
const long interval = 10000; // interval to wait for Wi-Fi connection (milliseconds)
#define WIFI_TIMEOUT 10000
// Stores LED state
String ledState;
// function declarations
void setup() {
}
void loop() {
}
// functions
// Initialize SPIFFS
void initSPIFFS() {
if (!SPIFFS.begin(true)) {
Serial.println("An error has occurred while mounting SPIFFS");
}
Serial.println("SPIFFS mounted successfully");
}
// Initialize WiFi
bool initWiFi() {
//scan nearby networks
WiFi.mode(WIFI_STA);
int n = WiFi.scanNetworks();
if(n){
//networks found, try to connect
String availableNetworkParams = "";
settings.begin("WiFi");
for(int i=0;i<n;i++){
//["WiFI":
// ["SSID":"pass,ip,gateway,timesConnected",
// ...
// ]
//]
availableNetworkParams = settings.getString(WiFi.SSID(i).c_str());
if(availableNetworkParams.length() > 1){
//known network
ssid = WiFi.SSID(i).c_str();
String netparams[4];
int s=0;
for(int j=0;j<4;j++){
int e = availableNetworkParams.indexOf(",",s);
netparams[j]=availableNetworkParams.substring(s,e);
s=e+1;
}
pass = netparams[0];
ip = netparams[1];
localIP.fromString(ip);
gateway = netparams[2];
localGateway.fromString(gateway);
timesConnected = netparams[3].toInt();
if (WiFi.config(localIP, localGateway, subnet)){
WiFi.begin(ssid.c_str(), pass.c_str());
Serial.println("Connecting to WiFi...");
unsigned long currentMillis = millis();
previousMillis = currentMillis;
bool res = true;
while(WiFi.status() != WL_CONNECTED) {
currentMillis = millis();
if (currentMillis - previousMillis >= WIFI_TIMEOUT) {
Serial.println("Failed to connect.");
// return false;
res = false;
}
if(!res) break;
}
// Serial.println(WiFi.localIP());
// return true;
if(res){
//save connection
settings.putString(ssid.c_str(),(pass + "," + localIP.toString() + "," + localGateway.toString() + "," + String(timesConnected + 1)).c_str());
//initWiFi() true
return true;
}
}else{
Serial.println("STA Failed to configure");
//return false;
}
}
}
settings.end();
}
//no networks found
Serial.println("Setting AP (Access Point)");
// NULL sets an open Access Point
WiFi.mode(WIFI_AP);
WiFi.softAP("ESP-WIFI", NULL);
localIP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(localIP);
return false;
}
// Replaces placeholder with LED state value
String getLEDState(const String& var) {
if(var == "STATE") {
if(digitalRead(LED_PIN)) {
ledState = "ON";
}
else {
ledState = "OFF";
}
return ledState;
}
return String();
}

11
test/README Normal file
View File

@@ -0,0 +1,11 @@
This directory is intended for PlatformIO Test Runner and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html