Merge branch 'development'

This commit is contained in:
Nic Limper
2023-02-02 20:56:39 +01:00
65 changed files with 4707 additions and 2535 deletions

View File

@@ -1,3 +1,55 @@
{
"C_Cpp.clang_format_fallbackStyle": "{ BasedOnStyle: Google, IndentWidth: 4, ColumnLimit: 0}"
"C_Cpp.clang_format_fallbackStyle": "{ BasedOnStyle: Google, IndentWidth: 4, ColumnLimit: 0}",
"files.associations": {
"array": "cpp",
"atomic": "cpp",
"*.tcc": "cpp",
"bitset": "cpp",
"cctype": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"map": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"optional": "cpp",
"random": "cpp",
"regex": "cpp",
"string": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"fstream": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"istream": "cpp",
"limits": "cpp",
"new": "cpp",
"ostream": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"cinttypes": "cpp",
"typeinfo": "cpp"
}
}

BIN
esp32_fw/data/alignment.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,66 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
<title>Solum - alternative proto AP</title>
<link rel="stylesheet" href="main.css" type="text/css"/>
</head>
<body>
<header>
<div class="logo">Solum - alternative proto AP</div>
</header>
<div class="container">
<div class="blocks"><ul></ul></div>
<div class="window">
<iframe name="empty" style="border:1px solid darkgrey; width:100%; height:4em;"></iframe>
<div class="actionbox">
<form action="send_image" method="POST" target="empty">
DST:<input name="dst" class="dst" size=12 maxlength=12 type="text" placeholder="001122334455">
Filename:<input name="filename" type="text" placeholder="default.bmp">
Next check-in:<input name="ttl" type="text" placeholder="1"> min
<input type="submit" value="Send Image">
</form>
</div>
<div class="actionbox">
<form action="send_fw" method="POST" target="empty">
DST:<input name="dst" class="dst" size=12 maxlength=12 type="text" placeholder="001122334455">
Filename:<input name="filename" type="text" placeholder="update.bin">
<input type="submit" value="Send FW Update">
</form>
</div>
<div class="actionbox">
<form action="req_checkin" method="POST" target="empty">
DST:<input name="dst" class="dst" size=12 maxlength=12 type="text" placeholder="001122334455">
<input type="submit" value="Request Check-in">
</form>
</div>
<div id="messages">
<ul class="messages">
</ul>
</div>
</div>
</div>
<script src="jquery.js"></script>
<script src="main.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

BIN
esp32_fw/data/kat-bw29.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -1,203 +0,0 @@
*{
margin:0;
padding:0;
border:0;
list-style-type: none;
-webkit-appearance: none;
outline: none;
font-weight: 400;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
font-smooth: auto;
-webkit-font-smoothing: antialiased;
}
html, body {
height: 100%;
}
body {
font-size: 12px;
font-family: Helvetica, Arial, Verdana, sans-serif;
line-height: 1.5;
background-color: #e8f1f9;
background-color: #f1f1f1;
/*background-image: linear-gradient(315deg, #dde1ee 0%, #e8f1f9 100%);*/
}
header {
height: 50px;
background-color: #666;
}
.logo {
margin: 0 auto;
width: 50px;
height: 50px;
text-indent: 100px;
overflow:hidden;
background: url('p2000.svg') center center no-repeat;
background-size: 50px 50px;
}
.container {
}
.blocks {
padding: 21px 0;
overflow-x: auto;
text-align:center;
}
.blocks ul {
display:table;
margin: 0 auto;
}
.blocks ul li {
display:table-cell;
vertical-align:top;
padding: 0 4px;
position: relative;
}
.blocks ul li:nth-child(6) div.b:before, .blocks ul li:nth-child(6) div.b:after {
content:"";
position:absolute;
margin-left: -5px;
left: 50%;
width: 0;
height: 0;
}
.blocks ul li:nth-child(6) div.b:before {
top: -5px;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 5px solid #c30;
}
.blocks ul li:nth-child(6) div.b:after {
bottom: -5px;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 5px solid #c30;
}
.blocks ul li, .blocks ul li div {
}
.blocks ul li div {
width: 80px;
padding: 0 4px;
}
.blocks ul li div.b {
position:relative;
padding: 6px 0;
background: #fff;
}
.blocks ul li div.rxt {
background-color: #e5f9ea;
}
.blocks ul li div.txt {
background-color: #f9eae5;
}
.blocks ul li div.b {
}
.window {
margin: 0 auto;
max-width: 94%;
background: #fff;
}
form {
padding: 3px;
}
.actionbox .dst {
size:12em;
}
.actionbox input {
border: solid 1px black;
padding: 2px;
}
input.actionbox {
border: solid 1px black;
}
ul.messages {
padding: 12px 0;
}
ul.messages li {
line-height: 22px;
position: relative;
padding: 0 12px;
}
ul.messages li.new {
-webkit-animation-name: new;
-webkit-animation-duration: 1400ms;
-webkit-animation-iteration-count: 1;
-webkit-animation-timing-function: ease-in-out;
}
ul.messages li div.message {
padding-left: 58px;
}
ul.messages li div.date {
position: absolute;
left: 12px
}
span.pending {
padding-left: 28px;
}
span.pending:before {
content:"";
position:absolute;
display:inline-block;
top: 0;
left: 12px;
width: 22px;
height: 22px;
background: url('roll.svg') center center no-repeat;
background-size: 22px 22px;
}
@media(max-width: 460px) {
.messages li div, ul.messages li div.date, ul.messages li div.message {
display:block;
position:relative;
padding: 0;
left: auto;
}
.messages li div.message, li.pending {
margin-bottom: 8px;
}
ul.messages {
padding-bottom: 4px;
}
}
@-webkit-keyframes new {
0% {
background-color: rgba(255, 255, 204, 1);
}
50% {
background-color: rgba(255, 255, 204, .5);
}
100% {
background-color: rgba(255, 255, 204, 0);
}
}

View File

@@ -1,23 +0,0 @@
$.fn.htmlTo = function(elem) {
return this.each(function() {
$(elem).html($(this).html());
});
}
let socket = new WebSocket("ws://" + location.host + "/ws");
socket.onmessage = function(event) {
let incomingMessage = event.data;
showMessage(incomingMessage);
};
socket.onclose = event => console.log(`Closed ${event.code}`);
function showMessage(message) {
$( "<li/>", {
"class": "new",
html: message
}).prependTo( "ul.messages" );
}

777
esp32_fw/data/www/edit.html Normal file
View File

@@ -0,0 +1,777 @@
<!--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>
<link rel="icon" href="data:,">
<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 li {
display: flex;
justify-content: stretch;
}
.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 .icon {
padding: 0 0 0 5px;
cursor: pointer;
display: inline-block;
width: 20px;
text-align: center;
font-size: 1.1em;
}
.tvu .filename {
padding: 2px 0 0 2px;
cursor: pointer;
display: inline-block;
width: auto;
flex: 2;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.tvu .size {
padding: 2px 5px 0 5px;
cursor: pointer;
display: inline-block;
text-align: right;
}
.tvu li:hover {
background-color:#cccccc;
}
#uploader {
position: absolute;
top: 0;
right: 0;
left: 0;
background-color: #444;
color: #EEE;
height: 45px;
}
button {
background-color: #eeeeee;
border: 0px;
cursor: pointer;
padding: 3px 5px;
margin: 10px;
appearance: none;
}
input {
background: #ffffff;
border: 0px;
appearance: none;
padding: 2px 5px;
margin: 10px;
}
input[type="file"] {
background: none;
}
.uploadpath {
background: none;
color: white;
}
#tree {
position: absolute;
top: 50px;
bottom: 0;
left: 0;
width: 300px;
}
#editor,
#preview {
position: absolute;
top: 45px;
right: 0;
bottom: 0;
left: 300px;
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: 40px;
height: 40px;
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 path = ce("input");
path.id = "upload-path";
path.type = "text";
path.name = "path";
path.classList.add("uploadpath");
path.defaultValue = "/";
ge(element).appendChild(path);
var input = ce("input");
input.type = "file";
input.multiple = false;
input.name = "data";
input.id = "upload-select";
ge(element).appendChild(input);
/*
var mkfile = ce("button");
mkfile.innerHTML = 'Create';
ge(element).appendChild(mkfile);
*/
var filename = ce("input");
filename.id = "editor-filename";
filename.type = "text";
filename.size = 20;
ge(element).appendChild(filename);
var button = ce("button");
button.id = "button";
button.innerHTML = 'Upload';
button.style.display = 'none';
ge(element).appendChild(button);
var savefile = ce("button");
savefile.id = "savefile";
savefile.innerHTML = ' Save ';
savefile.style.display = 'none';
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(filename.value);
editor.loadUrl(filename.value);
path.value = (filename.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], ge("editor-filename").value);
requests.add("POST", "/edit", formData, httpPostProcessRequest);
var uploadSelect = ge("upload-select");
uploadSelect.value = "";
};
input.onchange = function (e) {
if (input.files.length === 0) return;
var filename = input.files[0].name;
ge("editor-filename").value = path.value + (path.value=="/" ? "" : "/") + filename;
button.style.display = 'inline-block';
savefile.style.display = 'none';
};
}
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) {
ge("button").style.display = 'none';
ge("savefile").style.display = 'none';
var edfname = ge("editor-filename");
var filename = path;
var ext = /(?:\.([^.]+))?$/.exec(filename)[1];
var name = /(.*)\.[^.]+$/.exec(filename)[1];
if (typeof name !== undefined) {
filename = name;
}
edfname.value = filename + "." + ext;
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 = path + (path != "/" ? "/":"") + name;
var icon = ce("span");
icon.innerHTML = "&#128196;";
icon.classList.add("icon");
leaf.appendChild(icon);
var label = ce("span");
label.innerHTML = name;
label.classList.add("filename");
leaf.appendChild(label);
var filesize = ce("span");
filesize.innerHTML = formatBytes(size);
filesize.classList.add("size");
leaf.appendChild(filesize);
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 createDirLeaf(path, name, size) {
var leaf = ce("li");
leaf.id = path + (path != "/" ? "/" : "") + name;
var icon = ce("span");
icon.innerHTML = "&#128193;";
icon.classList.add("icon");
leaf.appendChild(icon);
var label = ce("span");
label.innerHTML = name;
label.classList.add("filename");
leaf.appendChild(label);
var filesize = ce("span");
filesize.innerHTML = "";
filesize.classList.add("size");
leaf.appendChild(filesize);
leaf.onclick = function (e) {
treeRoot.removeChild(treeRoot.childNodes[0]);
if (name == "..") {
httpGet(treeRoot, "/");
} else {
httpGet(treeRoot, "/" + name);
}
};
leaf.oncontextmenu = function (e) {
e.preventDefault();
e.stopPropagation();
showContextMenu(e, leaf.id, true);
};
return leaf;
}
function formatBytes(bytes, decimalPlaces = 2) {
if (bytes === 0) return '';
const k = 1024;
const dm = decimalPlaces < 0 ? 0 : decimalPlaces;
const sizes = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
function addList(parent, path, items) {
ge("upload-path").value = path
ge("editor-filename").value = path
sortByKey(items, 'name');
var list = ce("ul");
parent.appendChild(list);
if (path != "/") {
list.appendChild(createDirLeaf("/", "..", 0));
}
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));
}
if (items[i].type === "dir") {
list.insertBefore(createDirLeaf(path, items[i].name, items[i].size), list.firstChild);
}
}
}
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, path);
};
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);
},
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) {
ge("button").style.display = 'none';
ge("savefile").style.display = 'inline-block';
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);
};
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>

View File

@@ -0,0 +1,105 @@
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" />
<title>Solum - alternative proto AP</title>
<link rel="stylesheet" href="main.css" type="text/css" />
<link rel="icon" href="data:,">
</head>
<body>
<header>
<div class="logo">Solum - alternative proto AP</div>
</header>
<div id="configbox">
<div class="closebtn">&#10006;</div>
<h3 id="cfgmac">00000000</h3>
<p>
<label for="cfgalias">Alias</label>
<input id="cfgalias" type="text">
</p>
<p>
<label for="cfgmodel">Model</label>
<select id="cfgmodel">
<option value="0">unknown</option>
<option value="1">1.54" 152x152px</option>
<option value="2">2.9" 296x128px</option>
<option value="3">4.2" 400x300px</option>
</select>
</p>
<p>
<label for="cfgcontent">Content</label>
<select id="cfgcontent" onchange="contentselected()">
<option value="0">static image</option>
<option value="1">current date</option>
<option value="2">count days</option>
<option value="3">count hours</option>
<option value="4" disabled>current weather</option>
<option value="6" disabled>memo text</option>
<option value="7">image url</option>
<option value="5">firmware update</option>
</select>
</p>
<div id="customoptions"></div>
<p>
<input type="button" value="Save" id="cfgsave">
<span id="cfgdelete"><img src="data:image/gif;base64,R0lGODlhEAAQAPMAANXV1e3t7d/f39HR0dvb2/Hx8dTU1OLi4urq6mZmZpmZmf///wAAAAAAAAAAAAAAACH5BAEAAAwALAAAAAAQABAAAARBkMlJq71Yrp3ZXkr4WWCYnOZSgQVyEMYwJCq1nHhe20qgCAoA7QLyAYU7njE4JPV+zOSkCEUSFbmTVPPpbjvgTAQAOw==
"></span>
</p>
</div>
<form>
<div class="container">
<div class="window">
<div class="actionbox">
<div>
<div class="editbtn"><a href="/edit" target="littlefs" class="filebutton">edit littleFS</a></div>
Currently active tags:<br>
</div>
</div>
<div id="taglist" class="taglist">
<div class="tagcard" id="tagtemplate">
<div class="currimg"><img class="tagimg" src=""></div>
<div class="mac"></div>
<div class="alias"></div>
<div class="model"></div>
<div class="contentmode"></div>
<div class="pending"></div>
<div class="lastseen"></div>
<div class="nextcheckin"></div>
<div class="nextupdate"></div>
<div class="corner">
<div class="warningicon">&#9888;</div>
<div class="configicon"></div>
</div>
</div>
</div>
<div class="logbox">
<p>
<span>logging</span>
<span><img id="clearlog" src="data:image/gif;base64,R0lGODlhEAAQAPMAANXV1e3t7d/f39HR0dvb2/Hx8dTU1OLi4urq6mZmZpmZmf///wAAAAAAAAAAAAAAACH5BAEAAAwALAAAAAAQABAAAARBkMlJq71Yrp3ZXkr4WWCYnOZSgQVyEMYwJCq1nHhe20qgCAoA7QLyAYU7njE4JPV+zOSkCEUSFbmTVPPpbjvgTAQAOw==
"></span>
<span id="sysinfo"></span>
</p>
<ul id="messages" class="messages">
</ul>
</div>
</div>
</div>
</form>
<script src="main.js"></script>
</body>
</html>

277
esp32_fw/data/www/main.css Normal file
View File

@@ -0,0 +1,277 @@
*{
margin:0;
padding:0;
border:0;
list-style-type: none;
outline: none;
font-weight: 400;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
font-smooth: auto;
-webkit-font-smoothing: antialiased;
}
html, body {
height: 100%;
}
body {
font-size: 12px;
font-family: Helvetica, Arial, Verdana, sans-serif;
line-height: 1.5;
background-color: #e8f1f9;
background-color: #f1f1f1;
/*background-image: linear-gradient(315deg, #dde1ee 0%, #e8f1f9 100%);*/
}
header {
height: 50px;
background-color: #666;
}
label {
width:100px;
display: inline-block;
}
.logo {
margin: 0 auto;
height: 50px;
text-indent: 50px;
overflow:hidden;
background-size: 50px 50px;
font-size: 2.5em;
color: white;
}
.window {
margin: 0 auto;
max-width: 94%;
}
.actionbox>div:first-child {
padding: 10px;
background-color: white;
margin: 5px;
}
.actionbox p {
padding: 5px;
}
.actionbox .columns {
display:flex;
flex-wrap: wrap;
}
.filebutton {
padding:2px 5px;
background-color: #cccccc;
text-decoration: none;
color: black;
}
.editbtn {
float:right;
}
.columns div {
flex: 1;
}
input {
border: solid 1px #666666;
padding: 4px;
}
input[type=button] {
border: 0px;
padding: 4px 10px;
cursor:pointer;
}
input[type=button]:hover {
background-color:#aaaaaa;
}
select {
padding: 4px;
}
#configbox {
display: none;
position: fixed;
top: 80px;
left: 50px;
width: 500px;
padding: 15px;
background-color: #f0e6d3;
z-index: 999;
box-shadow: 7px 10px 52px -19px rgba(0, 0, 0, 0.63);
}
#configbox p {
padding: 5px;
}
#configbox h3 {
font-size: 1.5em;
font-weight: bold;
}
#configbox input {
border: solid 1px #666666;
padding: 4px;
}
#configbox label {
text-transform: capitalize;
}
#cfgdelete {
position: absolute;
bottom: 15px;
right: 15px;
cursor:pointer;
}
.closebtn {
border: 1px solid black;
float: right;
width: 19px;
height: 20px;
font-size: 1.1em;
text-align: center;
margin: 5px;
cursor: pointer;
}
.logbox {
margin: 5px;
}
.logbox p {
background-color: #ffffff;
padding: 5px 10px;
}
.logbox img {
vertical-align: bottom;
cursor:pointer;
}
.logbox #sysinfo {
float: right;
}
.taglist {
display: flex;
flex-wrap: wrap;
}
#tagtemplate {
display:none;
}
.tagcard {
width: 225px;
position: relative;
height: 170px;
margin: 5px;
padding: 5px;
background-color: #dddddd;
}
.tagcard .pending {
padding-bottom:15px;
}
.currimg {
float: right;
}
.currimg img {
max-width: 50px;
}
.mac {
font-size: 0.9em;
cursor:pointer;
}
.alias {
font-size: 1.4em;
font-weight: bold;
}
.corner {
position: absolute;
right: 0px;
bottom: 0px;
padding: 5px;
}
.configicon {
display: inline-block;
width: 20px;
height: 20px;
cursor:pointer;
background-image: url("data: image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAQAAAAngNWGAAABXklEQVQoz43SP2tUQRQF8N99ecFKFEVh0Y1ZbFQ2xEoIQkoLW0t7C/OVLGzyHay1EAQjRFGrLMkjKSwWsRGV3WvxZvc9XQtnivl35sydc048nfuvVkW0k8XYtT936pVja5jJv/Z7wPZo0wNzLzQSHWnVwTZtqY1su2to3bZhj7dewEYeu+iDawi7bhn7Yl9TWGsikrzksnNxr9zfsIErcaEAMysyZXrvE5mp6w7zc8rMzFJjGNlxFWHmWKMVd2DHsHuadQ+NkcKxZ2pPXC8FHHhu1v91X6ZcUZHYy8xwwyju5wAzJypDFdHka5OiaN1yHDl100BaMyq84cwrs1JjtTBu7E5xIpeebLndOZOlnK859d1bZ0JoHPiRU986ZxYZmdjP8/HRrkfSS2+MTTXlycyawhlHJBOH5k789A6x/H/sZQuMKKNatjGLvkoreUy/lsnsq1mXJOe/Ut6tM38DZpmDFxwTi8EAAAAASUVORK5CYII=");
}
.warningicon {
display:none;
font-size: 1.3em;
background-color: yellow;
color: black;
height: 20px;
width: 20px;
vertical-align: top;
text-align: center;
}
ul.messages {
padding: 5px;
}
ul.messages li {
position: relative;
}
ul.messages li.new {
animation-name: new;
animation-duration: 1400ms;
animation-iteration-count: 1;
animation-timing-function: ease-in-out;
}
.error {
color: red;
}
@media(max-width: 460px) {
.messages li div, ul.messages li div.date, ul.messages li div.message {
display:block;
position:relative;
padding: 0;
left: auto;
}
.messages li div.message, li.pending {
margin-bottom: 8px;
}
ul.messages {
padding-bottom: 4px;
}
}
@keyframes new {
0% {
background-color: rgba(255, 255, 204, 1);
}
50% {
background-color: rgba(255, 255, 204, .5);
}
100% {
background-color: rgba(255, 255, 204, 0);
}
}

280
esp32_fw/data/www/main.js Normal file
View File

@@ -0,0 +1,280 @@
const $ = document.querySelector.bind(document);
const contentModes = ["static image", "current date", "counting days", "counting hours", "current weather", "firmware update", "memo text", "image url"];
const models = ["unknown type", "1.54\" 152x152px", "2.9\" 296x128px", "4.2\" 400x300px"];
const contentModeOptions = [];
contentModeOptions[0] = ["filename","timetolive"];
contentModeOptions[1] = [];
contentModeOptions[2] = ["counter", "thresholdred"];
contentModeOptions[3] = ["counter", "thresholdred"];
contentModeOptions[4] = ["location"];
contentModeOptions[5] = ["filename"];
contentModeOptions[6] = ["text"];
contentModeOptions[7] = ["url","interval"];
const imageQueue = [];
let isProcessing = false;
let servertimediff = 0;
let socket;
connect();
setInterval(updatecards, 1000);
window.addEventListener("load", function () { loadTags(0) });
function loadTags(pos) {
fetch("/get_db?pos="+pos)
.then(response => response.json())
.then(data => {
processTags(data.tags);
if (data.continu && data.continu>pos) loadTags(data.continu);
})
//.catch(error => showMessage('loadTags error: ' + error));
}
function connect() {
socket = new WebSocket("ws://" + location.host + "/ws");
socket.addEventListener("open", (event) => {
showMessage("websocket connected");
});
socket.addEventListener("message", (event) => {
console.log(event.data);
const msg = JSON.parse(event.data);
if (msg.logMsg) {
showMessage(msg.logMsg,false);
}
if (msg.errMsg) {
showMessage(msg.logMsg,true);
}
if (msg.tags) {
processTags(msg.tags);
}
if (msg.sys) {
$('#sysinfo').innerHTML = 'free heap: ' + msg.sys.heap + ' bytes &#x2507; db size: ' + msg.sys.dbsize + ' bytes &#x2507; db record count: ' + msg.sys.recordcount + ' &#x2507; littlefs free: ' + msg.sys.littlefsfree + ' bytes';
servertimediff = (Date.now() / 1000) - msg.sys.currtime;
}
});
socket.addEventListener("close", (event) => {
showMessage(`websocket closed ${event.code}`);
setTimeout(connect, 5000);
});
}
function processTags(tagArray) {
for (const element of tagArray) {
tagmac = element.mac;
var div = $('#tag' + tagmac);
if (div == null) {
div = $('#tagtemplate').cloneNode(true);
div.setAttribute('id', 'tag'+tagmac);
div.dataset.mac = tagmac;
$('#taglist').appendChild(div);
$('#tag' + tagmac + ' .mac').innerHTML = tagmac;
var img = $('#tag' + tagmac + ' .tagimg');
img.addEventListener('error', function handleError() {
img.style.display = 'none';
});
}
div.style.display = 'block';
let alias = element.alias;
if (!alias) alias = tagmac;
$('#tag' + tagmac + ' .alias').innerHTML = alias;
if (div.dataset.hash != element.hash) loadImage(tagmac, '/current/' + tagmac + '.bmp?' + (new Date()).getTime());
$('#tag' + tagmac + ' .contentmode').innerHTML = contentModes[element.contentmode];
$('#tag' + tagmac + ' .model').innerHTML = models[element.model];
if (element.nextupdate > 1672531200 && element.nextupdate!=3216153600) {
var date = new Date(element.nextupdate * 1000);
var options = { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false };
$('#tag' + tagmac + ' .nextupdate').innerHTML = "next update: " + date.toLocaleString('nl-NL', options);
} else {
$('#tag' + tagmac + ' .nextupdate').innerHTML = "";
}
if (element.nextcheckin > 1672531200) {
div.dataset.nextcheckin = element.nextcheckin;
} else {
div.dataset.nextcheckin = element.lastseen + 1800;
}
div.dataset.lastseen = element.lastseen;
div.dataset.hash = element.hash;
$('#tag' + tagmac + ' .warningicon').style.display = 'none';
if (element.pending) $('#tag' + tagmac + ' .pending').innerHTML = "pending update..."; else $('#tag' + tagmac + ' .pending').innerHTML = "";
}
}
function updatecards() {
document.querySelectorAll('[data-mac]').forEach(item => {
let tagmac = item.dataset.mac;
if (item.dataset.lastseen && item.dataset.lastseen > 1672531200) {
let idletime = (Date.now() / 1000) + servertimediff - item.dataset.lastseen;
$('#tag' + tagmac + ' .lastseen').innerHTML = "last seen: "+displayTime(Math.floor(idletime))+" ago";
if ((Date.now() / 1000) + servertimediff > item.dataset.nextcheckin) $('#tag' + tagmac + ' .warningicon').style.display='inline-block';
} else {
$('#tag' + tagmac + ' .lastseen').innerHTML = ""
}
if (item.dataset.nextcheckin > 1672531200) {
let nextcheckin = item.dataset.nextcheckin - ((Date.now() / 1000) + servertimediff);
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "expecting next checkin: " + displayTime(Math.floor(nextcheckin));
}
})
}
$('#clearlog').onclick = function () {
$('#messages').innerHTML='';
}
$('.closebtn').onclick = function (event) {
event.target.parentNode.style.display='none';
}
$('#taglist').addEventListener("click", (event) => {
let currentElement = event.target;
while (currentElement !== $('#taglist')) {
if (currentElement.classList.contains("tagcard")) {
break;
}
currentElement = currentElement.parentNode;
}
if (!currentElement.classList.contains("tagcard")) {
return;
}
const mac = currentElement.dataset.mac;
if (event.target.classList.contains("mac")) {
$('#dstmac').value=mac;
}
if (event.target.classList.contains("configicon")) {
$('#cfgmac').innerHTML = mac;
$('#cfgmac').dataset.mac = mac;
fetch("/get_db?mac=" + mac)
.then(response => response.json())
.then(data => {
console.log(data);
var tagdata = data.tags[0];
$('#cfgalias').value = tagdata.alias;
$('#cfgcontent').value = tagdata.contentmode;
$('#cfgmodel').value = tagdata.model;
$('#cfgcontent').dataset.json = tagdata.modecfgjson;
contentselected();
$('#configbox').style.display = 'block';
})
.catch(error => showMessage('Error: ' + error));
}
})
$('#cfgsave').onclick = function () {
let contentmode = $('#cfgcontent').value;
let extraoptions = contentModeOptions[contentmode];
let obj={};
extraoptions.forEach(element => {
obj[element] = $('#opt' + element).value;
});
let formData = new FormData();
formData.append("mac", $('#cfgmac').dataset.mac);
formData.append("alias", $('#cfgalias').value);
formData.append("contentmode", contentmode);
formData.append("model", $('#cfgmodel').value);
formData.append("modecfgjson", JSON.stringify(obj));
fetch("/save_cfg", {
method: "POST",
body: formData
})
.then(response => response.text())
.then(data => showMessage(data))
.catch(error => showMessage('Error: ' + error));
$('#configbox').style.display = 'none';
}
$('#cfgdelete').onclick = function () {
let mac = $('#cfgmac').dataset.mac;
}
function contentselected() {
let contentmode=$('#cfgcontent').value;
let extraoptions = contentModeOptions[contentmode];
$('#customoptions').innerHTML="";
var obj = {};
if ($('#cfgcontent').dataset.json && ($('#cfgcontent').dataset.json!="null")) {
obj = JSON.parse($('#cfgcontent').dataset.json);
}
console.log(obj);
extraoptions.forEach(element => {
var label = document.createElement("label");
label.innerHTML = element;
label.setAttribute("for", 'opt' + element);
var input = document.createElement("input");
input.type = "text";
input.id = 'opt' + element;
if (obj[element]) input.value = obj[element];
var p = document.createElement("p");
p.appendChild(label);
p.appendChild(input);
$('#customoptions').appendChild(p);
});
}
function showMessage(message,iserr) {
const messages = $('#messages');
var date = new Date(),
time = date.toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute:'2-digit', second:'2-digit'});
if (iserr) {
messages.insertAdjacentHTML("afterbegin", '<li class="new error">' + htmlEncode(time + ' ' + message) + '</li>');
} else {
messages.insertAdjacentHTML("afterbegin", '<li class="new">'+htmlEncode(time+' '+message)+'</li>');
}
}
function htmlEncode(input) {
const textArea = document.createElement("textarea");
textArea.innerText = input;
return textArea.innerHTML.split("<br>").join("\n");
}
function loadImage(id, imageSrc) {
imageQueue.push({ id, imageSrc });
if (!isProcessing) {
processQueue();
}
}
function processQueue() {
if (imageQueue.length === 0) {
isProcessing = false;
return;
}
isProcessing = true;
const { id, imageSrc } = imageQueue.shift();
const image = $('#tag' + id + ' .tagimg');
image.onload = function () {
image.style.display = 'block';
processQueue();
}
image.onerror = function () {
image.style.display = 'none';
processQueue();
};
image.src = imageSrc;
}
function displayTime(seconds) {
let hours = Math.floor(Math.abs(seconds) / 3600);
let minutes = Math.floor((Math.abs(seconds) % 3600) / 60);
let remainingSeconds = Math.abs(seconds) % 60;
return (seconds < 0 ? '-' : '') + (hours > 0 ? `${hours}:${String(minutes).padStart(2, '0')}` : `${minutes}`) + `:${String(remainingSeconds).padStart(2, '0')}`;
}

View File

@@ -56,4 +56,6 @@ struct pendingData {
} __packed;
#define BLOCK_DATA_SIZE 4096
#define BLOCK_XFER_BUFFER_SIZE BLOCK_DATA_SIZE + sizeof(struct blockData)
#define BLOCK_XFER_BUFFER_SIZE BLOCK_DATA_SIZE + sizeof(struct blockData)
#pragma pack(pop)

View File

@@ -0,0 +1,15 @@
#include <Arduino.h>
#include <LittleFS.h>
#include "makeimage.h"
#include <time.h>
#include "tag_db.h"
#include <TFT_eSPI.h>
void contentRunner();
void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo);
bool updateTagImage(String &filename, uint8_t *dst, uint16_t nextCheckin);
void drawDate(String &filename);
void drawNumber(String &filename, int32_t count, int32_t thresholdred);
bool getImgURL(String &filename, String URL, time_t fetched);
char *formatHttpDate(time_t t);

View File

@@ -0,0 +1,35 @@
#include <Arduino.h>
#include <TFT_eSPI.h>
#pragma once
struct BitmapFileHeader {
uint8_t sig[2];
uint32_t fileSz;
uint8_t rfu[4];
uint32_t dataOfst;
uint32_t headerSz; //40
int32_t width;
int32_t height;
uint16_t colorplanes; //must be one
uint16_t bpp;
uint32_t compression;
uint32_t dataLen; //may be 0
uint32_t pixelsPerMeterX;
uint32_t pixelsPerMeterY;
uint32_t numColors; //if zero, assume 2^bpp
uint32_t numImportantColors;
} __attribute__((packed));
enum EinkClut {
EinkClutTwoBlacks = 0,
EinkClutTwoBlacksAndRed,
EinkClutFourBlacks,
EinkClutThreeBlacksAndRed,
};
void spr2grays(TFT_eSprite &spr, long w, long h, String &fileout);
void jpg2grays(String filein, String fileout);
void bmp2grays(String filein, String fileout);

View File

@@ -21,3 +21,5 @@ class pendingdata {
void garbageCollection(void* parameter);
extern std::vector<pendingdata*> pendingfiles;
#pragma pack(pop)

47
esp32_fw/include/tag_db.h Normal file
View File

@@ -0,0 +1,47 @@
#include <Arduino.h>
#include <ArduinoJson.h>
#include <vector>
#pragma pack(push, 1)
#pragma once
enum contentModes {
Image,
Today,
CountDays,
CountHours,
Weather,
Firmware,
Memo,
ImageUrl,
};
class tagRecord {
public:
uint16_t nextCheckinpending;
tagRecord() : mac{0}, model(0), alias(""), lastseen(0), nextupdate(0), contentMode(Image), pending(false), button(false), md5{0}, md5pending{0}, CheckinInMinPending(0), expectedNextCheckin(0), modeConfigJson("") {}
uint8_t mac[6];
u_int8_t model;
String alias;
uint32_t lastseen;
uint32_t nextupdate;
contentModes contentMode;
bool pending;
bool button;
uint8_t md5[16];
uint8_t md5pending[16];
uint16_t CheckinInMinPending;
uint32_t expectedNextCheckin;
String modeConfigJson;
static tagRecord* findByMAC(uint8_t mac[6]);
};
extern std::vector<tagRecord*> tagDB;
String tagDBtoJson(uint8_t mac[6] = nullptr, uint8_t startPos = 0);
void fillNode(JsonObject &tag, tagRecord* &taginfo);
void saveDB(String filename);
void loadDB(String filename);
#pragma pack(pop)

View File

@@ -1,13 +1,19 @@
#include <Arduino.h>
#include<Arduino.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
void init_web();
void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final);
extern void webSocketSendProcess(void *parameter);
extern void wsString(String text);
void wsLog(String text);
void wsErr(String text);
void wsSendTaginfo(uint8_t mac[6]);
void wsSendSysteminfo();
extern uint64_t swap64(uint64_t x);
extern AsyncWebSocket ws;//("/ws");
extern AsyncWebSocket ws; //("/ws");
extern SemaphoreHandle_t wsMutex;
extern TaskHandle_t websocketUpdater;

View File

@@ -22,5 +22,7 @@ lib_deps =
https://github.com/me-no-dev/ESPAsyncWebServer
https://github.com/tzapu/WiFiManager.git#feature_asyncwebserver
bblanchon/ArduinoJson
bodmer/TFT_eSPI
https://github.com/Bodmer/TJpg_Decoder.git
upload_port = COM5
monitor_port = COM5

View File

@@ -2,396 +2,9 @@
#include <FS.h>
// File: edit.htm.gz, Size: 4151
#define edit_htm_gz_len 4151
const uint8_t edit_htm_gz[] PROGMEM = {
0x1F, 0x8B, 0x08, 0x08, 0xB8, 0x94, 0xB1, 0x59, 0x00, 0x03, 0x65, 0x64, 0x69, 0x74, 0x2E, 0x68,
0x74, 0x6D, 0x00, 0xB5, 0x3A, 0x0B, 0x7B, 0xDA, 0xB8, 0xB2, 0x7F, 0xC5, 0x71, 0xCF, 0x66, 0xED,
0x83, 0x31, 0x90, 0xA4, 0xD9, 0xD6, 0xC4, 0xC9, 0x42, 0x92, 0x36, 0x6D, 0xF3, 0x6A, 0x80, 0xB6,
0x69, 0x4F, 0xEE, 0x7E, 0xC2, 0x16, 0xA0, 0xC6, 0x96, 0x5D, 0x5B, 0x0E, 0x49, 0x59, 0xFE, 0xFB,
0x9D, 0x91, 0x6C, 0xB0, 0x09, 0x69, 0x77, 0xCF, 0xBD, 0xBB, 0xDD, 0x2D, 0x92, 0x46, 0x33, 0x9A,
0x19, 0xCD, 0x53, 0xDE, 0xBD, 0x8D, 0xA3, 0x8B, 0xC3, 0xFE, 0xF5, 0xE5, 0xB1, 0x36, 0x11, 0x61,
0xB0, 0xBF, 0x87, 0x7F, 0x6B, 0x01, 0xE1, 0x63, 0x97, 0xF2, 0xFD, 0x3D, 0xC1, 0x44, 0x40, 0xF7,
0x8F, 0x7B, 0x97, 0xDA, 0xB1, 0xCF, 0x44, 0x94, 0xEC, 0x35, 0xD4, 0xCA, 0x5E, 0x2A, 0x1E, 0x02,
0xAA, 0x85, 0xD4, 0x67, 0xC4, 0x4D, 0xBD, 0x84, 0xC2, 0x66, 0xDB, 0x0B, 0x67, 0xDF, 0xEB, 0x8C,
0xFB, 0xF4, 0xDE, 0xD9, 0x6E, 0x36, 0xDB, 0x71, 0x94, 0x32, 0xC1, 0x22, 0xEE, 0x90, 0x61, 0x1A,
0x05, 0x99, 0xA0, 0xED, 0x80, 0x8E, 0x84, 0xF3, 0x3C, 0xBE, 0x6F, 0x0F, 0xA3, 0xC4, 0xA7, 0x89,
0xD3, 0x8A, 0xEF, 0x35, 0x00, 0x31, 0x5F, 0x7B, 0xB6, 0xB3, 0xB3, 0xD3, 0x1E, 0x12, 0xEF, 0x76,
0x9C, 0x44, 0x19, 0xF7, 0xEB, 0x5E, 0x14, 0x44, 0x89, 0xF3, 0x6C, 0xF4, 0x1C, 0xFF, 0xB4, 0x7D,
0x96, 0xC6, 0x01, 0x79, 0x70, 0x78, 0xC4, 0x29, 0xE0, 0xDE, 0xD7, 0xD3, 0x09, 0xF1, 0xA3, 0xA9,
0xD3, 0xD4, 0x9A, 0x5A, 0xAB, 0x09, 0x44, 0x92, 0xF1, 0x90, 0x18, 0x4D, 0x0B, 0xFF, 0xD8, 0x3B,
0x66, 0x7B, 0x14, 0x71, 0x51, 0x4F, 0xD9, 0x77, 0xEA, 0xB4, 0xB6, 0xE0, 0x34, 0x39, 0x1D, 0x91,
0x90, 0x05, 0x0F, 0x4E, 0x4A, 0x78, 0x5A, 0x4F, 0x69, 0xC2, 0x46, 0x6A, 0x79, 0x4A, 0xD9, 0x78,
0x22, 0x9C, 0xDF, 0x9A, 0xCD, 0x39, 0xF0, 0xAF, 0x65, 0xC1, 0x2C, 0x60, 0x29, 0x20, 0xA3, 0x78,
0xEA, 0x3C, 0x11, 0xC5, 0x4E, 0x53, 0xB1, 0xDE, 0x6C, 0x87, 0x24, 0x19, 0x33, 0x0E, 0x83, 0x98,
0xF8, 0x3E, 0xE3, 0x63, 0x47, 0xA1, 0x05, 0x6C, 0xB6, 0x90, 0x36, 0xA1, 0x01, 0x11, 0xEC, 0x8E,
0xB6, 0x43, 0xC6, 0xEB, 0x53, 0xE6, 0x8B, 0x89, 0xB3, 0x0B, 0x3C, 0xB6, 0xBD, 0x2C, 0x49, 0x41,
0xA6, 0x38, 0x62, 0x5C, 0xD0, 0x44, 0xA2, 0xA5, 0x31, 0xE1, 0xB3, 0x5C, 0x54, 0x54, 0x40, 0x21,
0x27, 0xE3, 0x01, 0xE3, 0xB4, 0x3E, 0x0C, 0x22, 0xEF, 0x76, 0x71, 0xD2, 0x6E, 0x7C, 0x9F, 0x9F,
0xE5, 0x4C, 0xA2, 0x3B, 0x9A, 0xCC, 0x96, 0xEA, 0x92, 0xD8, 0x15, 0x60, 0x85, 0x34, 0xA5, 0x74,
0x6E, 0x8B, 0xBB, 0x0C, 0xA0, 0x96, 0xFC, 0x05, 0x29, 0x17, 0xFC, 0x2F, 0x45, 0x5A, 0x11, 0x5C,
0xA1, 0x30, 0x1E, 0x67, 0x62, 0xF6, 0xF8, 0x2A, 0xA3, 0x98, 0x78, 0x4C, 0x3C, 0xA0, 0xFC, 0xB0,
0x6D, 0x86, 0xBA, 0x04, 0xAC, 0x24, 0x24, 0x81, 0x86, 0x3A, 0xD7, 0x3E, 0xD0, 0xC4, 0x27, 0x9C,
0x58, 0x9D, 0x84, 0x91, 0xC0, 0xEA, 0x2D, 0xB5, 0x5E, 0x0F, 0xA3, 0xEF, 0xF5, 0x0C, 0xC6, 0x30,
0x0F, 0xA8, 0x27, 0x94, 0x92, 0xE1, 0x1E, 0x86, 0xB7, 0x4C, 0x3C, 0x06, 0x3C, 0x5A, 0x28, 0xA9,
0x4B, 0x2A, 0x69, 0xA2, 0x2E, 0xB0, 0x25, 0xD5, 0x83, 0x1C, 0x4B, 0xC9, 0x95, 0x50, 0xF5, 0x61,
0x24, 0x44, 0x14, 0x4A, 0x93, 0x5B, 0x08, 0xAC, 0x49, 0xAB, 0x79, 0xF1, 0xE8, 0x46, 0xD6, 0x6B,
0xBF, 0x44, 0xBE, 0x0D, 0x7A, 0x15, 0xCC, 0x23, 0x41, 0x9D, 0x04, 0x6C, 0xCC, 0x9D, 0x90, 0xF9,
0x7E, 0x40, 0x4B, 0x56, 0xEB, 0x64, 0x49, 0x60, 0xF8, 0x44, 0x10, 0x87, 0x85, 0x64, 0x4C, 0x1B,
0x31, 0x1F, 0x03, 0x34, 0xA5, 0xBB, 0x3B, 0x16, 0xFB, 0xD0, 0xBD, 0xB8, 0x9A, 0x36, 0xDF, 0xBD,
0x1E, 0x47, 0x1D, 0xF8, 0xE7, 0xBC, 0x37, 0x98, 0x1C, 0x0F, 0xC6, 0x30, 0xEA, 0xE2, 0xB4, 0xF3,
0xFE, 0xB0, 0xF3, 0x1E, 0x7E, 0x0E, 0x5B, 0xB5, 0xAF, 0xA3, 0x6F, 0xB8, 0xD0, 0x7D, 0xED, 0x77,
0xFB, 0x83, 0xE3, 0x4E, 0xE7, 0x5D, 0xE3, 0xCD, 0xF9, 0xF4, 0xE3, 0xBB, 0x5D, 0x04, 0x77, 0x83,
0xE6, 0xD5, 0x87, 0x49, 0x73, 0xB0, 0xF5, 0x32, 0xF4, 0x4F, 0xFC, 0x89, 0x17, 0x0E, 0x3A, 0xEF,
0x3F, 0x5E, 0xDD, 0x5D, 0x87, 0x83, 0x71, 0xEF, 0x63, 0x6B, 0xF2, 0x79, 0xEB, 0x43, 0xEF, 0xF3,
0xC7, 0x57, 0xB7, 0xF4, 0xD3, 0xC9, 0xDB, 0xCF, 0xFD, 0x29, 0x20, 0x1C, 0x45, 0xBD, 0xC1, 0x55,
0xF7, 0x43, 0x77, 0xFC, 0xB9, 0xEB, 0x1D, 0xDF, 0x0F, 0x83, 0xF3, 0xEE, 0xEB, 0xCE, 0xB0, 0xB3,
0xE5, 0x51, 0x3A, 0xEE, 0x5F, 0x75, 0xB3, 0x37, 0xEF, 0x2E, 0xC6, 0x8C, 0x4D, 0x7A, 0x9F, 0xCF,
0xFB, 0xDE, 0xE1, 0xF3, 0xD3, 0xC1, 0x49, 0x87, 0x4D, 0xCE, 0xDF, 0x5E, 0x35, 0x6F, 0x5F, 0xBF,
0x3B, 0x3C, 0xF2, 0xAE, 0xDF, 0x5E, 0xEF, 0x1E, 0x6D, 0x37, 0x7E, 0xFB, 0xED, 0xCC, 0xBF, 0x60,
0xBC, 0x7F, 0xF7, 0xBD, 0x33, 0x3E, 0x9C, 0xBE, 0x78, 0x48, 0xFB, 0x93, 0x37, 0x77, 0xBC, 0xF1,
0x21, 0xFA, 0xFA, 0xE6, 0xE1, 0x0C, 0xFE, 0xBB, 0xBC, 0xAC, 0x0D, 0x7B, 0xAD, 0x74, 0xF0, 0xFE,
0xCD, 0x87, 0xAD, 0xF4, 0xE5, 0xF3, 0xB8, 0x7B, 0x74, 0x74, 0x17, 0x0E, 0x2F, 0x1B, 0xA1, 0x7F,
0x3B, 0x12, 0x2F, 0xB6, 0x45, 0x7C, 0x3D, 0xCE, 0x3E, 0x7F, 0x7B, 0xFE, 0x76, 0xD2, 0xB8, 0xA0,
0xE4, 0x7A, 0x52, 0x7B, 0xF8, 0xFE, 0xF0, 0x62, 0xD2, 0x3F, 0xB9, 0x3B, 0x0F, 0xC8, 0xFD, 0xF9,
0xB9, 0xF7, 0x3D, 0xAC, 0x05, 0xE4, 0xE5, 0x45, 0x3F, 0x20, 0x49, 0x6B, 0xE0, 0x77, 0x1A, 0xB5,
0xC3, 0xAD, 0xCE, 0x8E, 0x48, 0xAE, 0x0E, 0xF9, 0xD1, 0xF6, 0xD7, 0xDE, 0x8B, 0x6E, 0xB7, 0x15,
0x0D, 0xBF, 0x6D, 0xBD, 0xBE, 0xDD, 0x7D, 0x3D, 0xD8, 0x7D, 0x3F, 0x7C, 0xDF, 0xE9, 0xED, 0x74,
0x07, 0xE4, 0xBA, 0xF7, 0xBE, 0x33, 0xDA, 0x19, 0x4E, 0x26, 0xEF, 0xDE, 0xF5, 0x5F, 0xF9, 0x9D,
0xEF, 0x49, 0xE7, 0x62, 0xDA, 0xB9, 0x3F, 0x1E, 0x74, 0x4E, 0x6A, 0xEF, 0x8E, 0xCF, 0x9A, 0xAD,
0xDE, 0xF5, 0xF6, 0xF8, 0x6C, 0x77, 0xDA, 0x4D, 0x8F, 0x3B, 0xEF, 0xBB, 0xCD, 0xF1, 0xDB, 0x5A,
0x48, 0x3E, 0x47, 0x87, 0xDB, 0xE3, 0x37, 0xBB, 0xEC, 0xF2, 0x9A, 0x74, 0xDE, 0x74, 0xDF, 0xA6,
0xEC, 0x2A, 0x3C, 0x19, 0x34, 0x3B, 0x9D, 0xD3, 0x0B, 0xFA, 0xEA, 0x70, 0x9B, 0xBC, 0xDB, 0xF2,
0x3E, 0x82, 0xFE, 0x07, 0x9F, 0xE8, 0x6F, 0xB5, 0xCE, 0xF4, 0xA2, 0x19, 0x78, 0x2F, 0x69, 0xFF,
0xE4, 0xBA, 0x2F, 0x6F, 0xE7, 0x38, 0x78, 0xD5, 0xBF, 0xED, 0x65, 0xEF, 0xC3, 0xC3, 0x43, 0x53,
0xE3, 0x51, 0x3D, 0xA1, 0x31, 0x25, 0xA2, 0x1C, 0xAE, 0x16, 0xFE, 0x01, 0xB6, 0xB5, 0xB4, 0xC2,
0xDC, 0x4F, 0x05, 0xBD, 0x17, 0x75, 0x9F, 0x7A, 0x51, 0x42, 0xE4, 0x1E, 0x40, 0xA0, 0x09, 0x9A,
0xD8, 0xFC, 0x77, 0x19, 0x3F, 0x35, 0x15, 0x3F, 0x35, 0xC2, 0x7D, 0xCD, 0x28, 0x1C, 0x01, 0x83,
0x87, 0x4F, 0xEF, 0x98, 0x47, 0xEB, 0x31, 0xBB, 0xA7, 0x41, 0x5D, 0x22, 0x3B, 0x4D, 0x73, 0x26,
0xFD, 0xAD, 0xD8, 0x46, 0x38, 0x98, 0x9A, 0xA4, 0x5A, 0x2C, 0xF8, 0x5F, 0x89, 0x47, 0x21, 0xB0,
0x81, 0xCB, 0x84, 0xF8, 0xAB, 0x7C, 0x27, 0x4A, 0xEA, 0xC3, 0x6C, 0x3C, 0x62, 0xF7, 0xE0, 0xD0,
0x23, 0xC6, 0x99, 0xA0, 0x5A, 0x2B, 0x9D, 0xFF, 0x5E, 0x90, 0xB9, 0xA5, 0x0F, 0xA3, 0x84, 0x84,
0x34, 0xD5, 0xFE, 0x22, 0x99, 0xD9, 0x28, 0x89, 0xC2, 0x65, 0x10, 0x99, 0x8B, 0xA8, 0x34, 0x99,
0xCF, 0x9F, 0x65, 0x71, 0x10, 0x11, 0x10, 0x73, 0x4D, 0xE4, 0x50, 0xF1, 0x34, 0x91, 0x6E, 0xB5,
0x88, 0xAB, 0xB9, 0x9B, 0x6D, 0xA1, 0x5B, 0x96, 0xDD, 0x7A, 0x6B, 0x67, 0xE9, 0xBA, 0x75, 0xB9,
0x17, 0xE3, 0xFD, 0x9A, 0x4C, 0x81, 0xF1, 0xA0, 0x14, 0xEE, 0x9E, 0x09, 0x50, 0xE9, 0x13, 0x87,
0xCB, 0x43, 0xF2, 0xC8, 0xB0, 0x60, 0x40, 0x05, 0xEA, 0x96, 0x8C, 0xD4, 0x85, 0x24, 0xB0, 0x6F,
0xFE, 0x8C, 0xCA, 0xBC, 0x67, 0x3D, 0x8B, 0x13, 0xB8, 0x0D, 0x3A, 0xFD, 0x11, 0xCD, 0x42, 0xA6,
0x2A, 0x6D, 0x45, 0x53, 0x65, 0xBC, 0x5C, 0x84, 0x65, 0xDA, 0x93, 0xBC, 0x16, 0xA4, 0x1F, 0x4B,
0x05, 0xE0, 0x05, 0x37, 0xCF, 0x91, 0x9B, 0x1F, 0x6A, 0x75, 0x7B, 0xF7, 0x97, 0x9C, 0x87, 0x9D,
0xE6, 0x2F, 0x73, 0x3B, 0xDF, 0x5B, 0xA4, 0xE4, 0x56, 0x13, 0xFE, 0x29, 0x32, 0xEF, 0x8B, 0x25,
0x0B, 0xC3, 0xE7, 0xF8, 0xA7, 0x60, 0x10, 0xE9, 0x94, 0x80, 0xDB, 0x3B, 0x2F, 0x5F, 0xF8, 0xC3,
0x02, 0x98, 0x0B, 0xF6, 0x24, 0x3C, 0x21, 0x3E, 0xCB, 0x52, 0xE7, 0x79, 0xF3, 0x97, 0x5C, 0x9F,
0x5B, 0x3B, 0x28, 0xFB, 0xE2, 0x2E, 0x71, 0xB2, 0xB4, 0xD8, 0x34, 0x66, 0x5C, 0xDB, 0x4A, 0x35,
0xBC, 0x6F, 0x92, 0x2C, 0x0C, 0xB3, 0x92, 0xED, 0xE7, 0xBF, 0x2F, 0x4D, 0x13, 0xF7, 0xCF, 0x9A,
0xBF, 0xCC, 0x44, 0x02, 0xD9, 0x64, 0x04, 0xB9, 0xC6, 0x49, 0x22, 0x41, 0x04, 0x35, 0x9A, 0xE6,
0x1C, 0x84, 0x5B, 0x03, 0xD8, 0xDE, 0x6D, 0xFA, 0x74, 0x6C, 0xCE, 0xE7, 0x7B, 0x0D, 0x99, 0xD7,
0xA0, 0x6C, 0xF1, 0x12, 0x16, 0x8B, 0xFD, 0x51, 0xC6, 0x3D, 0xE4, 0x41, 0x1B, 0x53, 0x83, 0x9A,
0xB3, 0x84, 0x8A, 0x2C, 0xE1, 0x9A, 0x1F, 0x79, 0x19, 0x1A, 0xBB, 0x3D, 0xA6, 0xE2, 0x58, 0xD9,
0x7D, 0xF7, 0xE1, 0x8D, 0x0F, 0x3B, 0xE6, 0x0B, 0x04, 0x6F, 0x2D, 0x02, 0x38, 0x30, 0x9C, 0x97,
0xE3, 0x54, 0xF6, 0x43, 0x82, 0x01, 0x22, 0xEF, 0xE8, 0x83, 0x41, 0x2D, 0xB1, 0x40, 0xA4, 0x36,
0xAE, 0x1B, 0xC5, 0x2E, 0x80, 0x71, 0x73, 0x76, 0x07, 0x4A, 0x20, 0x2E, 0xFD, 0x22, 0x6E, 0x2C,
0xE6, 0x72, 0xF8, 0x69, 0xE7, 0xBB, 0xC9, 0x1E, 0x3B, 0xA8, 0xB7, 0x1C, 0xB2, 0xCF, 0x0E, 0x5A,
0xE0, 0x5E, 0x65, 0x6E, 0xE4, 0xB9, 0xAF, 0x58, 0x40, 0x07, 0xB9, 0xC3, 0xE1, 0x31, 0x48, 0x6C,
0xB1, 0x85, 0x28, 0xE2, 0x5B, 0xCD, 0xE6, 0x86, 0x4B, 0x0F, 0x48, 0x00, 0x39, 0xCC, 0xD0, 0x8F,
0xAF, 0xAE, 0x2E, 0xAE, 0xBE, 0xE8, 0x35, 0x5A, 0xD3, 0x6F, 0x1C, 0x4D, 0xAF, 0x71, 0xD3, 0x11,
0x76, 0x42, 0x47, 0x09, 0x4D, 0x27, 0x97, 0x44, 0x4C, 0x8C, 0xD4, 0xBE, 0x23, 0x41, 0x56, 0x16,
0x84, 0xA1, 0xDC, 0xC8, 0xA2, 0x70, 0x39, 0x9D, 0x6A, 0xAF, 0x40, 0xCD, 0x47, 0x90, 0xEA, 0xDA,
0xC2, 0x26, 0x71, 0x4C, 0xB9, 0x6F, 0xE8, 0x31, 0x20, 0xEA, 0x16, 0x35, 0xAD, 0x84, 0x7E, 0xCB,
0x68, 0x2A, 0x52, 0x1B, 0x2C, 0xD7, 0xD0, 0x2F, 0x07, 0x7D, 0xDD, 0xD2, 0x1B, 0xE8, 0x47, 0x3A,
0xF0, 0x46, 0xCC, 0x39, 0x52, 0x89, 0x5C, 0xD0, 0xA4, 0x3E, 0xCC, 0xC0, 0xA0, 0xB8, 0x6E, 0xB6,
0x23, 0x9B, 0x71, 0x4E, 0x93, 0x93, 0xFE, 0xD9, 0xA9, 0xAB, 0x5F, 0x29, 0x46, 0xB4, 0x53, 0x28,
0x48, 0x74, 0x4B, 0x5E, 0x51, 0x7E, 0xC8, 0xE1, 0x84, 0x05, 0xBE, 0x11, 0x99, 0x6D, 0x24, 0xE1,
0x49, 0x12, 0xB2, 0x40, 0x01, 0x0A, 0x9E, 0x2D, 0x1E, 0x62, 0xEA, 0xEA, 0x23, 0x50, 0x86, 0x6E,
0x79, 0x76, 0x98, 0x05, 0x82, 0xC5, 0x01, 0x75, 0x37, 0x5A, 0x30, 0xE3, 0x60, 0x41, 0xAE, 0x8E,
0xB9, 0x19, 0x61, 0xCC, 0x77, 0x75, 0x15, 0xA1, 0xF2, 0xB8, 0xB6, 0xEE, 0x14, 0x4F, 0x9D, 0x92,
0x56, 0x4E, 0x49, 0xCB, 0xB8, 0x4A, 0xE0, 0x34, 0x3F, 0x18, 0xC3, 0x3C, 0xCE, 0xD4, 0x51, 0x05,
0xCC, 0xA7, 0x23, 0x02, 0x9C, 0x7C, 0x40, 0x6D, 0xBA, 0x7A, 0x63, 0xDD, 0x41, 0xA9, 0x3A, 0xC8,
0xAF, 0x6A, 0xC4, 0x2F, 0x6B, 0x44, 0xDD, 0xEE, 0x3A, 0x64, 0x5F, 0x21, 0x07, 0x55, 0xE4, 0xA0,
0x8C, 0x7C, 0x28, 0x8D, 0x64, 0x1D, 0x72, 0xA0, 0x90, 0x93, 0x8A, 0x88, 0x89, 0x14, 0x51, 0x85,
0xBD, 0x3A, 0x6A, 0x13, 0x05, 0xD2, 0xAD, 0xA4, 0x22, 0x66, 0x62, 0x83, 0x97, 0x92, 0x61, 0x40,
0x7D, 0x77, 0xA3, 0x09, 0x33, 0x2C, 0xB6, 0xDD, 0xAD, 0xE6, 0x9A, 0x33, 0x12, 0x75, 0x46, 0x56,
0x65, 0x30, 0x2B, 0x33, 0xA8, 0xF5, 0xC8, 0x1D, 0xD5, 0xD6, 0x31, 0x98, 0x99, 0x56, 0x60, 0x47,
0xDC, 0x0B, 0x98, 0x77, 0xEB, 0x2E, 0xBD, 0xC5, 0x9C, 0xB1, 0x85, 0x85, 0x5A, 0x5C, 0x06, 0xBA,
0x01, 0x94, 0x5E, 0x8B, 0xA5, 0x7C, 0x80, 0xFA, 0x9E, 0x5B, 0xD9, 0x5A, 0x02, 0xDC, 0xA6, 0xF7,
0xD4, 0x3B, 0x8C, 0xC2, 0x90, 0xA0, 0xED, 0xA6, 0xC0, 0x41, 0x3E, 0xD1, 0xCD, 0xB9, 0x15, 0xAD,
0xC5, 0x79, 0xC2, 0x45, 0x2C, 0x7F, 0x3D, 0x8B, 0x23, 0x03, 0x5C, 0xCE, 0xF5, 0x6C, 0xD4, 0x61,
0x6A, 0x83, 0x1E, 0xC7, 0x62, 0xF2, 0x13, 0x17, 0x2A, 0x0C, 0x54, 0xA2, 0x7C, 0x69, 0xDE, 0x58,
0x0B, 0x91, 0x56, 0x7C, 0xEA, 0xA2, 0xB7, 0xE2, 0x54, 0xA8, 0xBC, 0x8A, 0x5D, 0x9A, 0x4B, 0x1D,
0x94, 0x61, 0xB9, 0xBD, 0x2F, 0xA0, 0xFA, 0x7C, 0x0E, 0xE7, 0x01, 0xFF, 0x13, 0x68, 0xF9, 0xE8,
0x5F, 0x17, 0x60, 0xC9, 0xA3, 0x34, 0x78, 0x8B, 0xBB, 0x0D, 0xE3, 0xC0, 0xF9, 0x8F, 0x6D, 0x7C,
0xF9, 0x1F, 0xFB, 0xA6, 0x66, 0x9A, 0x07, 0xFF, 0x6A, 0x48, 0x0D, 0x1B, 0xC2, 0xFC, 0xD2, 0xBA,
0xB1, 0x08, 0x80, 0xED, 0x7F, 0x9B, 0xFF, 0xB1, 0x25, 0xB8, 0x02, 0x6B, 0xDF, 0x45, 0x90, 0x49,
0xF0, 0x24, 0x34, 0xB0, 0x68, 0xA4, 0x91, 0xCD, 0x4D, 0x43, 0xB8, 0xA4, 0x72, 0x8D, 0x35, 0x51,
0xD3, 0x6D, 0x88, 0x53, 0x50, 0x5B, 0xAC, 0x04, 0xBF, 0x3E, 0x24, 0x7A, 0x15, 0x5B, 0x17, 0x00,
0xC9, 0x3D, 0xCA, 0x0C, 0x3D, 0x22, 0x97, 0x52, 0xCB, 0x0C, 0x02, 0x42, 0xA7, 0x89, 0xE7, 0x2A,
0xAD, 0x1D, 0x14, 0x30, 0x17, 0xA2, 0xE0, 0xBC, 0x1C, 0x2D, 0x15, 0xEA, 0xAA, 0xFD, 0x17, 0x0A,
0xA3, 0xD6, 0x12, 0x8A, 0x04, 0x31, 0xAD, 0xD8, 0x79, 0xC6, 0x72, 0x75, 0x4C, 0x59, 0xBA, 0x35,
0x59, 0x5D, 0x96, 0xAD, 0x04, 0xAE, 0x2F, 0x8D, 0xFE, 0xD7, 0x3D, 0x16, 0x8E, 0xB5, 0x12, 0x3F,
0xF8, 0x97, 0xFB, 0x2B, 0x46, 0xE4, 0xCD, 0x3F, 0xBC, 0x21, 0x70, 0x05, 0xA6, 0x41, 0x6D, 0x1E,
0x4D, 0x0D, 0xB3, 0xF6, 0xAB, 0xAE, 0x49, 0x8A, 0xAE, 0x1E, 0x92, 0xFB, 0xBC, 0xA7, 0xC4, 0x8C,
0xD7, 0xD6, 0x70, 0x5E, 0xB4, 0x28, 0xF9, 0x82, 0xEC, 0xE6, 0x48, 0x26, 0xA2, 0xB6, 0x56, 0x64,
0x52, 0xD5, 0xCA, 0xE8, 0x5A, 0x63, 0xFF, 0xD7, 0x4A, 0x40, 0xB7, 0x98, 0xBA, 0x4E, 0x15, 0x8C,
0xB3, 0x00, 0x1C, 0x93, 0x3E, 0x1D, 0x69, 0x03, 0x26, 0x03, 0x75, 0x35, 0x46, 0x5A, 0x81, 0xC1,
0xCC, 0x03, 0xC3, 0x2B, 0xFB, 0xF3, 0x1E, 0x16, 0xBF, 0xFB, 0x97, 0xAA, 0xAA, 0x81, 0xD4, 0x8B,
0x33, 0x5D, 0x59, 0x59, 0xD5, 0x4B, 0xE0, 0xD2, 0x08, 0xA0, 0x5B, 0x8B, 0x3C, 0x3A, 0x8C, 0xFC,
0x87, 0x52, 0xF6, 0x4D, 0xBB, 0x0F, 0x87, 0x01, 0x49, 0xD3, 0x73, 0xB8, 0x01, 0x43, 0xF7, 0x42,
0x50, 0xB8, 0xB2, 0xC2, 0xFD, 0xE6, 0xE6, 0x66, 0x15, 0x29, 0xA1, 0x21, 0x14, 0xDB, 0x8A, 0x2B,
0xF0, 0x49, 0xD3, 0xF1, 0x81, 0x30, 0x18, 0xD2, 0x1A, 0xC6, 0xF0, 0x25, 0xE3, 0x47, 0x5C, 0x71,
0xF4, 0xF4, 0x22, 0xA6, 0xFC, 0x33, 0xDC, 0x95, 0x32, 0xCB, 0x1A, 0xAD, 0xA6, 0x68, 0xFA, 0x8F,
0xD8, 0x3E, 0xCA, 0x0D, 0x76, 0xC1, 0x7A, 0xBA, 0x56, 0xA1, 0xFC, 0x9F, 0x61, 0xB9, 0x94, 0x28,
0xD6, 0x70, 0x9C, 0x40, 0x80, 0x5A, 0xC3, 0x31, 0xC4, 0x1A, 0x41, 0x17, 0xFC, 0x26, 0x6B, 0xF9,
0xCD, 0xFE, 0x19, 0x7E, 0x97, 0x76, 0x1E, 0x15, 0x25, 0x91, 0xAA, 0xAF, 0x50, 0x02, 0x9F, 0xDD,
0xE9, 0xA6, 0x15, 0xB9, 0x55, 0x0A, 0x50, 0x1B, 0x46, 0x41, 0xD0, 0x8F, 0xE2, 0x83, 0x27, 0xD6,
0x9D, 0xC5, 0x7A, 0x31, 0xC8, 0xD9, 0x5C, 0x6E, 0xB1, 0xBC, 0xB5, 0x44, 0x4F, 0xA1, 0xEC, 0x5F,
0x4B, 0x15, 0x01, 0x3F, 0x23, 0x8B, 0x7B, 0xAC, 0xD4, 0xA5, 0x36, 0x28, 0x0F, 0x56, 0x3F, 0xD5,
0x3C, 0xCB, 0x5F, 0xCC, 0xAE, 0x6B, 0x51, 0x9B, 0xC0, 0x38, 0x57, 0x92, 0x8B, 0x4A, 0xB2, 0xC8,
0x13, 0x01, 0xA8, 0x58, 0xC7, 0x2E, 0xC4, 0x4D, 0x6B, 0x7A, 0x7C, 0xBF, 0x5C, 0x83, 0xC2, 0xDF,
0xF5, 0xD5, 0x12, 0x33, 0x08, 0xC4, 0xD3, 0x95, 0x4B, 0x29, 0x5F, 0x37, 0x29, 0x8A, 0x0E, 0x62,
0x47, 0xA3, 0x51, 0x4A, 0xC5, 0x47, 0x0C, 0x49, 0x56, 0xB2, 0x98, 0x9F, 0xC8, 0x90, 0x04, 0x8C,
0x45, 0x3C, 0x8C, 0xB2, 0x94, 0x46, 0x99, 0xA8, 0xA4, 0x16, 0x63, 0x21, 0xCC, 0x5E, 0xFA, 0xE7,
0x9F, 0x8B, 0xC9, 0x7E, 0x5A, 0x0B, 0x96, 0xD3, 0xEB, 0x3D, 0xBF, 0x34, 0xD9, 0xF7, 0x6B, 0x89,
0xB9, 0x7A, 0xE9, 0xFF, 0x67, 0x4B, 0x21, 0x65, 0x4B, 0xF1, 0xB0, 0x54, 0x2E, 0x62, 0x62, 0x29,
0xE6, 0xC9, 0x82, 0x91, 0x97, 0x7C, 0x16, 0x0D, 0x1A, 0x2B, 0x25, 0x55, 0x9E, 0x97, 0x7D, 0x95,
0x43, 0x40, 0x59, 0x71, 0xE5, 0x35, 0x11, 0x06, 0x34, 0xE0, 0x63, 0x64, 0xF2, 0x41, 0xEB, 0xA7,
0xD1, 0x94, 0x26, 0x87, 0x24, 0xA5, 0x06, 0x24, 0xCD, 0x65, 0xDC, 0x41, 0xA8, 0xE9, 0x04, 0xEB,
0x76, 0x6D, 0x6E, 0x12, 0x05, 0xCE, 0x33, 0x77, 0xC4, 0xB1, 0x26, 0x03, 0xF9, 0xB2, 0xCA, 0x09,
0xD4, 0xC6, 0xBE, 0x12, 0xA4, 0x3E, 0x52, 0x25, 0xA8, 0x61, 0x5A, 0xD0, 0x76, 0xC0, 0x35, 0x5F,
0x26, 0x51, 0x4C, 0xC6, 0xB2, 0x07, 0x83, 0x35, 0x74, 0x0F, 0xA4, 0x66, 0x6D, 0x34, 0x91, 0x60,
0xA9, 0x73, 0x29, 0xFC, 0x66, 0xD9, 0xC2, 0x70, 0x4B, 0x57, 0xC9, 0xB0, 0xBD, 0xF4, 0xA5, 0x35,
0x59, 0x83, 0xE0, 0x0B, 0x6C, 0x62, 0xE0, 0x1E, 0x68, 0x64, 0xF2, 0x7B, 0x00, 0x77, 0x6B, 0xB6,
0xA3, 0x3D, 0xD6, 0x8E, 0x6A, 0x35, 0x53, 0x55, 0xE9, 0xAE, 0x0B, 0x6D, 0x4E, 0x74, 0x23, 0x0B,
0x4B, 0x10, 0xAA, 0x9A, 0x59, 0x0C, 0x38, 0x1B, 0x81, 0xAA, 0xBA, 0xC0, 0x11, 0xD6, 0x98, 0x66,
0xA9, 0x23, 0xF1, 0x97, 0x1D, 0xC9, 0x13, 0xB5, 0x07, 0x95, 0xF5, 0x05, 0xD4, 0x31, 0xAB, 0x25,
0x86, 0x30, 0xD3, 0x29, 0x13, 0xDE, 0x04, 0x03, 0x90, 0x07, 0x5A, 0xD5, 0x05, 0x14, 0xB5, 0x8E,
0x1C, 0x4D, 0x44, 0xB8, 0x1C, 0x05, 0xF9, 0xF0, 0x6B, 0x9A, 0x0F, 0xBC, 0xB4, 0x18, 0xDD, 0x97,
0x80, 0x50, 0xD2, 0xE6, 0xE0, 0x88, 0x8F, 0xF2, 0x21, 0xF4, 0xB2, 0x05, 0x9D, 0x02, 0x58, 0xFC,
0xC6, 0x71, 0x3E, 0x8A, 0x27, 0xC5, 0x68, 0x42, 0xEF, 0x17, 0x78, 0x51, 0x01, 0xF5, 0xA9, 0xEE,
0x28, 0x1B, 0xDB, 0x68, 0xCE, 0xF3, 0x41, 0x6B, 0x29, 0x7F, 0xF0, 0xFF, 0x28, 0x7F, 0xCC, 0xC7,
0x85, 0x34, 0x71, 0x31, 0x1A, 0xB3, 0x42, 0x96, 0x61, 0x18, 0xFF, 0x90, 0x93, 0xA4, 0xD4, 0x13,
0x97, 0x7A, 0x5A, 0xF1, 0xB3, 0xB6, 0x53, 0x98, 0x8E, 0x31, 0xAA, 0xF8, 0xE3, 0xC8, 0xF6, 0xF0,
0xF7, 0x3C, 0xF2, 0x65, 0x6D, 0x69, 0x5A, 0xA1, 0x31, 0x82, 0x3A, 0x57, 0x37, 0xCB, 0x7E, 0x9A,
0xFD, 0xB7, 0xAD, 0xE8, 0xD1, 0xF1, 0xE9, 0x71, 0xFF, 0xB8, 0x5C, 0x38, 0x23, 0xE7, 0x25, 0x93,
0x8A, 0x2B, 0x5D, 0xFA, 0xB2, 0x22, 0x80, 0x02, 0x1B, 0x45, 0x01, 0x7B, 0xDD, 0xDC, 0x54, 0x7E,
0xF1, 0xB6, 0x77, 0x71, 0x6E, 0xC7, 0x24, 0x01, 0x8F, 0x24, 0x15, 0xE6, 0xC2, 0x82, 0x44, 0xF9,
0xE0, 0xD7, 0xC7, 0xA5, 0x72, 0x5D, 0x7E, 0x61, 0x70, 0xC4, 0xDC, 0x52, 0xA7, 0xA9, 0x7E, 0x78,
0xE2, 0x62, 0x5D, 0x99, 0xBF, 0x04, 0x41, 0x72, 0x1A, 0x2D, 0x13, 0x55, 0x11, 0x67, 0x46, 0xE5,
0x30, 0x2F, 0xEE, 0xB2, 0x75, 0x0D, 0xD3, 0xC8, 0xB4, 0xC4, 0x84, 0xA5, 0xE5, 0x46, 0xA5, 0x12,
0x14, 0xFE, 0xA2, 0xB6, 0xE7, 0x8B, 0x91, 0x24, 0xB7, 0x5A, 0x73, 0xAB, 0x6F, 0x41, 0x2A, 0x3E,
0x58, 0x04, 0x23, 0x66, 0x39, 0xDB, 0x16, 0x77, 0xA3, 0x43, 0xEE, 0x61, 0x5C, 0x7F, 0xBA, 0x35,
0x78, 0xD2, 0x3C, 0x79, 0x61, 0x9E, 0xFC, 0xB1, 0x7B, 0x2E, 0x1C, 0x45, 0xF9, 0xDA, 0xE2, 0x98,
0xF6, 0x10, 0x58, 0xBB, 0x6D, 0x2F, 0x7D, 0x18, 0x20, 0xD2, 0x83, 0xCB, 0x00, 0xF4, 0x63, 0x58,
0xFF, 0x4A, 0xEE, 0x88, 0x7A, 0x09, 0xAA, 0xA2, 0xAD, 0x73, 0x54, 0xD8, 0xEE, 0xFD, 0x81, 0xA3,
0xF2, 0xCE, 0x65, 0x18, 0x48, 0x97, 0xC3, 0x92, 0x37, 0x8B, 0x75, 0xC1, 0x61, 0x19, 0x31, 0x64,
0x6C, 0x00, 0xE3, 0xCD, 0x5D, 0x49, 0x13, 0xD5, 0x1C, 0xB4, 0xF0, 0x1B, 0x08, 0x8A, 0x4F, 0x39,
0xCE, 0x9A, 0x38, 0xAD, 0x62, 0x72, 0xC5, 0x23, 0xC8, 0x4A, 0x67, 0x89, 0xC0, 0x6E, 0x10, 0x0D,
0x0D, 0x7C, 0x64, 0x9A, 0xA1, 0xB6, 0x1D, 0x3E, 0x37, 0xD7, 0xBC, 0xD9, 0x54, 0xFA, 0x4B, 0x62,
0x79, 0xD5, 0xB0, 0x8B, 0x1C, 0x56, 0xCC, 0x75, 0x7D, 0x1F, 0xF4, 0xA3, 0x4E, 0x29, 0xAF, 0x48,
0xA4, 0x53, 0xD1, 0x83, 0xC4, 0x86, 0xA2, 0x41, 0xBE, 0x91, 0x40, 0x44, 0x72, 0x4A, 0x33, 0x5D,
0xC7, 0xCA, 0xD2, 0x0B, 0x28, 0x49, 0x7A, 0xB2, 0x73, 0x95, 0x49, 0x6B, 0x25, 0x06, 0xFE, 0xC8,
0xD7, 0xF0, 0xC7, 0xA1, 0xD0, 0xA3, 0x83, 0x9B, 0x49, 0x2B, 0x83, 0xA4, 0x23, 0x64, 0x83, 0xA9,
0x37, 0xE4, 0xBB, 0xA8, 0x2D, 0x2F, 0xCB, 0xB4, 0x16, 0x50, 0x70, 0x71, 0x83, 0xBB, 0x11, 0x30,
0x52, 0x5A, 0xC4, 0x9E, 0x94, 0xA8, 0xC7, 0x8F, 0x10, 0x1F, 0x53, 0x4A, 0x20, 0x06, 0x20, 0xA6,
0x40, 0xD0, 0xA7, 0x42, 0x8A, 0x54, 0xE6, 0x92, 0x53, 0x2A, 0x20, 0xCA, 0x48, 0xCD, 0xE2, 0xC1,
0x85, 0x78, 0xD4, 0x46, 0xD6, 0x80, 0xFD, 0xDC, 0xBD, 0x73, 0x33, 0xDE, 0x90, 0x68, 0x09, 0x56,
0x36, 0x3D, 0x9A, 0xA6, 0x52, 0x5C, 0x54, 0xC7, 0x19, 0xF8, 0xA8, 0xA1, 0x03, 0x5A, 0x23, 0x84,
0x11, 0x1E, 0x84, 0x8A, 0x01, 0x40, 0x7F, 0x42, 0xC3, 0x1C, 0x22, 0x70, 0x08, 0x20, 0x82, 0xA0,
0x7F, 0x49, 0x0D, 0xF7, 0x64, 0x05, 0xC9, 0xF8, 0xD8, 0x6D, 0x35, 0xF0, 0x9D, 0x66, 0x95, 0xEC,
0x20, 0xA5, 0xBD, 0x68, 0x24, 0xFA, 0x64, 0x98, 0x1A, 0x50, 0x00, 0xAC, 0xD9, 0x01, 0xA0, 0x1E,
0x24, 0x5E, 0x63, 0x2B, 0x3F, 0xEF, 0x04, 0x2A, 0xBB, 0x00, 0xAB, 0xBB, 0x8E, 0x87, 0x5F, 0x39,
0x4F, 0x19, 0xA7, 0x39, 0x26, 0x00, 0x7B, 0x93, 0x68, 0x7A, 0x99, 0x30, 0x2E, 0xCE, 0x64, 0x1B,
0x6A, 0x6C, 0xB4, 0xE4, 0xF5, 0xA9, 0x87, 0x15, 0x79, 0x3F, 0xC5, 0x8B, 0xCB, 0x0C, 0xF3, 0xBA,
0x53, 0x79, 0x77, 0xB1, 0x86, 0x70, 0x21, 0x50, 0x66, 0x38, 0xB3, 0x29, 0x74, 0xB0, 0xFA, 0xA1,
0x48, 0x82, 0x7A, 0x4F, 0xB7, 0x42, 0xE2, 0xC1, 0x44, 0xED, 0x81, 0xF9, 0xDC, 0xC2, 0xD8, 0xE1,
0x94, 0x83, 0x5A, 0x0A, 0xB5, 0x02, 0x45, 0xC6, 0x95, 0xCD, 0x98, 0x35, 0x1D, 0x6A, 0x58, 0x88,
0x61, 0xE0, 0xAF, 0xFE, 0x05, 0x0F, 0x1E, 0x1C, 0xC8, 0x55, 0x3F, 0xE1, 0x23, 0xE3, 0x7E, 0xF4,
0x23, 0x3E, 0x3E, 0xAF, 0xF0, 0xF1, 0x79, 0x1D, 0x1F, 0xB4, 0xAA, 0x3C, 0x98, 0x0C, 0x80, 0xEC,
0x19, 0xE1, 0x64, 0x4C, 0x13, 0x58, 0xC0, 0x43, 0x50, 0x25, 0x7F, 0x8B, 0xB3, 0x84, 0xFE, 0x98,
0xB3, 0xDE, 0x84, 0x8D, 0xC4, 0x23, 0xFE, 0x8A, 0xD5, 0xFF, 0x82, 0x4B, 0x3C, 0x70, 0x3D, 0x97,
0x79, 0x6D, 0x5A, 0x49, 0x28, 0x3F, 0x7E, 0x2B, 0x91, 0x7E, 0xE4, 0x42, 0x78, 0xA9, 0x38, 0xC8,
0xDF, 0xB7, 0xF4, 0x00, 0xBC, 0x11, 0xF8, 0x29, 0x35, 0x75, 0xBC, 0x0B, 0xA5, 0xFC, 0x29, 0x30,
0x64, 0xA8, 0xC0, 0x47, 0xDD, 0xD9, 0xDC, 0x12, 0xAE, 0x01, 0x8A, 0xF1, 0xA3, 0x29, 0xB0, 0xEA,
0xC9, 0x02, 0xD7, 0x9E, 0x40, 0x26, 0x04, 0x91, 0xE0, 0x48, 0xC8, 0xA7, 0x8D, 0x2F, 0x07, 0x9B,
0x37, 0x35, 0xC8, 0x43, 0x2E, 0xFC, 0x98, 0x2E, 0x0C, 0x36, 0x6F, 0xFE, 0x6D, 0x36, 0xC6, 0xCC,
0x5A, 0x76, 0xA4, 0x96, 0x4C, 0xF6, 0xF4, 0x0B, 0xBF, 0x71, 0x09, 0x48, 0x5D, 0x49, 0x78, 0x45,
0x34, 0x03, 0x6B, 0x43, 0x61, 0xE1, 0x07, 0xFF, 0x47, 0x09, 0xF8, 0x91, 0x9E, 0x07, 0xCE, 0xBD,
0xE6, 0x3D, 0x5E, 0x2F, 0x3E, 0x85, 0xE9, 0x56, 0xE9, 0xC1, 0x4A, 0xC7, 0xEF, 0x53, 0x3A, 0x76,
0x59, 0xA2, 0x14, 0x4A, 0x14, 0x59, 0x88, 0x1A, 0x6A, 0x50, 0x0E, 0x51, 0x98, 0x89, 0x17, 0xCD,
0x81, 0x02, 0x9B, 0x73, 0x34, 0x5B, 0x3A, 0x02, 0x0F, 0xF4, 0xF5, 0x45, 0xEE, 0xFC, 0x74, 0x76,
0x7A, 0x22, 0x44, 0x7C, 0xA5, 0x62, 0x22, 0xD0, 0xAA, 0x2E, 0x2C, 0x2F, 0xCF, 0x9C, 0x89, 0xE4,
0xA1, 0x28, 0x75, 0x30, 0x31, 0x28, 0x87, 0xFE, 0x74, 0x31, 0xFC, 0x0A, 0x71, 0xD6, 0xD0, 0xCF,
0x52, 0x48, 0x58, 0x5B, 0x36, 0xA2, 0xF7, 0xFB, 0x97, 0xF6, 0xAE, 0xDD, 0x84, 0xBA, 0x00, 0xB4,
0x0A, 0x69, 0x19, 0xEE, 0x7D, 0xFE, 0xB7, 0x90, 0xB7, 0xFF, 0x1E, 0x32, 0x83, 0xA8, 0x95, 0x42,
0x58, 0x2A, 0xF0, 0xAB, 0xB8, 0x93, 0x24, 0x9A, 0x4A, 0xB4, 0xE3, 0x24, 0xC1, 0x4B, 0xE9, 0x43,
0x85, 0xA2, 0x0D, 0x61, 0x31, 0xA5, 0x89, 0xE6, 0x47, 0x34, 0xD5, 0x78, 0x24, 0xB4, 0x34, 0x8B,
0x63, 0x68, 0x5C, 0x56, 0xF4, 0x61, 0xEB, 0xC5, 0xEB, 0xCB, 0xFB, 0x8C, 0x66, 0xD4, 0xCF, 0x97,
0x69, 0x52, 0xD1, 0x0B, 0x56, 0x50, 0xDF, 0x10, 0xEE, 0x7E, 0xB9, 0xC9, 0xEB, 0xA9, 0x8C, 0x73,
0x8C, 0xA2, 0x1B, 0x2D, 0x35, 0x07, 0xE9, 0x26, 0x40, 0xD5, 0xE5, 0x59, 0x10, 0xCC, 0xDB, 0x2B,
0xB4, 0xA0, 0xF1, 0x8A, 0x44, 0x24, 0x9F, 0xCB, 0x67, 0x7F, 0xE4, 0xC9, 0xA9, 0xE2, 0x82, 0x50,
0xF2, 0x54, 0xA9, 0x36, 0xAD, 0x0D, 0x63, 0x83, 0x6A, 0x8C, 0xA7, 0x82, 0x70, 0x0F, 0xAF, 0x51,
0xE9, 0xC2, 0x2C, 0x6A, 0x29, 0xDC, 0xDE, 0x46, 0x5F, 0xCB, 0x6D, 0xE9, 0x89, 0x7C, 0x2A, 0x25,
0xE3, 0xAE, 0xAE, 0x63, 0x55, 0x45, 0xB1, 0x3E, 0x25, 0x61, 0x5A, 0x26, 0x5B, 0x54, 0x06, 0x26,
0x77, 0x0B, 0x70, 0x9B, 0x06, 0x29, 0x1C, 0xBD, 0x7E, 0x7F, 0xCE, 0x46, 0xD1, 0xCE, 0x11, 0x80,
0x69, 0xC5, 0x3E, 0x93, 0xD7, 0xE0, 0x24, 0xCC, 0x73, 0x07, 0x32, 0xE9, 0x4A, 0x03, 0x0E, 0xA9,
0x98, 0x44, 0xFE, 0x81, 0x7E, 0xA0, 0x3B, 0x3A, 0xFC, 0xBB, 0x09, 0x35, 0x47, 0xCD, 0xA5, 0xD0,
0xA4, 0xFA, 0x74, 0x70, 0xF5, 0x06, 0xC2, 0x53, 0x0C, 0xA5, 0x01, 0x17, 0x50, 0x34, 0xD7, 0x74,
0x7C, 0x7A, 0x7D, 0x0C, 0x29, 0xC8, 0x7F, 0x21, 0x37, 0x66, 0xBB, 0xAA, 0x6C, 0xB8, 0xF3, 0xEA,
0x75, 0x56, 0x2E, 0x03, 0x7A, 0x61, 0x8C, 0x58, 0x0F, 0x29, 0x7E, 0xFB, 0x7B, 0xF4, 0x9E, 0x8D,
0x15, 0xD2, 0x6A, 0x5D, 0x6F, 0xCE, 0x76, 0x90, 0x67, 0x89, 0xD5, 0x43, 0x2C, 0x70, 0x97, 0x1F,
0x29, 0x59, 0x95, 0x35, 0xDC, 0xF6, 0x48, 0x10, 0xE0, 0xC7, 0x5A, 0x03, 0x1B, 0x6A, 0x22, 0xB2,
0xD4, 0x42, 0x22, 0x29, 0x08, 0x90, 0xD2, 0x3E, 0x84, 0x39, 0xD3, 0x92, 0x65, 0x86, 0xB2, 0xA1,
0xBC, 0xFF, 0xC5, 0x9A, 0xA3, 0x64, 0x46, 0xE8, 0xCE, 0xF9, 0x6C, 0x73, 0x53, 0xD8, 0x85, 0x99,
0x18, 0x05, 0x52, 0x8A, 0x01, 0x1C, 0x9A, 0x7D, 0x68, 0x2D, 0x8C, 0xB2, 0x90, 0x58, 0xAB, 0x3D,
0xD2, 0xB6, 0x51, 0x55, 0x03, 0x54, 0x7C, 0x46, 0x01, 0x03, 0xCE, 0xB2, 0x24, 0x80, 0xA8, 0x8B,
0x39, 0xBA, 0xB2, 0x2D, 0xC5, 0xBA, 0xD0, 0x84, 0x0E, 0xEC, 0x67, 0xC8, 0x12, 0x95, 0x97, 0xAD,
0xA2, 0x27, 0x12, 0xC5, 0x77, 0x95, 0x9E, 0xC8, 0x6F, 0xE5, 0x84, 0xAA, 0xC8, 0x77, 0x88, 0x2F,
0x13, 0x5C, 0xD4, 0xD1, 0x13, 0xA0, 0x24, 0x83, 0x52, 0x34, 0x60, 0x2A, 0x2C, 0x37, 0xEE, 0xEB,
0xD3, 0xE9, 0xB4, 0x8E, 0xDF, 0x6A, 0xEB, 0x70, 0x82, 0xB2, 0x02, 0x5F, 0x5F, 0xC7, 0x21, 0x47,
0x15, 0x58, 0xF8, 0x6E, 0xE1, 0xAC, 0xBA, 0xE8, 0x42, 0x7F, 0x2B, 0xDE, 0xD4, 0xAA, 0xD2, 0x59,
0xE1, 0x73, 0x79, 0xDB, 0x7B, 0x3B, 0x2B, 0x20, 0x32, 0xC4, 0xAF, 0xB2, 0x90, 0x69, 0x20, 0x0D,
0x3B, 0xE5, 0x46, 0x56, 0x25, 0x85, 0x65, 0x5C, 0xB0, 0xE3, 0x2C, 0x9D, 0x18, 0x33, 0x60, 0xDD,
0x11, 0x96, 0xD2, 0x95, 0x43, 0x2D, 0x65, 0xB7, 0x0E, 0xB7, 0x0A, 0xFB, 0x70, 0x30, 0x83, 0x94,
0x79, 0xFB, 0xF3, 0x4F, 0x39, 0x5B, 0xDE, 0xF6, 0x92, 0x62, 0x71, 0xE1, 0xF3, 0xFC, 0xA9, 0x35,
0xAF, 0x69, 0xA5, 0xD1, 0xAF, 0xC4, 0x97, 0xBD, 0x46, 0xFE, 0x19, 0x3B, 0xFF, 0x9C, 0xAD, 0x81,
0xB1, 0x43, 0x23, 0x2A, 0xDC, 0x4C, 0x8C, 0xEA, 0x2F, 0x34, 0xE6, 0x63, 0x79, 0x29, 0xBF, 0x2D,
0xA0, 0x54, 0xA9, 0xD3, 0x68, 0x78, 0x3E, 0xFF, 0x9A, 0x42, 0x19, 0x1D, 0x65, 0xFE, 0x28, 0x20,
0x09, 0xC5, 0x82, 0xA3, 0x41, 0xBE, 0x92, 0xFB, 0x46, 0xC0, 0x86, 0x69, 0x03, 0x93, 0x6D, 0xCB,
0xDE, 0xB2, 0x77, 0x71, 0x64, 0x7F, 0x4D, 0xF7, 0x57, 0x4F, 0xD8, 0x5F, 0x34, 0x69, 0x58, 0x0B,
0xE7, 0xB5, 0xAB, 0x8A, 0x4D, 0x6A, 0x83, 0xFB, 0xC4, 0xA7, 0x70, 0x3D, 0x6F, 0xB3, 0xCC, 0xB6,
0x1A, 0xE4, 0x5F, 0x60, 0xD4, 0x31, 0xBA, 0x95, 0x2F, 0x92, 0xF4, 0x81, 0x7B, 0x18, 0x5B, 0x17,
0x54, 0x26, 0x70, 0x49, 0xD5, 0x87, 0x34, 0xB9, 0xD3, 0x9C, 0x2F, 0x39, 0xC3, 0xB7, 0x3C, 0xA8,
0x03, 0xE4, 0x37, 0x9C, 0x72, 0x39, 0xB0, 0xBF, 0x07, 0x5D, 0x33, 0x2A, 0x41, 0x79, 0xB1, 0x26,
0x9B, 0xE6, 0x7C, 0x02, 0x82, 0x01, 0x70, 0xB1, 0xA3, 0x48, 0xCD, 0x2B, 0xCB, 0x98, 0x9B, 0x57,
0x96, 0x54, 0xE2, 0x5F, 0x59, 0xCC, 0xDB, 0x9F, 0xFC, 0xDB, 0x4C, 0xF9, 0x7F, 0x5B, 0x28, 0x36,
0x32, 0xF9, 0xE1, 0x09, 0xF7, 0x56, 0x3F, 0x45, 0xAD, 0x47, 0x51, 0xBB, 0xF7, 0xFF, 0x17, 0x53,
0xE8, 0x9D, 0x36, 0x92, 0x29, 0x00, 0x00};
#define SPIFFS_MAXLENGTH_FILEPATH 32
const char *excludeListFile = "/.exclude.files";
typedef struct ExcludeListS {
char *item;
ExcludeListS *next;
} ExcludeList;
static ExcludeList *excludes = NULL;
static bool matchWild(const char *pattern, const char *testee) {
const char *nxPat = NULL;
const char *nxTst = NULL;
while (*testee) {
if ((*pattern == '?') || (*pattern == *testee)) {
++pattern;
++testee;
continue;
}
if (*pattern == '*') {
nxPat = pattern++;
nxTst = testee;
continue;
}
if (nxPat) {
pattern = nxPat + 1;
testee = ++nxTst;
continue;
}
return false;
}
while (*pattern == '*') {
++pattern;
}
return (*pattern == 0);
}
static bool addExclude(const char *item) {
size_t len = strlen(item);
if (!len) {
return false;
}
ExcludeList *e = (ExcludeList *)malloc(sizeof(ExcludeList));
if (!e) {
return false;
}
e->item = (char *)malloc(len + 1);
if (!e->item) {
free(e);
return false;
}
memcpy(e->item, item, len + 1);
e->next = excludes;
excludes = e;
return true;
}
static void loadExcludeList(fs::FS &_fs, const char *filename) {
static char linebuf[SPIFFS_MAXLENGTH_FILEPATH];
fs::File excludeFile;
if (filename[0] != '/') {
excludeFile = _fs.open("/" + ((String)filename), "r");
} else {
excludeFile = _fs.open(filename, "r");
}
if (!excludeFile) {
// addExclude("/*.js.gz");
return;
}
#ifdef ESP32
if (excludeFile.isDirectory()) {
excludeFile.close();
return;
}
#endif
if (excludeFile.size() > 0) {
uint8_t idx;
bool isOverflowed = false;
while (excludeFile.available()) {
linebuf[0] = '\0';
idx = 0;
int lastChar;
do {
lastChar = excludeFile.read();
if (lastChar != '\r') {
linebuf[idx++] = (char)lastChar;
}
} while ((lastChar >= 0) && (lastChar != '\n') && (idx < SPIFFS_MAXLENGTH_FILEPATH));
if (isOverflowed) {
isOverflowed = (lastChar != '\n');
continue;
}
isOverflowed = (idx >= SPIFFS_MAXLENGTH_FILEPATH);
linebuf[idx - 1] = '\0';
if (!addExclude(linebuf)) {
excludeFile.close();
return;
}
}
}
excludeFile.close();
}
static bool isExcluded(fs::FS &_fs, const char *filename) {
if (excludes == NULL) {
loadExcludeList(_fs, excludeListFile);
}
ExcludeList *e = excludes;
while (e) {
if (matchWild(e->item, filename)) {
return true;
}
e = e->next;
}
return false;
}
// WEB HANDLER IMPLEMENTATION
#ifdef ESP32
SPIFFSEditor::SPIFFSEditor(const fs::FS &fs, const String &username, const String &password)
#else
SPIFFSEditor::SPIFFSEditor(const String &username, const String &password, const fs::FS &fs)
#endif
: _fs(fs), _username(username), _password(password), _authenticated(false), _startTime(0) {
}
@@ -406,24 +19,20 @@ bool SPIFFSEditor::canHandle(AsyncWebServerRequest *request) {
if (!request->_tempFile) {
return false;
}
#ifdef ESP32
if (request->_tempFile.isDirectory()) {
request->_tempFile.close();
return false;
}
#endif
}
if (request->hasParam("download")) {
request->_tempFile = _fs.open("/" + request->arg("download"), "r");
if (!request->_tempFile) {
return false;
}
#ifdef ESP32
if (request->_tempFile.isDirectory()) {
request->_tempFile.close();
return false;
}
#endif
}
request->addInterestingHeader("If-Modified-Since");
return true;
@@ -442,46 +51,22 @@ void SPIFFSEditor::handleRequest(AsyncWebServerRequest *request) {
if (request->method() == HTTP_GET) {
if (request->hasParam("list")) {
const String path = request->getParam("list")->value();
#ifdef ESP32
File dir = _fs.open(path);
#else
Dir dir = _fs.openDir(path);
#endif
String output = "[";
#ifdef ESP32
File file = dir.openNextFile();
while (file) {
#else
while (dir.next()) {
fs::File entry = dir.openFile("r");
#endif
if (isExcluded(_fs, file.name())) {
#ifdef ESP32
file = dir.openNextFile();
#endif
continue;
}
if (output != "[") {
output += ',';
}
output += "{\"type\":\"";
output += "file";
output += "\",\"name\":\"";
output += file.name();
output += "\",\"size\":";
output += file.size();
output += ",\"ver\":";
output += file.getLastWrite();
output += "}";
#ifdef ESP32
if (file.isDirectory()) {
output += "{\"type\":\"dir\",\"name\":\"" + String(file.name()) + "\",\"size\":" + file.size() + "}";
} else {
output += "{\"type\":\"file\",\"name\":\"" + String(file.name()) + "\",\"size\":" + file.size() + "}";
}
file = dir.openNextFile();
#else
file.close();
#endif
}
#ifdef ESP32
dir.close();
#endif
output += "]";
request->send(200, "application/json", output);
} else if (request->hasParam("edit") || request->hasParam("download")) {
@@ -491,8 +76,7 @@ void SPIFFSEditor::handleRequest(AsyncWebServerRequest *request) {
if (request->header("If-Modified-Since").equals(buildTime)) {
request->send(304);
} else {
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", edit_htm_gz, edit_htm_gz_len);
response->addHeader("Content-Encoding", "gzip");
AsyncWebServerResponse *response = request->beginResponse(_fs, "/www/edit.html");
response->addHeader("Last-Modified", buildTime);
request->send(response);
}

View File

@@ -0,0 +1,246 @@
#include "contentmanager.h"
#include <Arduino.h>
#include <ArduinoJson.h>
#include <HTTPClient.h>
#include "newproto.h"
#include <MD5Builder.h>
#include <locale.h>
#include <time.h>
#include "commstructs.h"
#include "makeimage.h"
#include "web.h"
void contentRunner() {
time_t now;
time(&now);
for (int16_t c = 0; c < tagDB.size(); c++) {
tagRecord* taginfo = nullptr;
taginfo = tagDB.at(c);
if (now >= taginfo->nextupdate || taginfo->button) {
uint8_t mac8[8] = {0, 0, 0, 0, 0, 0, 0, 0};
memcpy(mac8 + 2, taginfo->mac, 6);
uint8_t src[8];
*((uint64_t *)src) = swap64(*((uint64_t *)mac8));
drawNew(src, taginfo->button, taginfo);
taginfo->button = false;
}
}
}
void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
time_t now;
time(&now);
char buffer[64];
uint8_t src[8];
*((uint64_t *)src) = swap64(*((uint64_t *)mac));
sprintf(buffer, "%02X%02X%02X%02X%02X%02X\0", src[2], src[3], src[4], src[5], src[6], src[7]);
String dst = (String)buffer;
String filename = "/" + dst + ".bmp";
struct tm time_info;
getLocalTime(&time_info);
time_info.tm_hour = 0;
time_info.tm_min = 0;
time_info.tm_sec = 0;
time_info.tm_mday++;
time_t midnight = mktime(&time_info);
DynamicJsonDocument doc(500);
deserializeJson(doc, taginfo->modeConfigJson);
JsonObject cfgobj = doc.as<JsonObject>();
wsLog("Updating " + dst + " mode " + String(taginfo->contentMode));
taginfo->nextupdate = now + 600;
switch (taginfo->contentMode) {
case Image:
filename = cfgobj["filename"].as<String>();
if (filename && filename !="null" && !cfgobj["#fetched"].as<bool>()) {
if (prepareDataAvail(&filename, DATATYPE_IMGRAW, mac, cfgobj["timetolive"].as<int>())) {
cfgobj["#fetched"] = true;
} else {
wsErr("Error accessing " + filename);
}
taginfo->nextupdate = 3216153600;
}
break;
case Today:
drawDate(filename);
// updateTagImage(filename, mac, (midnight - now) / 60 - 10);
updateTagImage(filename, mac, 60);
taginfo->nextupdate = midnight;
break;
case CountDays:
if (buttonPressed) cfgobj["counter"] = 0;
drawNumber(filename, (int32_t)cfgobj["counter"], (int32_t)cfgobj["thresholdred"]);
updateTagImage(filename, mac, (buttonPressed?0:60));
cfgobj["counter"] = (int32_t)cfgobj["counter"] + 1;
taginfo->nextupdate = midnight;
break;
case CountHours:
if (buttonPressed) cfgobj["counter"] = 0;
drawNumber(filename, (int32_t)cfgobj["counter"], (int32_t)cfgobj["thresholdred"]);
// updateTagImage(&filename, mac, (3600 - now % 3600) / 60);
// taginfo->nextupdate = now + 3600 - (now % 3600);
updateTagImage(filename, mac, (buttonPressed?0:3));
cfgobj["counter"] = (int32_t)cfgobj["counter"] + 1;
taginfo->nextupdate = now + 300;
break;
case Weather:
// https://open-meteo.com/
break;
case Firmware:
filename = cfgobj["filename"].as<String>();
if (filename && filename != "null" && !cfgobj["#fetched"].as<bool>()) {
if (prepareDataAvail(&filename, DATATYPE_UPDATE, mac, cfgobj["timetolive"].as<int>())) {
cfgobj["#fetched"] = true;
} else {
wsErr("Error accessing " + filename);
}
cfgobj["filename"]="";
taginfo->nextupdate = 3216153600;
taginfo->contentMode = Image;
} else {
taginfo->nextupdate = now + 300;
}
break;
case Memo:
break;
case ImageUrl:
if (getImgURL(filename, cfgobj["url"], (time_t)cfgobj["#fetched"])) {
updateTagImage(filename, mac, cfgobj["interval"].as<int>());
cfgobj["#fetched"] = now;
}
taginfo->nextupdate = now + 60 * (cfgobj["interval"].as<int>() < 5 ? 5 : cfgobj["interval"].as<int>()) ;
break;
}
taginfo->modeConfigJson = doc.as<String>();
}
bool updateTagImage(String &filename, uint8_t *dst, uint16_t nextCheckin) {
prepareDataAvail(&filename, DATATYPE_IMGRAW, dst, nextCheckin);
return true;
}
void drawDate(String &filename) {
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite spr = TFT_eSprite(&tft);
time_t now;
time(&now);
struct tm timeinfo;
localtime_r(&now, &timeinfo);
String Dag[] = {"zondag","maandag","dinsdag", "woensdag", "donderdag", "vrijdag", "zaterdag"};
String Maand[] = {"januari", "februari", "maart", "april", "mei", "juni","juli", "augustus", "september", "oktober", "november", "december"};
int weekday_number = timeinfo.tm_wday;
int month_number = timeinfo.tm_mon;
LittleFS.begin();
long w = 296, h = 128; // mag staand of liggend
spr.createSprite(w, h);
if (spr.getPointer() == nullptr) {
wsErr("Failed to create sprite in drawDate");
}
spr.setColorDepth(8);
spr.fillSprite(TFT_WHITE);
spr.setTextDatum(TC_DATUM);
spr.loadFont("fonts/calibrib62", LittleFS);
spr.setTextColor(TFT_RED, TFT_WHITE);
spr.drawString(Dag[timeinfo.tm_wday], w / 2, 10);
spr.loadFont("fonts/calibrib50", LittleFS);
spr.setTextColor(TFT_BLACK, TFT_WHITE);
spr.drawString(String(timeinfo.tm_mday) + " " + Maand[timeinfo.tm_mon], w / 2, 73);
spr.unloadFont();
spr2grays(spr, w, h, filename);
spr.deleteSprite();
}
void drawNumber(String &filename, int32_t count, int32_t thresholdred) {
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite spr = TFT_eSprite(&tft);
LittleFS.begin();
long w = 296, h = 128;
spr.createSprite(w, h);
if (spr.getPointer() == nullptr) {
wsErr("Failed to create sprite in drawNumber");
}
spr.setColorDepth(8);
spr.fillSprite(TFT_WHITE);
spr.setTextDatum(MC_DATUM);
if (count > thresholdred) {
spr.setTextColor(TFT_RED, TFT_WHITE);
} else {
spr.setTextColor(TFT_BLACK, TFT_WHITE);
}
String font = "fonts/numbers1-2";
if (count > 999) font = "fonts/numbers2-2";
if (count > 9999) font = "fonts/numbers3-2";
spr.loadFont(font, LittleFS);
spr.drawString(String(count), w/2, h/2+10);
spr.unloadFont();
spr2grays(spr, w, h, filename);
spr.deleteSprite();
}
bool getImgURL(String &filename, String URL, time_t fetched) {
// https://images.klari.net/kat-bw29.jpg
LittleFS.begin();
Serial.println("get external " + URL);
HTTPClient http;
http.begin(URL);
http.addHeader("If-Modified-Since", formatHttpDate(fetched));
http.setTimeout(5000); //timeout in ms
int httpCode = http.GET();
if (httpCode == 200) {
File f = LittleFS.open(filename, "w");
if (f) {
http.writeToStream(&f);
f.close();
jpg2grays(filename, filename);
}
} else {
if (httpCode!=304) {
wsErr("http " + URL + " " + String(httpCode));
} else {
wsLog("http " + URL + " " + String(httpCode));
}
}
http.end();
return (httpCode == 200);
}
char *formatHttpDate(time_t t) {
static char buf[40];
struct tm *timeinfo;
timeinfo = gmtime(&t);
strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", timeinfo);
return buf;
}

View File

@@ -3,50 +3,44 @@
#include <WiFiManager.h>
#include <time.h>
#include "contentmanager.h"
#include "flasher.h"
#include "makeimage.h"
#include "pendingdata.h"
#include "serial.h"
#include "soc/rtc_wdt.h"
#include "tag_db.h"
#include "web.h"
void freeHeapTask(void* parameter) {
void timeTask(void* parameter) {
while (1) {
//Serial.printf("Free heap=%d\n", ESP.getFreeHeap());
vTaskDelay(30000 / portTICK_PERIOD_MS);
time_t now;
time(&now);
tm tm;
if (!getLocalTime(&tm)) {
Serial.println("Failed to obtain time");
} else {
if (now % 10 == 0) wsSendSysteminfo();
contentRunner();
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void setup() {
Serial.begin(115200);
Serial.print(">\n");
configTzTime("CET-1CEST,M3.5.0,M10.5.0/3", "europe.pool.ntp.org", "time.nist.gov");
// https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
init_web();
loadDB("/current/tagDB.json");
long timezone = 2;
byte daysavetime = 1;
configTime(0, 3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
struct tm tmstruct;
delay(2000);
tmstruct.tm_year = 0;
getLocalTime(&tmstruct, 5000);
Serial.printf("\nNow is : %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, tmstruct.tm_sec);
Serial.println("");
// WiFiManager wm;
xTaskCreate(freeHeapTask, "print free heap", 10000, NULL, 2, NULL);
xTaskCreate(timeTask, "timed tasks", 10000, NULL, 2, NULL);
xTaskCreate(zbsRxTask, "zbsRX Process", 10000, NULL, 2, NULL);
xTaskCreate(garbageCollection, "pending-data cleanup", 5000, NULL, 1, NULL);
xTaskCreate(webSocketSendProcess, "ws", 5000, NULL,configMAX_PRIORITIES-10, NULL);
/*
wm.setWiFiAutoReconnect(true);
wm.setConfigPortalTimeout(180);
bool res = wm.autoConnect("ESP32ZigbeeBase", "password"); // password protected ap
if (!res) {
Serial.println("Failed to connect");
ESP.restart();
}
wm.setWiFiAutoReconnect(true);
*/
}
void loop() {

430
esp32_fw/src/makeimage.cpp Normal file
View File

@@ -0,0 +1,430 @@
#include <Arduino.h>
#include <FS.h>
#include <LittleFS.h>
#include <TFT_eSPI.h>
#include <TJpg_Decoder.h>
#include <makeimage.h>
#include <web.h>
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite spr = TFT_eSprite(&tft);
bool spr_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t *bitmap) {
spr.pushImage(x, y, w, h, bitmap);
return 1;
}
void jpg2grays(String filein, String fileout) {
TJpgDec.setJpgScale(1);
TJpgDec.setCallback(spr_output);
uint16_t w = 0, h = 0;
TJpgDec.getFsJpgSize(&w, &h, filein);
Serial.println("jpeg conversion " + String(w) + "x" + String(h));
spr.createSprite(w, h);
if (spr.getPointer() == nullptr) {
wsErr("Failed to create sprite in jpg2grays");
}
spr.setColorDepth(8);
spr.fillSprite(TFT_WHITE);
TJpgDec.drawFsJpg(0, 0, filein);
spr2grays(spr, w, h, fileout);
spr.deleteSprite();
}
static uint32_t repackPackedVals(uint32_t val, uint32_t pixelsPerPackedUnit, uint32_t packedMultiplyVal) {
uint32_t ret = 0, i;
for (i = 0; i < pixelsPerPackedUnit; i++) {
ret = ret * packedMultiplyVal + val % packedMultiplyVal;
val /= packedMultiplyVal;
}
return ret;
}
void spr2grays(TFT_eSprite &spr, long w, long h, String &fileout) {
// based on bmp2grays function by Dmitry.GR
long t = millis();
LittleFS.begin();
fs::File f_out = LittleFS.open(fileout, "w");
uint32_t c, rowBytesOut, rowBytesIn, outBpp, i, numRows, pixelsPerPackedUnit = 1, packedMultiplyVal = 0x01000000, packedOutBpp = 0;
uint32_t numGrays, extraColor = 0;
struct BitmapFileHeader hdr;
memset(&hdr, 0, sizeof(hdr));
enum EinkClut clutType;
uint8_t clut[256][3];
bool dither = false, rotated = false;
int skipBytes;
clutType = EinkClutTwoBlacksAndRed;
if (w > h) {
hdr.width = h;
hdr.height = w;
rotated = true;
} else {
hdr.width = w;
hdr.height = h;
}
hdr.bpp = 24;
hdr.sig[0] = 'B';
hdr.sig[1] = 'M';
hdr.colorplanes = 1;
switch (clutType) {
case EinkClutTwoBlacks:
numGrays = 2;
outBpp = 1;
break;
case EinkClutTwoBlacksAndRed:
extraColor = 0xff0000;
numGrays = 2;
outBpp = 2;
break;
case EinkClutFourBlacks:
numGrays = 4;
outBpp = 2;
break;
case EinkClutThreeBlacksAndRed:
numGrays = 3;
extraColor = 0xff0000;
outBpp = 2;
break;
}
packedOutBpp = outBpp;
rowBytesIn = (hdr.width * hdr.bpp + 31) / 32 * 4;
rowBytesOut = ((hdr.width + pixelsPerPackedUnit - 1) / pixelsPerPackedUnit) * packedOutBpp;
rowBytesOut = (rowBytesOut + 31) / 32 * 4;
numRows = hdr.height < 0 ? -hdr.height : hdr.height;
hdr.bpp = outBpp;
hdr.numColors = 1 << outBpp;
hdr.numImportantColors = 1 << outBpp;
hdr.dataOfst = sizeof(struct BitmapFileHeader) + 4 * hdr.numColors;
hdr.dataLen = numRows * rowBytesOut;
hdr.fileSz = hdr.dataOfst + hdr.dataLen;
hdr.headerSz = 40;
hdr.compression = 0;
f_out.write((uint8_t *)&hdr, sizeof(hdr));
// emit & record grey clut entries
for (i = 0; i < numGrays; i++) {
uint32_t val = 255 * i / (numGrays - 1);
f_out.write(val);
f_out.write(val);
f_out.write(val);
f_out.write(val);
clut[i][0] = val;
clut[i][1] = val;
clut[i][2] = val;
}
if (extraColor) {
f_out.write((extraColor >> 0) & 0xff); // B
f_out.write((extraColor >> 8) & 0xff); // G
f_out.write((extraColor >> 16) & 0xff); // R
f_out.write(0x00); // A
clut[i][0] = (extraColor >> 0) & 0xff;
clut[i][1] = (extraColor >> 8) & 0xff;
clut[i][2] = (extraColor >> 16) & 0xff;
}
// pad clut to size
for (i = numGrays + (extraColor ? 1 : 0); i < hdr.numColors; i++) {
f_out.write(0x00);
f_out.write(0x00);
f_out.write(0x00);
f_out.write(0x00);
}
while (numRows--) {
uint32_t pixelValsPackedSoFar = 0, numPixelsPackedSoFar = 0, valSoFar = 0, bytesIn = 0, bytesOut = 0, bitsSoFar = 0;
for (c = 0; c < hdr.width; c++, bytesIn += 3) {
int64_t bestDist = 0x7fffffffffffffffll;
uint8_t bestIdx = 0;
int32_t ditherFudge = 0;
uint16_t color565;
if (rotated) {
color565 = spr.readPixel(hdr.height - 1 - numRows, c);
} else {
color565 = spr.readPixel(c, numRows);
}
uint8_t red = ((color565 >> 11) & 0x1F) * 8;
uint8_t green = ((color565 >> 5) & 0x3F) * 4;
uint8_t blue = (color565 & 0x1F) * 8;
if (dither)
ditherFudge = (rand() % 255 - 127) / (int)numGrays;
for (i = 0; i < hdr.numColors; i++) {
int64_t dist = 0;
dist += (blue - clut[i][0] + ditherFudge) * (blue - clut[i][0] + ditherFudge) * 4750ll;
dist += (green - clut[i][1] + ditherFudge) * (green - clut[i][1] + ditherFudge) * 47055ll;
dist += (red - clut[i][2] + ditherFudge) * (red - clut[i][2] + ditherFudge) * 13988ll;
if (dist < bestDist) {
bestDist = dist;
bestIdx = i;
}
}
// pack pixels as needed
pixelValsPackedSoFar = pixelValsPackedSoFar * packedMultiplyVal + bestIdx;
if (++numPixelsPackedSoFar != pixelsPerPackedUnit)
continue;
numPixelsPackedSoFar = 0;
// it is easier to display when low val is first pixel. currently last pixel is low - reverse this
pixelValsPackedSoFar = repackPackedVals(pixelValsPackedSoFar, pixelsPerPackedUnit, packedMultiplyVal);
valSoFar = (valSoFar << packedOutBpp) | pixelValsPackedSoFar;
pixelValsPackedSoFar = 0;
bitsSoFar += packedOutBpp;
if (bitsSoFar >= 8) {
f_out.write(valSoFar >> (bitsSoFar -= 8));
valSoFar &= (1 << bitsSoFar) - 1;
bytesOut++;
}
}
// see if we have unfinished pixel packages to write
if (numPixelsPackedSoFar) {
while (numPixelsPackedSoFar++ != pixelsPerPackedUnit)
pixelValsPackedSoFar *= packedMultiplyVal;
// it is easier to display when low val is first pixel. currently last pixel is low - reverse this
pixelValsPackedSoFar = repackPackedVals(pixelValsPackedSoFar, pixelsPerPackedUnit, packedMultiplyVal);
valSoFar = (valSoFar << packedOutBpp) | pixelValsPackedSoFar;
pixelValsPackedSoFar = 0;
bitsSoFar += packedOutBpp;
if (bitsSoFar >= 8) {
f_out.write(valSoFar >> (bitsSoFar -= 8));
valSoFar &= (1 << bitsSoFar) - 1;
bytesOut++;
}
}
if (bitsSoFar) {
valSoFar <<= 8 - bitsSoFar; // left-align it as is expected
f_out.write(valSoFar);
bytesOut++;
}
while (bytesOut++ < rowBytesOut)
f_out.write(0);
}
f_out.close();
Serial.println(millis() - t);
Serial.println("finished writing BMP");
}
void bmp2grays(String filein, String fileout) {
// based on bmp2grays function by Dmitry.GR
long t = millis();
LittleFS.begin();
fs::File f_in = LittleFS.open(filein, "r");
fs::File f_out = LittleFS.open(fileout, "w");
uint32_t c, rowBytesOut, rowBytesIn, outBpp, i, numRows, pixelsPerPackedUnit = 1, packedMultiplyVal = 0x01000000, packedOutBpp = 0;
uint32_t numGrays, extraColor = 0;
struct BitmapFileHeader hdr;
enum EinkClut clutType;
uint8_t clut[256][3];
bool dither = false;
int skipBytes;
clutType = EinkClutTwoBlacksAndRed;
f_in.read((uint8_t *)&hdr, sizeof(hdr));
if (hdr.sig[0] != 'B' || hdr.sig[1] != 'M' || hdr.headerSz < 40 || hdr.colorplanes != 1 || hdr.bpp != 24 || hdr.compression) {
Serial.println("BITMAP HEADER INVALID, use uncompressed 24 bits RGB");
return;
}
switch (clutType) {
case EinkClutTwoBlacks:
numGrays = 2;
outBpp = 1;
break;
case EinkClutTwoBlacksAndRed:
extraColor = 0xff0000;
numGrays = 2;
outBpp = 2;
break;
case EinkClutFourBlacks:
numGrays = 4;
outBpp = 2;
break;
case EinkClutThreeBlacksAndRed:
numGrays = 3;
extraColor = 0xff0000;
outBpp = 2;
break;
}
packedOutBpp = outBpp;
skipBytes = hdr.dataOfst - sizeof(hdr);
if (skipBytes < 0) {
fprintf(stderr, "file header was too short!\n");
exit(-1);
}
f_in.read(NULL, skipBytes);
rowBytesIn = (hdr.width * hdr.bpp + 31) / 32 * 4;
rowBytesOut = ((hdr.width + pixelsPerPackedUnit - 1) / pixelsPerPackedUnit) * packedOutBpp;
rowBytesOut = (rowBytesOut + 31) / 32 * 4;
numRows = hdr.height < 0 ? -hdr.height : hdr.height;
hdr.bpp = outBpp;
hdr.numColors = 1 << outBpp;
hdr.numImportantColors = 1 << outBpp;
hdr.dataOfst = sizeof(struct BitmapFileHeader) + 4 * hdr.numColors;
hdr.dataLen = numRows * rowBytesOut;
hdr.fileSz = hdr.dataOfst + hdr.dataLen;
hdr.headerSz = 40;
hdr.compression = 0;
f_out.write((uint8_t *)&hdr, sizeof(hdr));
// emit & record grey clut entries
for (i = 0; i < numGrays; i++) {
uint32_t val = 255 * i / (numGrays - 1);
f_out.write(val);
f_out.write(val);
f_out.write(val);
f_out.write(val);
clut[i][0] = val;
clut[i][1] = val;
clut[i][2] = val;
}
// if there is a color CLUT entry, emit that
if (extraColor) {
f_out.write((extraColor >> 0) & 0xff); // B
f_out.write((extraColor >> 8) & 0xff); // G
f_out.write((extraColor >> 16) & 0xff); // R
f_out.write(0x00); // A
clut[i][0] = (extraColor >> 0) & 0xff;
clut[i][1] = (extraColor >> 8) & 0xff;
clut[i][2] = (extraColor >> 16) & 0xff;
}
// pad clut to size
for (i = numGrays + (extraColor ? 1 : 0); i < hdr.numColors; i++) {
f_out.write(0x00);
f_out.write(0x00);
f_out.write(0x00);
f_out.write(0x00);
}
while (numRows--) {
uint32_t pixelValsPackedSoFar = 0, numPixelsPackedSoFar = 0, valSoFar = 0, bytesIn = 0, bytesOut = 0, bitsSoFar = 0;
for (c = 0; c < hdr.width; c++, bytesIn += 3) {
int64_t bestDist = 0x7fffffffffffffffll;
uint8_t rgb[3], bestIdx = 0;
int32_t ditherFudge = 0;
f_in.read(rgb, sizeof(rgb));
if (dither)
ditherFudge = (rand() % 255 - 127) / (int)numGrays;
for (i = 0; i < hdr.numColors; i++) {
int64_t dist = 0;
dist += (rgb[0] - clut[i][0] + ditherFudge) * (rgb[0] - clut[i][0] + ditherFudge) * 4750ll;
dist += (rgb[1] - clut[i][1] + ditherFudge) * (rgb[1] - clut[i][1] + ditherFudge) * 47055ll;
dist += (rgb[2] - clut[i][2] + ditherFudge) * (rgb[2] - clut[i][2] + ditherFudge) * 13988ll;
if (dist < bestDist) {
bestDist = dist;
bestIdx = i;
}
}
// pack pixels as needed
pixelValsPackedSoFar = pixelValsPackedSoFar * packedMultiplyVal + bestIdx;
if (++numPixelsPackedSoFar != pixelsPerPackedUnit)
continue;
numPixelsPackedSoFar = 0;
// it is easier to display when low val is first pixel. currently last pixel is low - reverse this
pixelValsPackedSoFar = repackPackedVals(pixelValsPackedSoFar, pixelsPerPackedUnit, packedMultiplyVal);
valSoFar = (valSoFar << packedOutBpp) | pixelValsPackedSoFar;
pixelValsPackedSoFar = 0;
bitsSoFar += packedOutBpp;
if (bitsSoFar >= 8) {
f_out.write(valSoFar >> (bitsSoFar -= 8));
valSoFar &= (1 << bitsSoFar) - 1;
bytesOut++;
}
}
// see if we have unfinished pixel packages to write
if (numPixelsPackedSoFar) {
while (numPixelsPackedSoFar++ != pixelsPerPackedUnit)
pixelValsPackedSoFar *= packedMultiplyVal;
// it is easier to display when low val is first pixel. currently last pixel is low - reverse this
pixelValsPackedSoFar = repackPackedVals(pixelValsPackedSoFar, pixelsPerPackedUnit, packedMultiplyVal);
valSoFar = (valSoFar << packedOutBpp) | pixelValsPackedSoFar;
pixelValsPackedSoFar = 0;
bitsSoFar += packedOutBpp;
if (bitsSoFar >= 8) {
f_out.write(valSoFar >> (bitsSoFar -= 8));
valSoFar &= (1 << bitsSoFar) - 1;
bytesOut++;
}
}
if (bitsSoFar) {
valSoFar <<= 8 - bitsSoFar; // left-align it as is expected
f_out.write(valSoFar);
bytesOut++;
}
while (bytesIn++ < rowBytesIn)
f_in.read(NULL, 1);
while (bytesOut++ < rowBytesOut)
f_out.write(0);
}
f_in.close();
f_out.close();
Serial.println(millis() - t);
Serial.println("finished writing BMP2");
}

View File

@@ -1,14 +1,16 @@
#pragma pack(push, 1)
#include "newproto.h"
#include <Arduino.h>
#include <MD5Builder.h>
#include <makeimage.h>
#include <time.h>
#include "LittleFS.h"
#include "commstructs.h"
#include "pendingdata.h"
#include "serial.h"
#include "settings.h"
#include "tag_db.h"
#include "web.h"
extern void sendBlock(const void* data, const uint16_t len);
@@ -29,7 +31,7 @@ bool checkCRC(void* p, uint8_t len) {
return ((uint8_t*)p)[0] == total;
}
uint8_t* getDataForFile(File* file) {
uint8_t* getDataForFile(fs::File* file) {
uint8_t* ret = nullptr;
ret = (uint8_t*)malloc(file->size());
if (ret) {
@@ -48,15 +50,51 @@ void prepareCancelPending(uint64_t ver) {
}
bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t nextCheckin) {
if (nextCheckin > 1440) {
//to prevent very long sleeps of the tag
nextCheckin = 0;
}
*filename = "/" + *filename;
if (!LittleFS.exists(*filename)) return false;
File file = LittleFS.open(*filename);
fs::File file = LittleFS.open(*filename);
if (file.size() == 0) {
Serial.print("opened a file with size 0??\n");
return false;
}
if (filename->endsWith(".bmp") || filename->endsWith(".BMP")) {
struct BitmapFileHeader hdr;
file.read((uint8_t*)&hdr, sizeof(hdr));
if (hdr.width == 296 && hdr.height == 128) {
//sorry, can't rotate
Serial.println("when using BMP files, remember to only use 128px width and 296px height");
wsErr("when using BMP files, remember to only use 128px width and 296px height");
return false;
}
if (hdr.sig[0] == 'B' && hdr.sig[1] == 'M' && hdr.bpp == 24) {
Serial.println("converting 24bpp bmp to grays");
char fileout[64];
sprintf(fileout, "/temp/%02X%02X%02X%02X%02X%02X.bmp\0", dst[5], dst[4], dst[3], dst[2], dst[1], dst[0]);
bmp2grays(*filename,(String)fileout);
*filename = (String)fileout;
file.close();
file = LittleFS.open(*filename);
}
}
if (filename->endsWith(".jpg") || filename->endsWith(".JPG")) {
Serial.println("converting jpg to grays");
char fileout[64];
sprintf(fileout, "/temp/%02X%02X%02X%02X%02X%02X.bmp\0", dst[5], dst[4], dst[3], dst[2], dst[1], dst[0]);
jpg2grays(*filename, (String)fileout);
*filename = (String)fileout;
file.close();
file = LittleFS.open(*filename);
}
uint8_t md5bytes[16];
{
MD5Builder md5;
@@ -66,6 +104,20 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t
md5.getBytes(md5bytes);
}
uint8_t src[8];
*((uint64_t*)src) = swap64(*((uint64_t*)dst));
uint8_t mac[6];
memcpy(mac, src + 2, sizeof(mac));
tagRecord* taginfo = nullptr;
taginfo = tagRecord::findByMAC(mac);
if (taginfo != nullptr) {
if (memcmp(md5bytes, taginfo->md5pending, 16) == 0) {
Serial.println("new image is the same as current image. not updating tag.");
wsSendTaginfo(mac);
return false;
}
}
// the message that will be sent to the AP to tell the tag there is data pending
struct pendingData pending = {0};
memcpy(pending.targetMac, dst, 8);
@@ -84,10 +136,38 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t
pendinginfo->len = pending.availdatainfo.dataSize;
pendinginfo->data = nullptr;
pendinginfo->timeout = PENDING_TIMEOUT;
// pendinginfo->data = getDataForFile(&file);
file.close();
pendinginfo->timeout = 1800;
//pendinginfo->data = getDataForFile(&file);
pendinginfo->timeout = 1800; // ***fixme... a tag can sleep for a long time when ttl is used.
pendingfiles.push_back(pendinginfo);
if (dataType != DATATYPE_UPDATE) {
char dst_path[64];
sprintf(dst_path, "/current/%02X%02X%02X%02X%02X%02X.pending\0", dst[5], dst[4], dst[3], dst[2], dst[1], dst[0]);
fs::File dstfile = LittleFS.open(dst_path, "w");
//int bytes_written = dstfile.write(pendinginfo->data, pendinginfo->len);
file.seek(0);
const int chunkSize = 512;
uint8_t buffer[chunkSize];
size_t bytesRead = 0;
while ((bytesRead = file.read(buffer, chunkSize)) > 0) {
dstfile.write(buffer, bytesRead);
}
dstfile.close();
wsLog("new image pending: " + String(dst_path));
if (taginfo != nullptr) {
taginfo->pending = true;
taginfo->CheckinInMinPending = nextCheckin + 1;
memcpy(taginfo->md5pending, md5bytes, sizeof(md5bytes));
}
}
else {
wsLog("firmware upload pending");
}
file.close();
wsSendTaginfo(mac);
return true;
}
@@ -105,7 +185,7 @@ void processBlockRequest(struct espBlockRequest* br) {
} else {
if (pd->data == nullptr) {
// not cached. open file, cache the data
File file = LittleFS.open(pd->filename);
fs::File file = LittleFS.open(pd->filename);
if (!file) {
Serial.print("Dunno how this happened... File pending but deleted in the meantime?\n");
}
@@ -129,7 +209,7 @@ void processBlockRequest(struct espBlockRequest* br) {
sendBlock(pd->data + (br->blockId * BLOCK_DATA_SIZE), len);
char buffer[64];
sprintf(buffer, "< Block Request received for MD5 %llu, block %d\n\0", br->ver, br->blockId);
wsString((String)buffer);
wsLog((String)buffer);
Serial.printf("<BlockId=%d\n", br->blockId);
}
@@ -138,14 +218,60 @@ void processXferComplete(struct espXferComplete* xfc) {
uint8_t src[8];
*((uint64_t*)src) = swap64(*((uint64_t*)xfc->src));
sprintf(buffer, "< %02X%02X%02X%02X%02X%02X reports xfer complete\n\0", src[2], src[3], src[4], src[5], src[6], src[7]);
wsString((String)buffer);
wsLog((String)buffer);
Serial.print(buffer);
uint8_t mac[6];
memcpy(mac, src + 2, sizeof(mac));
char src_path[64];
char dst_path[64];
char tmp_path[64];
sprintf(src_path, "/current/%02X%02X%02X%02X%02X%02X.pending\0", src[2], src[3], src[4], src[5], src[6], src[7]);
sprintf(dst_path, "/current/%02X%02X%02X%02X%02X%02X.bmp\0", src[2], src[3], src[4], src[5], src[6], src[7]);
sprintf(tmp_path, "/temp/%02X%02X%02X%02X%02X%02X.bmp\0", src[2], src[3], src[4], src[5], src[6], src[7]);
if (LittleFS.exists(dst_path)) {
LittleFS.remove(dst_path);
}
LittleFS.rename(src_path, dst_path);
if (LittleFS.exists(tmp_path)) {
LittleFS.remove(tmp_path);
}
time_t now;
time(&now);
tagRecord* taginfo = nullptr;
taginfo = tagRecord::findByMAC(mac);
if (taginfo != nullptr) {
taginfo->pending = false;
taginfo->expectedNextCheckin = now + 60 * taginfo->CheckinInMinPending + 30;
memcpy(taginfo->md5, taginfo->md5pending, sizeof(taginfo->md5pending));
}
wsSendTaginfo(mac);
}
void processDataReq(struct espAvailDataReq* eadr) {
char buffer[64];
uint8_t src[8];
sprintf(buffer, "<ADR %02X%02X%02X%02X%02X%02X%02X%02X button: %02X\n\0", eadr->src[7], eadr->src[6], eadr->src[5], eadr->src[4], eadr->src[3], eadr->src[2], eadr->src[1], eadr->src[0], eadr->adr.buttonState);
wsString((String)buffer);
*((uint64_t*)src) = swap64(*((uint64_t*)eadr->src));
tagRecord* taginfo = nullptr;
uint8_t mac[6];
memcpy(mac, src + 2, sizeof(mac));
taginfo = tagRecord::findByMAC(mac);
if (taginfo == nullptr) {
taginfo = new tagRecord;
memcpy(taginfo->mac, src + 2, sizeof(taginfo->mac));
taginfo->pending = false;
tagDB.push_back(taginfo);
}
time_t now;
time(&now);
taginfo->lastseen = now;
taginfo->expectedNextCheckin = now + 300;
taginfo->button = (eadr->adr.buttonState == 1);
sprintf(buffer, "<ADR %02X%02X%02X%02X%02X%02X\n\0", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
Serial.print(buffer);
wsSendTaginfo(mac);
}

View File

@@ -1,4 +1,3 @@
#pragma pack(push, 1)
#include <Arduino.h>
#include <HardwareSerial.h>
#include <LittleFS.h>
@@ -139,7 +138,6 @@ void SerialRXLoop() {
}
if (strncmp(cmdbuffer, "BST>", 4) == 0) {
Serial.print(">SYNC BURST\n");
wsString(">SYNC BURST");
RXState = ZBS_RX_WAIT_HEADER;
}
if (strncmp(cmdbuffer, "XFC>", 4) == 0) {

167
esp32_fw/src/tag_db.cpp Normal file
View File

@@ -0,0 +1,167 @@
#include "tag_db.h"
#include <Arduino.h>
#include <ArduinoJson.h>
#include <vector>
#include "LittleFS.h"
std::vector<tagRecord*> tagDB;
tagRecord* tagRecord::findByMAC(uint8_t mac[6]) {
for (int16_t c = 0; c < tagDB.size(); c++) {
tagRecord* tag = nullptr;
tag = tagDB.at(c);
if (memcmp(tag->mac, mac, 6) == 0) {
return tag;
}
}
return nullptr;
}
String tagDBtoJson(uint8_t mac[6], uint8_t startPos) {
DynamicJsonDocument doc(2500);
JsonArray tags = doc.createNestedArray("tags");
for (int16_t c = startPos; c < tagDB.size(); c++) {
tagRecord* taginfo = nullptr;
taginfo = tagDB.at(c);
bool select = false;
if (mac) {
if (memcmp(taginfo->mac, mac, 6) == 0) {
select = true;
}
} else {
select = true;
}
if (select) {
JsonObject tag = tags.createNestedObject();
fillNode(tag, taginfo);
if (mac) {
break;
}
}
if (doc.capacity()-doc.memoryUsage() < doc.memoryUsage()/(c+1) + 100) {
doc["continu"] = c+1;
break;
}
}
return doc.as<String>();
}
void fillNode(JsonObject &tag, tagRecord* &taginfo) {
char buffer[16];
sprintf(buffer, "%02X%02X%02X%02X%02X%02X\0", taginfo->mac[0], taginfo->mac[1], taginfo->mac[2], taginfo->mac[3], taginfo->mac[4], taginfo->mac[5]);
tag["mac"] = (String)buffer;
char hex[7];
sprintf(hex, "%02x%02x%02x\0", taginfo->md5[0], taginfo->md5[1], taginfo->md5[2]);
tag["hash"] = hex;
tag["lastseen"] = taginfo->lastseen;
tag["nextupdate"] = taginfo->nextupdate;
tag["nextcheckin"] = taginfo->expectedNextCheckin;
tag["model"] = taginfo->model;
tag["pending"] = taginfo->pending;
tag["button"] = taginfo->button;
tag["alias"] = taginfo->alias;
tag["contentmode"] = taginfo->contentMode;
tag["modecfgjson"] = taginfo->modeConfigJson;
}
void saveDB(String filename) {
DynamicJsonDocument doc(2500);
long t = millis();
LittleFS.begin();
fs::File file = LittleFS.open(filename, "w");
if (!file) {
Serial.println("saveDB: Failed to open file");
return;
}
file.write('[');
for (int16_t c = 0; c < tagDB.size(); c++) {
doc.clear();
tagRecord* taginfo = nullptr;
taginfo = tagDB.at(c);
JsonObject tag = doc.createNestedObject();
fillNode(tag, taginfo);
if (c > 0) {
file.write(',');
}
serializeJson(doc, file);
}
file.write(']');
file.close();
Serial.println(millis() - t);
Serial.println("finished writing DB");
return;
}
void loadDB(String filename) {
StaticJsonDocument<400> doc;
Serial.println("start reading DB from file");
long t = millis();
LittleFS.begin();
fs::File readfile = LittleFS.open(filename, "r");
if (!readfile) {
Serial.println("loadDB: Failed to open file");
return;
}
time_t now;
time(&now);
bool parsing = true;
if (readfile.find("[")) {
while (parsing) {
DeserializationError err = deserializeJson(doc, readfile);
if (!err) {
JsonObject tag = doc[0];
String dst = tag["mac"].as<String>();
uint8_t mac[12];
if (sscanf(dst.c_str(), "%02X%02X%02X%02X%02X%02X", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) == 6) {
tagRecord* taginfo = nullptr;
taginfo = tagRecord::findByMAC(mac);
if (taginfo == nullptr) {
taginfo = new tagRecord;
memcpy(taginfo->mac, mac, sizeof(taginfo->mac));
tagDB.push_back(taginfo);
}
//taginfo->lastseen = (uint32_t)tag["lastseen"];
taginfo->lastseen = 0;
taginfo->nextupdate = (uint32_t)tag["nextupdate"];
taginfo->expectedNextCheckin = (uint16_t)tag["nextcheckin"];
if (taginfo->expectedNextCheckin < now - 1800) {
taginfo->expectedNextCheckin = now + 1800;
}
taginfo->model = (uint8_t)tag["model"];
taginfo->pending = false;
taginfo->button = false;
taginfo->alias = tag["alias"].as<String>();
taginfo->contentMode = static_cast<contentModes>(tag["contentmode"]);
taginfo->modeConfigJson = tag["modecfgjson"].as<String>();
}
} else {
Serial.print(F("deserializeJson() failed: "));
Serial.println(err.c_str());
parsing = false;
}
parsing = parsing && readfile.find(",");
}
}
readfile.close();
Serial.println(millis() - t);
Serial.println("finished reading file");
return;
}

View File

@@ -2,6 +2,7 @@
#include <Arduino.h>
#include <ArduinoJson.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <ESPmDNS.h>
@@ -14,6 +15,7 @@
#include "commstructs.h"
#include "newproto.h"
#include "settings.h"
#include "tag_db.h"
extern uint8_t data_to_send[];
@@ -53,62 +55,15 @@ void webSocketSendProcess(void *parameter) {
// sendStatus(STATUS_WIFI_ACTIVITY);
DynamicJsonDocument doc(1500);
if (ulNotificationValue & 2) { // WS_SEND_MODE_STATUS) {
/* doc["rxActive"] = status.rxActive;
doc["txActive"] = status.txActive;
doc["freq"] = status.freq;
doc["txMode"] = status.currentmode;
*/
}
/*
JsonArray statusframes = doc.createNestedArray("frames");
for (uint8_t c = 0; c < STATUSFRAMELISTSIZE; c++) {
if (statusframearr[c]) {
JsonObject statusframe = statusframes.createNestedObject();
statusframe["frame"] = statusframearr[c]->frameno;
statusframe["isTX"] = statusframearr[c]->isTX;
statusframe["freq"] = statusframearr[c]->freq;
statusframe["txSkipped"] = statusframearr[c]->txCancelled;
switch (statusframearr[c]->rxtype) {
case flexsynctype::SYNC_FLEX_1600:
statusframe["rxType"] = "FLEX_1600";
break;
case flexsynctype::SYNC_FLEX_3200_2:
statusframe["rxType"] = "FLEX_3200_2";
break;
case flexsynctype::SYNC_FLEX_3200_4:
statusframe["rxType"] = "FLEX_3200_4";
break;
case flexsynctype::SYNC_FLEX_6400:
statusframe["rxType"] = "FLEX_3200_4";
break;
default:
break;
}
switch (statusframearr[c]->txformat) {
case txframe::FORMAT_FLEX:
statusframe["txType"] = "FLEX";
break;
case txframe::FORMAT_POCSAG:
statusframe["txType"] = "POCSAG";
break;
case txframe::FORMAT_IDLE:
statusframe["txType"] = "IDLE";
break;
case txframe::FORMAT_BLOCKED:
statusframe["txType"] = "BLOCKED";
break;
default:
break;
}
}
}
}*/
size_t len = measureJson(doc);
xSemaphoreTake(wsMutex, portMAX_DELAY);
auto buffer = std::make_shared<std::vector<uint8_t>>(len);
serializeJson(doc, buffer->data(), len);
// ws.textAll((char*)buffer->data());
ws.textAll("ohai");
xSemaphoreGive(wsMutex);
}
}
@@ -188,16 +143,59 @@ void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType
}
}
void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final);
void wsString(String text) {
void wsLog(String text) {
DynamicJsonDocument doc(100);
doc["logMsg"] = text;
xSemaphoreTake(wsMutex, portMAX_DELAY);
ws.textAll(text);
ws.textAll(doc.as<String>());
xSemaphoreGive(wsMutex);
}
void wsErr(String text) {
DynamicJsonDocument doc(100);
doc["errMsg"] = text;
xSemaphoreTake(wsMutex, portMAX_DELAY);
ws.textAll(doc.as<String>());
xSemaphoreGive(wsMutex);
}
void wsSendSysteminfo() {
DynamicJsonDocument doc(250);
JsonObject sys = doc.createNestedObject("sys");
time_t now;
time(&now);
sys["currtime"] = now;
sys["heap"] = ESP.getFreeHeap();
sys["recordcount"] = tagDB.size();
sys["dbsize"] = tagDB.size() * sizeof(tagRecord);
sys["littlefsfree"] = LittleFS.totalBytes() - LittleFS.usedBytes();
xSemaphoreTake(wsMutex, portMAX_DELAY);
ws.textAll(doc.as<String>());
xSemaphoreGive(wsMutex);
}
void wsSendTaginfo(uint8_t mac[6]) {
String json = "";
json = tagDBtoJson(mac);
xSemaphoreTake(wsMutex, portMAX_DELAY);
ws.textAll(json);
xSemaphoreGive(wsMutex);
}
void init_web() {
LittleFS.begin(true);
if (!LittleFS.exists("/current")) {
LittleFS.mkdir("/current");
}
if (!LittleFS.exists("/temp")) {
LittleFS.mkdir("/temp");
}
WiFi.mode(WIFI_STA);
WiFiManager wm;
bool res;
@@ -214,87 +212,20 @@ void init_web() {
ws.onEvent(onEvent);
server.addHandler(&ws);
server.on("/heap", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send(200, "text/plain", String(ESP.getFreeHeap()));
});
server.on("/reboot", HTTP_POST, [](AsyncWebServerRequest *request) {
request->send(200, "text/plain", "OK Reboot");
ESP.restart();
});
server.serveStatic("/", LittleFS, "/").setDefaultFile("index.htm");
server.serveStatic("/current", LittleFS, "/current/");
server.serveStatic("/", LittleFS, "/www/").setDefaultFile("index.html");
server.on(
"/imgupload", HTTP_POST, [](AsyncWebServerRequest *request) {
request->send(200);
},
doImageUpload);
server.on("/send_image", HTTP_POST, [](AsyncWebServerRequest *request) {
String filename;
String dst;
uint16_t nextCheckin;
if (request->hasParam("filename", true) && request->hasParam("dst", true)) {
filename = request->getParam("filename", true)->value();
dst = request->getParam("dst", true)->value();
nextCheckin = request->getParam("ttl",true)->value().toInt();
uint8_t mac_addr[12]; // I expected this to return like 8 values, but if I make the array 8 bytes long, things die.
mac_addr[0] = 0x00;
mac_addr[1] = 0x00;
if (sscanf(dst.c_str(), "%02X%02X%02X%02X%02X%02X",
&mac_addr[2],
&mac_addr[3],
&mac_addr[4],
&mac_addr[5],
&mac_addr[6],
&mac_addr[7]) != 6) {
request->send(200, "text/plain", "Something went wrong trying to parse the mac address");
} else {
*((uint64_t *)mac_addr) = swap64(*((uint64_t *)mac_addr));
if (prepareDataAvail(&filename, DATATYPE_IMGRAW, mac_addr, nextCheckin)) {
request->send(200, "text/plain", "Sending to " + dst);
} else {
request->send(200, "text/plain", "Couldn't find filename :(");
}
}
return;
}
request->send(200, "text/plain", "Didn't get the required filename + dst");
return;
});
server.on("/send_fw", HTTP_POST, [](AsyncWebServerRequest *request) {
String filename;
String dst;
if (request->hasParam("filename", true) && request->hasParam("dst", true)) {
filename = request->getParam("filename", true)->value();
dst = request->getParam("dst", true)->value();
uint8_t mac_addr[12]; // I expected this to return like 8 values, but if I make the array 8 bytes long, things die.
mac_addr[0] = 0x00;
mac_addr[1] = 0x00;
if (sscanf(dst.c_str(), "%02X%02X%02X%02X%02X%02X",
&mac_addr[2],
&mac_addr[3],
&mac_addr[4],
&mac_addr[5],
&mac_addr[6],
&mac_addr[7]) != 6) {
request->send(200, "text/plain", "Something went wrong trying to parse the mac address");
} else {
*((uint64_t *)mac_addr) = swap64(*((uint64_t *)mac_addr));
if (prepareDataAvail(&filename, DATATYPE_UPDATE, mac_addr, 0)) {
request->send(200, "text/plain", "Sending FW to " + dst);
} else {
request->send(200, "text/plain", "Couldn't find filename :(");
}
}
return;
}
request->send(200, "text/plain", "Didn't get the required filename + dst");
return;
});
server.on("/req_checkin", HTTP_POST, [](AsyncWebServerRequest *request) {
String filename;
String dst;
@@ -323,60 +254,53 @@ void init_web() {
return;
});
server.on("/get_db", HTTP_GET, [](AsyncWebServerRequest *request) {
String json = "";
if (request->hasParam("mac")) {
String dst = request->getParam("mac")->value();
uint8_t mac[6];
if (sscanf(dst.c_str(), "%02X%02X%02X%02X%02X%02X", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5])==6) {
json = tagDBtoJson(mac);
}
} else {
uint8_t startPos=0;
if (request->hasParam("pos")) {
startPos = atoi(request->getParam("pos")->value().c_str());
}
json = tagDBtoJson(nullptr,startPos);
}
request->send(200, "application/json", json);
});
server.on("/save_cfg", HTTP_POST, [](AsyncWebServerRequest *request) {
if (request->hasParam("mac", true)) {
String dst = request->getParam("mac", true)->value();
uint8_t mac[6];
if (sscanf(dst.c_str(), "%02X%02X%02X%02X%02X%02X", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) == 6) {
tagRecord *taginfo = nullptr;
taginfo = tagRecord::findByMAC(mac);
if (taginfo != nullptr) {
taginfo->alias = request->getParam("alias", true)->value();
taginfo->modeConfigJson = request->getParam("modecfgjson", true)->value();
taginfo->contentMode = (contentModes)atoi(request->getParam("contentmode", true)->value().c_str());
taginfo->model = atoi(request->getParam("model", true)->value().c_str());
taginfo->nextupdate = 0;
wsSendTaginfo(mac);
saveDB("/current/tagDB.json");
request->send(200, "text/plain", "Ok, saved");
} else {
request->send(200, "text/plain", "Error while saving: mac not found");
}
}
}
request->send(200, "text/plain", "Ok, saved");
});
server.onNotFound([](AsyncWebServerRequest *request) {
if (request->url() == "/" || request->url() == "index.htm") {
request->send(200, "text/html", "-");
return;
}
Serial.printf("NOT_FOUND: ");
switch (request->method()) {
case HTTP_GET:
Serial.printf("GET");
break;
case HTTP_POST:
Serial.printf("POST");
break;
case HTTP_DELETE:
Serial.printf("DELETE");
break;
case HTTP_PUT:
Serial.printf("PUT");
break;
case HTTP_PATCH:
Serial.printf("PATCH");
break;
case HTTP_HEAD:
Serial.printf("HEAD");
break;
case HTTP_OPTIONS:
Serial.printf("OPTIONS");
break;
default:
Serial.printf("UNKNOWN");
break;
}
Serial.printf(" http://%s%s\n", request->host().c_str(), request->url().c_str());
if (request->contentLength()) {
Serial.printf("_CONTENT_TYPE: %s\n", request->contentType().c_str());
Serial.printf("_CONTENT_LENGTH: %u\n", request->contentLength());
}
for (int i = 0; i < request->headers(); i++) {
AsyncWebHeader *h = request->getHeader(i);
Serial.printf("_HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str());
}
for (int i = 0; i < request->params(); i++) {
AsyncWebParameter *p = request->getParam(i);
if (p->isFile()) {
Serial.printf("_FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size());
} else if (p->isPost()) {
Serial.printf("_POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
} else {
Serial.printf("_GET[%s]: %s\n", p->name().c_str(), p->value().c_str());
}
}
request->send(404);
});

View File

@@ -5,7 +5,7 @@ BUILD ?= zbs29v033
SOURCES += main.c eeprom.c drawing.c
SOURCES += comms.c
SOURCES += syncedproto.c epd.c userinterface.c
SOURCES += powermgt.c barcode.c
all: #make sure it is the first target

101
tag_fw/barcode.c Normal file
View File

@@ -0,0 +1,101 @@
#include <stdbool.h>
#include "barcode.h"
#include "asmUtil.h"
//code128 generator (c) 2021 Dmitry Grinberg https://dmitry.gr
//non-commercial use only, contact licensing@dmitry.gr for other options
#pragma nogcse
static const uint16_t __code mCode128[] = {
0b00110011011, 0b00110110011, 0b01100110011, 0b00011001001, 0b00110001001, 0b00110010001, 0b00010011001, 0b00100011001,
0b00100110001, 0b00010010011, 0b00100010011, 0b00100100011, 0b00111001101, 0b00111011001, 0b01110011001, 0b00110011101,
0b00110111001, 0b01100111001, 0b01001110011, 0b00111010011, 0b01110010011, 0b00100111011, 0b00101110011, 0b01110110111,
0b00110010111, 0b00110100111, 0b01100100111, 0b00100110111, 0b00101100111, 0b01001100111, 0b00011011011, 0b01100011011,
0b01101100011, 0b00011000101, 0b00011010001, 0b01100010001, 0b00010001101, 0b00010110001, 0b01000110001, 0b00010001011,
0b00010100011, 0b01000100011, 0b00011101101, 0b01110001101, 0b01110110001, 0b00011011101, 0b01100011101, 0b01101110001,
0b01101110111, 0b01110001011, 0b01110100011, 0b00010111011, 0b01000111011, 0b01110111011, 0b00011010111, 0b01100010111,
0b01101000111, 0b00010110111, 0b01000110111, 0b01011000111, 0b01011110111, 0b01000010011, 0b01010001111, 0b00001100101,
0b00110000101, 0b00001101001, 0b01100001001, 0b00110100001, 0b01100100001, 0b00001001101, 0b00100001101, 0b00001011001,
0b01000011001, 0b00101100001, 0b01001100001, 0b01001000011, 0b00001010011, 0b01011101111, 0b00101000011, 0b01011110001,
0b00111100101, 0b00111101001, 0b01111001001, 0b00100111101, 0b00101111001, 0b01001111001, 0b00100101111, 0b00101001111,
0b01001001111, 0b01111011011, 0b01101111011, 0b01101101111, 0b00011110101, 0b01111000101, 0b01111010001, 0b00010111101,
0b01000111101, 0b00010101111, 0b01000101111, 0b01111011101, 0b01110111101, 0b01111010111, 0b01110101111
};
#define CODE128_START_B (0b00001001011)
#define CODE128_STOP (0b1101011100011)
#define CODE128_IDX_START_A (103)
#define CODE128_IDX_START_B (104)
#define CODE128_IDX_START_C (105)
#define CODE128_IDX_CODE_STOP (106)
enum BarCodeState {
BarCodeInit,
BarCodeEmittingChar,
BarCodeEmittingChecksum,
BarCodeEmittingStop,
BarCodeDone,
};
__bit barcodeIsDone(struct BarcodeInfo __xdata *bci)
{
return bci->state == BarCodeDone;
}
__bit barcodeNextBar(struct BarcodeInfo __xdata *bci)
{
uint8_t t;
__bit ret;
if (!bci->barsLeft) switch (bci->state) {
case BarCodeInit:
bci->curBars = CODE128_START_B;
bci->barsLeft = 11;
bci->state = BarCodeEmittingChar;
bci->csum = CODE128_IDX_START_B;
break;
case BarCodeEmittingChar:
t = charsPrvDerefAndIncGenericPtr(&bci->str);
if (t) {
t -= 0x20;
if (t >= 0x60)
t = '?' - 0x20;
bci->csum = mathPrvMod16x8(mathPrvMul8x8(++bci->csumMul, t) + bci->csum, 103);
}
else {
bci->state = BarCodeEmittingChecksum;
t = bci->csum;
}
bci->curBars = mCode128[t];
bci->barsLeft = 11;
break;
case BarCodeEmittingChecksum:
bci->state = BarCodeEmittingStop;
bci->curBars = CODE128_STOP;
bci->barsLeft = 13;
break;
case BarCodeEmittingStop:
bci->state = BarCodeDone;
//fallthrough
case BarCodeDone:
default:
return false;
}
ret = bci->curBars & 1;
bci->curBars >>= 1;
bci->barsLeft--;
return ret;
}

29
tag_fw/barcode.h Normal file
View File

@@ -0,0 +1,29 @@
#ifndef _BARCODE_H_
#define _BARCODE_H_
#include <stdint.h>
//code128 generator (c) 2021 Dmitry Grinberg https://dmitry.gr
//non-commercial use only, contact licensing@dmitry.gr for other options
struct BarcodeInfo { //zero-init this except the string ptr
const char *str;
uint16_t curBars;
uint8_t barsLeft;
uint8_t state;
uint8_t csum;
uint8_t csumMul;
};
#define barcodeWidth(nChars) (11 * (nChars) + 11 /*start */+ 11 /*check sum */ + 13 /* stop */)
#pragma callee_saves barcodeIsDone
__bit barcodeIsDone(struct BarcodeInfo __xdata *bci);
#pragma callee_saves barcodeNextBar
__bit barcodeNextBar(struct BarcodeInfo __xdata *bci);
#endif

210
tag_fw/bitmaps.h Normal file
View File

@@ -0,0 +1,210 @@
#ifndef _BITMAPS_H_
#define _BITMAPS_H_
// images generated by https://lvgl.io/tools/imageconverter, prepended with width, height. "CF_INDEXED_1_BIT"-mode, little-endian
#include <stdint.h>
static const uint8_t __code solum[] = {
128, 26,
0x00, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x1f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x07, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01, 0xfc,
0x00, 0x0f, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x03, 0xfc,
0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x07, 0xfc,
0x00, 0x7f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x0f, 0xfc,
0x00, 0xff, 0x80, 0x00, 0x00, 0x3f, 0xc0, 0x78, 0x03, 0x80, 0x1c, 0x01, 0xff, 0x80, 0x0f, 0xf8,
0x00, 0xff, 0x00, 0x00, 0x01, 0xff, 0xf0, 0x78, 0x07, 0xc0, 0x3e, 0x01, 0xff, 0x80, 0x1f, 0xf8,
0x01, 0xfe, 0x00, 0x00, 0x03, 0xff, 0xf8, 0xf8, 0x07, 0xc0, 0x3e, 0x01, 0xff, 0x80, 0x3e, 0xf8,
0x01, 0xfe, 0x03, 0xfe, 0x0f, 0xe1, 0xfc, 0xf8, 0x0f, 0x80, 0x7c, 0x03, 0xef, 0x80, 0x7d, 0xf8,
0x01, 0xfe, 0x00, 0x7f, 0x0f, 0x80, 0xfc, 0xf8, 0x0f, 0x80, 0x7c, 0x03, 0xef, 0x80, 0x7d, 0xf0,
0x01, 0xff, 0x80, 0x3f, 0x9f, 0x00, 0x7c, 0xf0, 0x0f, 0x80, 0x7c, 0x03, 0xe7, 0xc0, 0xf9, 0xf0,
0x00, 0x7f, 0xf0, 0x3f, 0xbf, 0x00, 0x7d, 0xf0, 0x0f, 0x00, 0x78, 0x03, 0xe7, 0xc1, 0xf1, 0xf0,
0x00, 0x00, 0x00, 0x3f, 0xbe, 0x00, 0x7d, 0xf0, 0x1f, 0x00, 0xf8, 0x07, 0xc7, 0xc3, 0xf3, 0xe0,
0x00, 0x00, 0x00, 0x7f, 0xbc, 0x00, 0x7d, 0xe0, 0x1f, 0x00, 0xf8, 0x07, 0xc7, 0xc3, 0xe3, 0xe0,
0x00, 0x00, 0x01, 0xff, 0x7c, 0x00, 0x7f, 0xe0, 0x1f, 0x00, 0xf0, 0x07, 0xc3, 0xc7, 0xc3, 0xe0,
0x00, 0x00, 0x03, 0xff, 0x7c, 0x00, 0x7f, 0xe0, 0x1e, 0x01, 0xf0, 0x07, 0xc3, 0xef, 0x87, 0xe0,
0x00, 0x00, 0x0f, 0xfc, 0x7c, 0x00, 0xfb, 0xe0, 0x3e, 0x01, 0xf0, 0x0f, 0x83, 0xef, 0x87, 0xc0,
0x00, 0x00, 0x3f, 0xf8, 0x7c, 0x01, 0xfb, 0xc0, 0x3e, 0x01, 0xe0, 0x0f, 0x83, 0xff, 0x07, 0xc0,
0x00, 0x00, 0xff, 0xf0, 0x7c, 0x01, 0xf7, 0xc0, 0x3e, 0x03, 0xe0, 0x0f, 0x83, 0xfe, 0x07, 0xc0,
0x00, 0x07, 0xff, 0xc0, 0x7e, 0x07, 0xe7, 0xc0, 0x3e, 0x07, 0xc0, 0x0f, 0x81, 0xfe, 0x0f, 0xc0,
0x00, 0x3f, 0xff, 0x00, 0x3f, 0x9f, 0xc7, 0xff, 0xbf, 0x9f, 0xc0, 0x1f, 0x01, 0xfc, 0x0f, 0x80,
0x01, 0xff, 0xfe, 0x00, 0x1f, 0xff, 0x87, 0xff, 0x9f, 0xff, 0x80, 0x1f, 0x01, 0xf8, 0x0f, 0x80,
0x1f, 0xff, 0xf0, 0x00, 0x0f, 0xfe, 0x07, 0xff, 0x8f, 0xfe, 0x00, 0x1f, 0x01, 0xf0, 0x1f, 0x80,
};
static const uint8_t __code hacked[] = {
112, 56,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x03, 0xff, 0xc0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x87, 0xff, 0xe0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x87, 0xc3, 0xf0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x87, 0x83, 0xf8,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x81, 0xfb, 0x8f, 0x03, 0xfc,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x03, 0x81, 0xf3, 0x8f, 0x03, 0xfe,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x80, 0x07, 0x81, 0xe7, 0x8f, 0x07, 0xbf,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x80, 0x07, 0x81, 0xc7, 0x8f, 0x07, 0x9f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xc0, 0x07, 0x81, 0xc7, 0x87, 0x8f, 0x8f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x07, 0x01, 0xcf, 0x07, 0x9f, 0x03,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x0f, 0x01, 0xef, 0x07, 0xff, 0x00,
0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0xe1, 0xf8, 0x0f, 0x01, 0xff, 0x03, 0xfe, 0x00,
0x38, 0x07, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xfc, 0x0f, 0x01, 0xfe, 0x03, 0xfc, 0x00,
0x38, 0x07, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x7c, 0x1e, 0x00, 0xff, 0xf0, 0x00, 0x00,
0x3c, 0x07, 0x80, 0x00, 0x00, 0x01, 0xe0, 0x3e, 0x1e, 0x00, 0xff, 0xf0, 0x00, 0x00,
0x3c, 0x07, 0x80, 0x00, 0x00, 0x03, 0xe0, 0x1f, 0x1e, 0x00, 0x3f, 0xf0, 0x00, 0x00,
0x3e, 0x07, 0xc0, 0x00, 0x00, 0x03, 0xc0, 0x1f, 0x9f, 0xe0, 0x1f, 0xc0, 0x00, 0x00,
0x1e, 0x03, 0xc0, 0x00, 0x00, 0x07, 0xc0, 0x0f, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00,
0x1f, 0x03, 0xc0, 0x00, 0x00, 0x07, 0x80, 0x07, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00,
0x0f, 0x03, 0xe0, 0x03, 0xc0, 0x07, 0x80, 0x03, 0xfc, 0xff, 0xc0, 0x00, 0x00, 0x00,
0x0f, 0x01, 0xe0, 0x07, 0xc0, 0x07, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0f, 0x81, 0xfc, 0x07, 0xf8, 0x07, 0x80, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0x81, 0xfc, 0x0f, 0xf8, 0x07, 0x80, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0x80, 0xfc, 0x0f, 0x7c, 0x07, 0xc0, 0x78, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0xc0, 0xfc, 0x0f, 0x7e, 0x03, 0xe0, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0xc3, 0xfc, 0x0e, 0x3e, 0x03, 0xf3, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0xdf, 0xfc, 0x0e, 0x3f, 0x01, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0xff, 0xfe, 0x0e, 0x1f, 0x80, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0xff, 0x9e, 0x0f, 0x1f, 0x80, 0x7f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0xfc, 0x1e, 0x0f, 0x1f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0xf0, 0x1f, 0x0f, 0xbf, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0xf0, 0x0f, 0x07, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xf8, 0x0f, 0x87, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x78, 0x07, 0x83, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x78, 0x07, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x38, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x38, 0x03, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x3c, 0x01, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x3c, 0x01, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x3e, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1e, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const uint8_t __code receive[] = {
56, 56,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xc0,
0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xf0,
0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xf0,
0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xf0,
0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xf0,
0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xc0,
0x00, 0x00, 0x00, 0x1f, 0xff, 0x80, 0x00,
0x00, 0x00, 0x00, 0x7f, 0xf8, 0x00, 0x00,
0x00, 0x00, 0x00, 0xff, 0xe0, 0x00, 0x00,
0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00,
0x00, 0x00, 0x07, 0xfe, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0f, 0xf8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x1f, 0xe0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x3f, 0xc0, 0x00, 0x03, 0xe0,
0x00, 0x00, 0x7f, 0x80, 0x00, 0x7f, 0xf0,
0x00, 0x00, 0xfe, 0x00, 0x03, 0xff, 0xf0,
0x00, 0x01, 0xfc, 0x00, 0x0f, 0xff, 0xf0,
0x00, 0x01, 0xf8, 0x00, 0x3f, 0xff, 0xf0,
0x00, 0x03, 0xf8, 0x00, 0xff, 0xff, 0x80,
0x00, 0x07, 0xf0, 0x01, 0xff, 0xe0, 0x00,
0x00, 0x0f, 0xe0, 0x03, 0xff, 0x00, 0x00,
0x00, 0x0f, 0xc0, 0x0f, 0xf8, 0x00, 0x00,
0x00, 0x1f, 0x80, 0x1f, 0xf0, 0x00, 0x00,
0x00, 0x1f, 0x80, 0x3f, 0xc0, 0x00, 0x00,
0x00, 0x3f, 0x00, 0x3f, 0x80, 0x00, 0x00,
0x00, 0x3f, 0x00, 0x7f, 0x00, 0x00, 0x00,
0x00, 0x7e, 0x00, 0xfe, 0x00, 0x07, 0xe0,
0x00, 0x7e, 0x01, 0xfc, 0x00, 0x1f, 0xf0,
0x00, 0xfc, 0x01, 0xf8, 0x00, 0x7f, 0xf0,
0x00, 0xfc, 0x03, 0xf0, 0x01, 0xff, 0xf0,
0x00, 0xf8, 0x03, 0xf0, 0x03, 0xff, 0xf0,
0x01, 0xf8, 0x07, 0xe0, 0x07, 0xff, 0x00,
0x01, 0xf8, 0x07, 0xe0, 0x0f, 0xf0, 0x00,
0x01, 0xf0, 0x0f, 0xc0, 0x1f, 0xe0, 0x00,
0x01, 0xf0, 0x0f, 0xc0, 0x3f, 0x80, 0x00,
0x03, 0xf0, 0x0f, 0x80, 0x3f, 0x00, 0x00,
0x03, 0xf0, 0x1f, 0x80, 0x7e, 0x00, 0x00,
0x03, 0xe0, 0x1f, 0x80, 0x7e, 0x00, 0x00,
0x03, 0xe0, 0x1f, 0x00, 0xfc, 0x01, 0xe0,
0x03, 0xe0, 0x1f, 0x00, 0xfc, 0x07, 0xf8,
0x03, 0xe0, 0x1f, 0x00, 0xf8, 0x0f, 0xfc,
0x03, 0xe0, 0x3f, 0x00, 0xf8, 0x0f, 0xfc,
0x03, 0xe0, 0x3f, 0x01, 0xf8, 0x1f, 0xfe,
0x03, 0xe0, 0x3f, 0x01, 0xf8, 0x1f, 0xfe,
0x03, 0xe0, 0x3f, 0x01, 0xf8, 0x1f, 0xfe,
0x03, 0xc0, 0x3e, 0x01, 0xf0, 0x1f, 0xfe,
0x01, 0xc0, 0x1e, 0x00, 0xf0, 0x0f, 0xfc,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xfc,
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf8,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xe0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const uint8_t __code failed[] = {
48, 48,
0x00, 0x00, 0x1f, 0xf8, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
0x00, 0x03, 0xff, 0xff, 0xe0, 0x00,
0x00, 0x0f, 0xff, 0xff, 0xf0, 0x00,
0x00, 0x3f, 0xff, 0xff, 0xfc, 0x00,
0x00, 0x7f, 0xf0, 0x0f, 0xfe, 0x00,
0x00, 0xff, 0x80, 0x01, 0xff, 0x00,
0x01, 0xfe, 0x00, 0x00, 0x7f, 0x80,
0x03, 0xf8, 0x00, 0x00, 0x1f, 0xc0,
0x07, 0xf0, 0x00, 0x00, 0x3f, 0xe0,
0x0f, 0xe0, 0x00, 0x00, 0x7f, 0xf0,
0x0f, 0xc0, 0x00, 0x00, 0xff, 0xf0,
0x1f, 0x80, 0x00, 0x01, 0xff, 0xf8,
0x1f, 0x00, 0x00, 0x03, 0xff, 0xf8,
0x3f, 0x00, 0x00, 0x07, 0xfe, 0xfc,
0x3e, 0x00, 0x00, 0x0f, 0xfc, 0x7c,
0x7e, 0x00, 0x00, 0x1f, 0xf8, 0x7e,
0x7c, 0x00, 0x00, 0x3f, 0xf0, 0x3e,
0x7c, 0x00, 0x00, 0x7f, 0xe0, 0x3e,
0xfc, 0x00, 0x00, 0xff, 0xc0, 0x3f,
0xf8, 0x00, 0x01, 0xff, 0x80, 0x1f,
0xf8, 0x00, 0x03, 0xff, 0x00, 0x1f,
0xf8, 0x00, 0x07, 0xfe, 0x00, 0x1f,
0xf8, 0x00, 0x0f, 0xfc, 0x00, 0x1f,
0xf8, 0x00, 0x1f, 0xf8, 0x00, 0x1f,
0xf8, 0x00, 0x3f, 0xf0, 0x00, 0x1f,
0xf8, 0x00, 0x7f, 0xe0, 0x00, 0x1f,
0xf8, 0x00, 0xff, 0xc0, 0x00, 0x1f,
0xfc, 0x01, 0xff, 0x80, 0x00, 0x3f,
0x7c, 0x03, 0xff, 0x00, 0x00, 0x3e,
0x7c, 0x07, 0xfe, 0x00, 0x00, 0x3e,
0x7e, 0x0f, 0xfc, 0x00, 0x00, 0x7e,
0x3e, 0x1f, 0xf8, 0x00, 0x00, 0x7c,
0x3f, 0x3f, 0xf0, 0x00, 0x00, 0xfc,
0x3f, 0x7f, 0xe0, 0x00, 0x00, 0xfc,
0x1f, 0xff, 0xc0, 0x00, 0x01, 0xf8,
0x0f, 0xff, 0x80, 0x00, 0x03, 0xf0,
0x0f, 0xff, 0x00, 0x00, 0x07, 0xf0,
0x07, 0xfe, 0x00, 0x00, 0x0f, 0xe0,
0x03, 0xfc, 0x00, 0x00, 0x1f, 0xc0,
0x01, 0xfe, 0x00, 0x00, 0x7f, 0x80,
0x00, 0xff, 0x80, 0x01, 0xff, 0x00,
0x00, 0x7f, 0xf0, 0x0f, 0xfe, 0x00,
0x00, 0x3f, 0xff, 0xff, 0xfc, 0x00,
0x00, 0x0f, 0xff, 0xff, 0xf0, 0x00,
0x00, 0x03, 0xff, 0xff, 0xe0, 0x00,
0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
0x00, 0x00, 0x1f, 0xf8, 0x00, 0x00,
};
#endif

View File

@@ -4,27 +4,11 @@
#include <stdint.h>
#include "spi.h"
#include "uart.h"
//colors for ui messages
#define UI_MSG_MAGNIFY1 1
#define UI_MSG_MAGNIFY2 1
#define UI_MSG_MAGNIFY3 1
#define UI_MSG_BACK_COLOR 4
#define UI_MSG_FORE_COLOR_1 0
#define UI_MSG_FORE_COLOR_2 5
#define UI_MSG_FORE_COLOR_3 5
#define UI_BARCODE_VERTICAL
#define eepromByte spiByte
#define eepromPrvSelect() do { __asm__("nop\nnop\nnop\n"); P1_1 = 0; __asm__("nop\nnop\nnop\n"); } while(0)
#define eepromPrvDeselect() do { __asm__("nop\nnop\nnop\n"); P1_1 = 1; __asm__("nop\nnop\nnop\n"); } while(0)
//debug uart (enable only when needed, on some boards it inhibits eeprom access)
#define dbgUartOn()
#define dbgUartOff()
#define dbgUartByte uartTx
//eeprom map
#define EEPROM_SETTINGS_AREA_START (0x01000UL)
#define EEPROM_SETTINGS_AREA_LEN (0x03000UL)
@@ -35,19 +19,10 @@
//till end of eeprom really. do not put anything after - it will be erased at pairing time!!!
#define EEPROM_PROGRESS_BYTES (128)
//radio cfg
#define RADIO_FIRST_CHANNEL (11) //2.4-GHz channels start at 11
#define RADIO_NUM_CHANNELS (1)
//hw types
#define HW_TYPE_NORMAL HW_TYPE_154_INCH_ZBS_033
#define HW_TYPE_CYCLING HW_TYPE_154_INCH_ZBS_033_FRAME_MODE
#include "../boardCommon.h"
#endif
#endif

View File

@@ -21,7 +21,7 @@
#define SCREEN_DATA_PASSES 2
#endif
#define SCREEN_LUT_LENGTH 10
#endif

View File

@@ -4,27 +4,11 @@
#include <stdint.h>
#include "spi.h"
#include "uart.h"
//colors for ui messages
#define UI_MSG_MAGNIFY1 1
#define UI_MSG_MAGNIFY2 1
#define UI_MSG_MAGNIFY3 1
#define UI_MSG_BACK_COLOR 4
#define UI_MSG_FORE_COLOR_1 0
#define UI_MSG_FORE_COLOR_2 5
#define UI_MSG_FORE_COLOR_3 5
#define UI_BARCODE_VERTICAL
#define eepromByte spiByte
#define eepromPrvSelect() do { __asm__("nop\nnop\nnop\n"); P1_1 = 0; __asm__("nop\nnop\nnop\n"); } while(0)
#define eepromPrvDeselect() do { __asm__("nop\nnop\nnop\n"); P1_1 = 1; __asm__("nop\nnop\nnop\n"); } while(0)
//debug uart (enable only when needed, on some boards it inhibits eeprom access)
#define dbgUartOn()
#define dbgUartOff()
#define dbgUartByte uartTx
//eeprom map
#define EEPROM_SETTINGS_AREA_START (0x01000UL)
#define EEPROM_SETTINGS_AREA_LEN (0x03000UL)
@@ -35,19 +19,10 @@
//till end of eeprom really. do not put anything after - it will be erased at pairing time!!!
#define EEPROM_PROGRESS_BYTES (128)
//radio cfg
#define RADIO_FIRST_CHANNEL (11) //2.4-GHz channels start at 11
#define RADIO_NUM_CHANNELS (1)
//hw types
#define HW_TYPE_NORMAL HW_TYPE_29_INCH_ZBS_026
#define HW_TYPE_CYCLING HW_TYPE_29_INCH_ZBS_026_FRAME_MODE
#include "../boardCommon.h"
#endif
#endif

View File

@@ -4,12 +4,6 @@
#include <stdbool.h>
#include <stdint.h>
//i hate globals, but for 8051 this makes life a lot easier, sorry :(
extern uint8_t __xdata mScreenVcom;
extern int8_t __xdata mCurTemperature;
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 296
@@ -27,22 +21,7 @@ extern int8_t __xdata mCurTemperature;
#define SCREEN_DATA_PASSES 2
void screenShutdown(void);
void screenTest(void);
__bit screenTxStart(__bit forPartial);
void screenEndPass(void); //at end of each pass
#pragma callee_saves screenByteTx
void screenByteTx(uint8_t byte);
void screenTxEnd(void);
void screenSleep(void);
extern uint8_t __xdata mScreenRow[]; //320 bytes used as temp by many on cc where memory is tight
#endif
#define SCREEN_LUT_LENGTH 7
#endif

View File

@@ -4,27 +4,11 @@
#include <stdint.h>
#include "spi.h"
#include "uart.h"
//colors for ui messages
#define UI_MSG_MAGNIFY1 1
#define UI_MSG_MAGNIFY2 1
#define UI_MSG_MAGNIFY3 1
#define UI_MSG_BACK_COLOR 4
#define UI_MSG_FORE_COLOR_1 0
#define UI_MSG_FORE_COLOR_2 5
#define UI_MSG_FORE_COLOR_3 5
#define UI_BARCODE_VERTICAL
#define eepromByte spiByte
#define eepromPrvSelect() do { __asm__("nop\nnop\nnop\n"); P1_1 = 0; __asm__("nop\nnop\nnop\n"); } while(0)
#define eepromPrvDeselect() do { __asm__("nop\nnop\nnop\n"); P1_1 = 1; __asm__("nop\nnop\nnop\n"); } while(0)
//debug uart (enable only when needed, on some boards it inhibits eeprom access)
#define dbgUartOn()
#define dbgUartOff()
#define dbgUartByte uartTx
//eeprom map
#define EEPROM_SETTINGS_AREA_START (0x01000UL)
#define EEPROM_SETTINGS_AREA_LEN (0x03000UL)
@@ -35,19 +19,10 @@
//till end of eeprom really. do not put anything after - it will be erased at pairing time!!!
#define EEPROM_PROGRESS_BYTES (128)
//radio cfg
#define RADIO_FIRST_CHANNEL (11) //2.4-GHz channels start at 11
#define RADIO_NUM_CHANNELS (1)
//hw types
#define HW_TYPE_NORMAL HW_TYPE_42_INCH_ZBS_026
#define HW_TYPE_CYCLING HW_TYPE_42_INCH_ZBS_026_FRAME_MODE
#include "../boardCommon.h"
#endif
#endif

View File

@@ -4,12 +4,6 @@
#include <stdbool.h>
#include <stdint.h>
//i hate globals, but for 8051 this makes life a lot easier, sorry :(
extern uint8_t __xdata mScreenVcom;
extern int8_t __xdata mCurTemperature;
#define SCREEN_WIDTH 400
#define SCREEN_HEIGHT 300
@@ -27,24 +21,7 @@ extern int8_t __xdata mCurTemperature;
#define SCREEN_DATA_PASSES 2
void screenShutdown(void);
void screenTest(void);
__bit screenTxStart(__bit forPartial);
void screenEndPass(void); //at end of each pass
#pragma callee_saves screenByteTx
void screenByteTx(uint8_t byte);
void screenTxEnd(void);
void screenSleep(void);
extern uint8_t __xdata mScreenRow[]; //320 bytes used as temp by many on cc where memory is tight
#endif
#define SCREEN_LUT_LENGTH 7
#endif

View File

@@ -21,34 +21,8 @@
static uint8_t __xdata mCommsBuf[127];
static uint8_t __xdata mSeq = 0;
static uint8_t __xdata mLastLqi = 0;
static int8_t __xdata mLastRSSI = 0;
struct MacFrameFromMaster {
struct MacFcs fcs;
uint8_t seq;
uint16_t pan;
uint8_t dst[8];
uint16_t from;
};
struct MacFrameNormal {
struct MacFcs fcs;
uint8_t seq;
uint16_t pan;
uint8_t dst[8];
uint8_t src[8];
};
struct MacFrameBcast {
struct MacFcs fcs;
uint8_t seq;
uint16_t dstPan;
uint16_t dstAddr;
uint16_t srcPan;
uint8_t src[8];
};
uint8_t __xdata mLastLqi = 0;
int8_t __xdata mLastRSSI = 0;
uint8_t commsGetLastPacketLQI(void)
{

View File

@@ -3,21 +3,16 @@
#include <stdint.h>
#define COMMS_MAX_RADIO_WAIT_MSEC 200
#define COMMS_IV_SIZE (4) //zeroes except these 4 counter bytes
#define COMMS_MAX_RADIO_WAIT_MSEC 200
#define COMMS_RX_ERR_NO_PACKETS (-1)
#define COMMS_RX_ERR_INVALID_PACKET (-2)
#define COMMS_RX_ERR_MIC_FAIL (-3)
#define COMMS_MAX_PACKET_SZ (127)
#pragma callee_saves commsGetLastPacketLQI
uint8_t commsGetLastPacketLQI(void);
#pragma callee_saves commsGetLastPacketRSSI
int8_t commsGetLastPacketRSSI(void);
extern uint8_t __xdata mLastLqi;
extern int8_t __xdata mLastRSSI;
int8_t commsRxUnencrypted(void __xdata *data);
bool commsTxUnencrypted(const void __xdata *packetP, uint8_t len);

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,10 @@
#include "drawing.h"
#include <stdbool.h>
#include "asmUtil.h"
#include "board.h"
#include "cpu.h"
#include "drawing.h"
#include "eeprom.h"
#include "epd.h"
#include "printf.h"
@@ -318,10 +320,9 @@ static void drawPrvDecodeImageOnce(void) {
}
}
extern uint8_t blockXferBuffer[];
static uint8_t __xdata prev, step = 0;
void ByteDecode(uint8_t byte) {
static uint8_t __xdata prev, step = 0;
prev <<= 2;
prev |= (mColorMap[mPassNo][byte >> 4] << 1) | mColorMap[mPassNo][byte & 0x0f];
if (++step == 4) {
@@ -341,15 +342,19 @@ void drawImageAtAddress(uint32_t addr) {
return;
drawPrvLoadAndMapClut(clutAddr);
//screenTxStart(false);
epdSetup();
mPassNo = 0;
beginFullscreenImage();
beginWriteFramebuffer(EPD_COLOR_BLACK);
prev = 0;
step = 0;
drawPrvDecodeImageOnce();
endWriteFramebuffer();
mPassNo++;
beginFullscreenImage();
beginWriteFramebuffer(EPD_COLOR_RED);
prev = 0;
step = 0;
drawPrvDecodeImageOnce();
endWriteFramebuffer();

View File

@@ -4,6 +4,7 @@
#include <string.h>
#include "asmUtil.h"
#include "barcode.h"
#include "board.h"
#include "cpu.h"
#include "font.h"
@@ -64,16 +65,24 @@
P2_2 = 1; \
} while (0)
static uint8_t __xdata currentLUT = 0x00; // Current selected LUT
static bool __idata epdPr = false; // wheter or not we copy the pr("") output to the EPD
static uint8_t __xdata epdCharSize = 1; // character size, 1 or 2 (doubled)
static bool __xdata directionY = true; // print direction, X or Y (true)
static uint8_t __xdata rbuffer[32]; // used to rotate bits around
static uint16_t __xdata fontCurXpos = 0; // current X value we're working with
static uint16_t __xdata fontCurYpos = 0; // current Y value we're working with
extern void dump(uint8_t* __xdata a, uint16_t __xdata l); // remove me when done
static uint8_t __xdata epdCharSize = 1; // character size, 1 or 2 (doubled)
static bool __xdata directionY = true; // print direction, X or Y (true)
static uint8_t __xdata rbuffer[32]; // used to rotate bits around
static uint16_t __xdata fontCurXpos = 0; // current X value we're working with
static uint16_t __xdata fontCurYpos = 0; // current Y value we're working with
static uint8_t __xdata currentLut = 0;
static bool __xdata isInited = false;
struct waveform __xdata waveform;
uint8_t waveformbuffer[120];
#if (SCREEN_LUT_LENGTH == 10)
struct waveform10* __xdata waveform = (struct waveform10*)waveformbuffer; // holds the LUT/waveform
#endif
#if (SCREEN_LUT_LENGTH == 7)
struct waveform* __xdata waveform = (struct waveform*)waveformbuffer; // holds the LUT/waveform
#endif
#pragma callee_saves epdBusySleep
#pragma callee_saves epdBusyWait
@@ -189,13 +198,12 @@ void epdEnterSleep() {
P2_0 = 1;
timerDelay(50);
shortCommand(CMD_SOFT_RESET2);
epdBusyWait(133300);
epdBusyWait(TIMER_TICKS_PER_MS * 10);
shortCommand1(CMD_ENTER_SLEEP, 0x03);
isInited = false;
}
void epdSetup() {
epdReset();
currentLUT = 0;
shortCommand1(CMD_ANALOG_BLK_CTRL, 0x54);
shortCommand1(CMD_DIGITAL_BLK_CTRL, 0x3B);
shortCommand2(CMD_UNKNOWN_1, 0x04, 0x63);
@@ -219,8 +227,9 @@ void epdSetup() {
shortCommand1(CMD_DISP_UPDATE_CTRL2, 0xB1); // mode 1 (i2C)
// shortCommand1(CMD_DISP_UPDATE_CTRL2, 0xB9); // mode 2?
shortCommand(CMD_ACTIVATION);
epdBusyWait(1333000UL);
epdBusyWait(TIMER_TICKS_PER_SECOND);
isInited = true;
currentLut = EPD_LUT_DEFAULT;
}
static uint8_t epdGetStatus() {
uint8_t sta;
@@ -238,11 +247,11 @@ uint16_t epdGetBattery(void) {
shortCommand1(CMD_DISP_UPDATE_CTRL2, SCREEN_CMD_CLOCK_ON | SCREEN_CMD_ANALOG_ON);
shortCommand(CMD_ACTIVATION);
epdBusyWait(133300);
epdBusyWait(TIMER_TICKS_PER_MS * 100);
for (val = 3; val < 8; val++) {
shortCommand1(CMD_SETUP_VOLT_DETECT, val);
epdBusyWait(133300);
epdBusyWait(TIMER_TICKS_PER_MS * 100);
if (epdGetStatus() & 0x10) { // set if voltage is less than threshold ( == 1.9 + val / 10)
voltage = 1850 + mathPrvMul8x8(val, 100);
break;
@@ -251,30 +260,109 @@ uint16_t epdGetBattery(void) {
shortCommand1(CMD_DISP_UPDATE_CTRL2, 0xB1);
shortCommand(CMD_ACTIVATION);
epdBusyWait(133300);
epdBusyWait(TIMER_TICKS_PER_MS * 100);
if (!isInited)
epdEnterSleep();
return voltage;
}
void loadFixedTempOTPLUT() {
shortCommand1(0x18, 0x48); // external temp sensor
shortCommand2(0x1A, 0x05, 0x00); // < temp register
shortCommand1(CMD_DISP_UPDATE_CTRL2, 0xB1); // mode 1 (i2C)
shortCommand(CMD_ACTIVATION);
epdBusyWait(TIMER_TICKS_PER_SECOND);
}
static void writeLut() {
commandBegin(CMD_WRITE_LUT);
for (uint8_t i = 0; i < (SCREEN_LUT_LENGTH * 10); i++)
epdSend(waveformbuffer[i]);
commandEnd();
}
static void readLut() {
commandReadBegin(0x33);
uint16_t checksum = 0;
uint16_t ident = 0;
uint16_t shortl = 0;
for (uint16_t c = 0; c < ((SCREEN_LUT_LENGTH * 10) + 6); c++) {
waveformbuffer[c] = epdReadByte();
}
commandReadEnd();
}
static void lutGroupDisable(uint8_t group) {
memset(&(waveform->group[group]), 0x00, 5);
}
static void lutGroupSpeedup(uint8_t group, uint8_t speed) {
for (uint8_t i = 0; i < 4; i++) {
waveform->group[group].phaselength[i] = 1 + (waveform->group[group].phaselength[i] / speed);
}
}
static void lutGroupRepeat(uint8_t group, uint8_t repeat) {
waveform->group[group].repeat = repeat;
}
static void lutGroupRepeatReduce(uint8_t group, uint8_t factor) {
waveform->group[group].repeat = waveform->group[group].repeat / factor;
}
void selectLUT(uint8_t lut) {
if (lut == currentLUT) return;
// lut = 1;
if (currentLut == lut) {
return;
}
if (currentLut != EPD_LUT_DEFAULT) {
// load the 'default' LUT for the current temperature in the EPD lut register
shortCommand1(CMD_DISP_UPDATE_CTRL2, 0xB1); // mode 1?
shortCommand(CMD_ACTIVATION);
epdBusyWait(TIMER_TICKS_PER_SECOND);
}
currentLut = lut;
// if we're going to be using the default LUT, we're done here.
if (lut == EPD_LUT_DEFAULT) {
return;
}
// download the current LUT from the waveform buffer
readLut();
switch (lut) {
case 0:
shortCommand1(CMD_DISP_UPDATE_CTRL2, 0xB1); // mode 1?
shortCommand(CMD_ACTIVATION);
epdBusyWait(1333000UL);
case EPD_LUT_NO_REPEATS:
lutGroupDisable(LUTGROUP_NEGATIVE);
lutGroupDisable(LUTGROUP_FASTBLINK);
lutGroupRepeat(LUTGROUP_SLOWBLINK, 0);
lutGroupSpeedup(LUTGROUP_SET, 2);
lutGroupSpeedup(LUTGROUP_IMPROVE_SHARPNESS, 2);
lutGroupRepeatReduce(LUTGROUP_IMPROVE_SHARPNESS, 2);
lutGroupSpeedup(LUTGROUP_IMPROVE_REDS, 2);
lutGroupRepeatReduce(LUTGROUP_IMPROVE_REDS, 2);
break;
case 1:
commandBegin(CMD_WRITE_LUT);
for (uint8_t i = 0; i < 70; i++)
epdSend(lutorig[i]);
commandEnd();
case EPD_LUT_FAST_NO_REDS:
lutGroupDisable(LUTGROUP_NEGATIVE);
lutGroupDisable(LUTGROUP_FASTBLINK);
lutGroupDisable(LUTGROUP_SLOWBLINK);
//lutGroupSpeedup(LUTGROUP_SET, 2);
lutGroupDisable(LUTGROUP_IMPROVE_REDS);
lutGroupDisable(LUTGROUP_IMPROVE_SHARPNESS);
break;
case EPD_LUT_FAST:
lutGroupDisable(LUTGROUP_NEGATIVE);
lutGroupDisable(LUTGROUP_FASTBLINK);
lutGroupDisable(LUTGROUP_SLOWBLINK);
lutGroupRepeat(LUTGROUP_SET, 0);
//lutGroupSpeedup(LUTGROUP_SET, 2);
lutGroupDisable(LUTGROUP_IMPROVE_REDS);
lutGroupDisable(LUTGROUP_IMPROVE_SHARPNESS);
break;
}
currentLUT = lut;
#if (SCREEN_LUT_LENGTH == 10)
lutGroupDisable(LUTGROUP_UNUSED);
lutGroupDisable(LUTGROUP_UNKNOWN);
lutGroupDisable(LUTGROUP_UNUSED3);
lutGroupDisable(LUTGROUP_UNUSED4);
#endif
writeLut();
}
void setWindowX(uint16_t start, uint16_t end) {
@@ -284,8 +372,8 @@ void setWindowY(uint16_t start, uint16_t end) {
commandBegin(CMD_WINDOW_Y_SIZE);
epdSend((start)&0xff);
epdSend((start) >> 8);
epdSend((end)&0xff); // was end-1
epdSend((end) >> 8); // was end-1
epdSend((end - 1) & 0xff);
epdSend((end - 1) >> 8);
commandEnd();
}
void setPosXY(uint16_t x, uint16_t y) {
@@ -316,10 +404,11 @@ void clearScreen() {
setWindowX(0, SCREEN_WIDTH);
setWindowY(0, SCREEN_HEIGHT);
setPosXY(0, 0);
shortCommand1(CMD_DATA_ENTRY_MODE, 3); // was 3
shortCommand1(CMD_WRITE_PATTERN_BW, 0x66);
epdBusyWait(133300UL);
epdBusyWait(TIMER_TICKS_PER_MS * 100);
shortCommand1(CMD_WRITE_PATTERN_RED, 0x66);
epdBusyWait(133300UL);
epdBusyWait(TIMER_TICKS_PER_MS * 100);
}
void draw() {
shortCommand1(0x22, 0xCF);
@@ -332,6 +421,9 @@ void drawNoWait() {
// shortCommand1(0x22, SCREEN_CMD_REFRESH);
shortCommand(0x20);
}
void epdWaitRdy() {
epdBusyWait(TIMER_TICKS_PER_SECOND * 120);
}
void drawLineHorizontal(bool color, uint16_t x1, uint16_t x2, uint16_t y) {
setWindowX(x1, x2);
setWindowY(y, y + 1);
@@ -340,9 +432,8 @@ void drawLineHorizontal(bool color, uint16_t x1, uint16_t x2, uint16_t y) {
} else {
shortCommand1(CMD_WRITE_PATTERN_BW, 0xE6);
}
epdBusyWait(133300UL);
epdBusyWait(TIMER_TICKS_PER_MS * 100);
}
void drawLineVertical(bool color, uint16_t x, uint16_t y1, uint16_t y2) {
setWindowY(y1, y2);
setWindowX(x, x + 8);
@@ -354,7 +445,7 @@ void drawLineVertical(bool color, uint16_t x, uint16_t y1, uint16_t y2) {
commandBegin(CMD_WRITE_FB_BW);
}
uint8_t __xdata c = 0x80;
c>>=(x%8);
c >>= (x % 8);
for (; y1 < y2; y1++) {
epdSend(c);
}
@@ -378,7 +469,43 @@ void beginWriteFramebuffer(bool color) {
void endWriteFramebuffer() {
commandEnd();
}
void loadRawBitmap(uint8_t* bmp, uint16_t x, uint16_t y, bool color) {
uint16_t xsize = bmp[0] / 8;
if (bmp[0] % 8) xsize++;
uint16_t size = xsize * bmp[1];
setWindowX(x, x + (xsize * 8));
setWindowY(y, bmp[1] + y);
setPosXY(x, y);
shortCommand1(CMD_DATA_ENTRY_MODE, 3);
if (color) {
commandBegin(CMD_WRITE_FB_RED);
} else {
commandBegin(CMD_WRITE_FB_BW);
}
bmp += 2;
while (size--) {
epdSend(*(bmp++));
}
commandEnd();
}
void printBarcode(const uint8_t* string, uint16_t x, uint16_t y) {
setWindowY(y, 1);
setWindowX(x, x + 8);
setPosXY(x, y);
shortCommand1(CMD_DATA_ENTRY_MODE, 1);
commandBegin(CMD_WRITE_FB_BW);
struct BarcodeInfo __xdata bci = {
.str = string,
};
while (!barcodeIsDone(&bci)) {
if (barcodeNextBar(&bci)) {
epdSend(0xFF);
} else {
epdSend(0x00);
}
}
commandEnd();
}
// stuff for printing text
static void pushXFontBytesToEPD(uint8_t byte1, uint8_t byte2) {
if (epdCharSize == 1) {
@@ -464,9 +591,6 @@ static void pushYFontBytesToEPD(uint8_t byte1, uint8_t byte2) {
}
}
void writeCharEPD(uint8_t c) {
if (!epdPr) {
return;
}
// Writes a single character to the framebuffer
bool empty = true;
for (uint8_t i = 0; i < 20; i++) {
@@ -508,6 +632,7 @@ void writeCharEPD(uint8_t c) {
pushXFontBytesToEPD(0x00, 0x00);
}
}
// Print text to the EPD. Origin is top-left
void epdPrintBegin(uint16_t x, uint16_t y, bool direction, bool fontsize, bool color) {
directionY = direction;
@@ -526,7 +651,7 @@ void epdPrintBegin(uint16_t x, uint16_t y, bool direction, bool fontsize, bool c
rbuffer[1] = 0;
}
setWindowY(y, 0);
setWindowY(y, 1);
if (epdCharSize == 2) {
setWindowX(x, x + 32 + extra);
setPosXY(x, y);
@@ -550,7 +675,6 @@ void epdPrintBegin(uint16_t x, uint16_t y, bool direction, bool fontsize, bool c
memset(rbuffer, 0, 32);
}
epdPr = true;
if (color) {
commandBegin(CMD_WRITE_FB_RED);
} else {
@@ -564,29 +688,8 @@ void epdPrintEnd() {
}
}
commandEnd();
epdPr = false;
}
void loadFixedTempLUT() {
shortCommand1(0x18, 0x48);
shortCommand2(0x1A, 0x05, 0x00); // < temp register
shortCommand1(CMD_DISP_UPDATE_CTRL2, 0xB1); // mode 1 (i2C)
shortCommand(CMD_ACTIVATION);
epdBusyWait(1333000UL);
}
static void readLut() {
commandReadBegin(0x33);
uint16_t checksum = 0;
uint16_t ident = 0;
uint16_t shortl = 0;
for (uint16_t c = 0; c < 76; c++) {
((uint8_t*)&waveform)[c] = epdReadByte();
}
commandReadEnd();
}
extern void dump(uint8_t* __xdata a, uint16_t __xdata l); // remove me when done
extern uint8_t __xdata blockXferBuffer[];
void readRam() {
@@ -612,4 +715,4 @@ void readRam() {
void lutTest() {
readLut();
dump((uint8_t*)&waveform, 96);
}
}

View File

@@ -11,11 +11,18 @@
#define EPD_SIZE_DOUBLE true
#define EPD_COLOR_RED true
#define EPD_COLOR_BLACK false
#define EPD_LOAD_CUSTOM_LUT true
#define EPD_LOAD_OTP_LUT false
#define EPD_MODE_NORMAL 0x00
#define EPD_MODE_INVERT 0x08
#define EPD_MODE_IGNORE 0x04
#define EPD_LUT_DEFAULT 0
#define EPD_LUT_NO_REPEATS 1
#define EPD_LUT_FAST_NO_REDS 2
#define EPD_LUT_FAST 3
#define epdSelect() \
do { \
P1_7 = 0; \
@@ -38,12 +45,15 @@ void clearWindow(bool color);
void clearScreen();
void draw();
void drawNoWait();
void epdWaitRdy();
void drawLineHorizontal(bool color, uint16_t x1, uint16_t x2, uint16_t y);
void drawLineVertical(bool color, uint16_t x, uint16_t y1, uint16_t y2);
void beginFullscreenImage();
void beginWriteFramebuffer(bool color);
void endWriteFramebuffer();
void loadRawBitmap(uint8_t* bmp, uint16_t x, uint16_t y, bool color);
void printBarcode(const uint8_t* string, uint16_t x, uint16_t y);
void selectLUT(uint8_t lut);
@@ -52,10 +62,12 @@ void ByteDecode(uint8_t byte);
void epdPrintBegin(uint16_t x, uint16_t y, bool direction, bool fontsize, bool red);
void epdPrintEnd();
void beginFullscreenImage();
void beginWriteFramebuffer(bool color);
void lutTest();
// for printf.c
void writeCharEPD(uint8_t c);
#endif

View File

@@ -1,5 +1,21 @@
#define __packed
#include "screen.h"
#define LUTGROUP_NEGATIVE 0
#define LUTGROUP_FASTBLINK 1
#define LUTGROUP_SLOWBLINK 2
#define LUTGROUP_SET 3
#define LUTGROUP_IMPROVE_SHARPNESS 4
#define LUTGROUP_IMPROVE_REDS 5
#define LUTGROUP_UNUSED 6
#if (SCREEN_LUT_LENGTH == 10)
#define LUTGROUP_UNKNOWN 7
#define LUTGROUP_UNUSED3 8
#define LUTGROUP_UNUSED4 9
#endif
struct vgroup {
uint8_t A : 2;
uint8_t B : 2;
@@ -11,143 +27,29 @@ struct lut {
struct vgroup group[7];
} __packed;
struct lut10 {
struct vgroup group[10];
} __packed;
struct group {
uint8_t phaselength[4];
uint8_t repeat;
} __packed;
struct waveform {
struct lut elut[5];
struct group egroup[7];
struct lut lut[5];
struct group group[7];
uint8_t gatelevel;
uint8_t sourcelevel[3];
uint8_t dummyline;
uint8_t gatewidth;
} __packed;
static const uint8_t __code lut154[] = {
// lut0 (KEEP) voltages
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// lut1 (W2B) voltages
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// lut2 (B2W) voltages
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// lut3 (unused) voltages
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// lut4 (vcom) voltages
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// group0 phase lengths and repeat count
0x40, 0x0, 0x00, 0x00, 0x00,
// group1 not used
0x00, 0x00, 0x00, 0x00, 0x00,
// group2 not used
0x00, 0x00, 0x00, 0x00, 0x00,
// group3 phase lengths and repeat count
0x00, 0x00, 0x00, 0x00, 0x00,
// group4 phase lengths and repeat count
0x00, 0x00, 0x00, 0x00, 0x00,
// group5 phase lengths and repeat count
0x00, 0x00, 0x00, 0x00, 0x00,
// group6 phase lengths and repeat count
0x00, 0x00, 0x00, 0x00, 0x00,
};
static const uint8_t __code lut29[] = {
// lut0 (KEEP) voltages
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// lut1 (W2B) voltages
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// lut2 (B2W) voltages
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// lut3 (unused) voltages
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// lut4 (vcom) voltages
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// group0 phase lengths and repeat count
// 0x10, 0x02, 0x00, 0x00, 0x03 - 1,
0x05, 0x02, 0x00, 0x00, 0x00,
// 0x40, 0x00, 0x00, 0x00, 0x00,
// group1 not used
0x00, 0x00, 0x00, 0x00, 0x00,
// group2 not used
0x00, 0x00, 0x00, 0x00, 0x00,
// group3 phase lengths and repeat count
0x00, 0x00, 0x00, 0x00, 0x00,
// group4 phase lengths and repeat count
0x00, 0x00, 0x00, 0x00, 0x00,
// group5 phase lengths and repeat count
0x00, 0x00, 0x00, 0x00, 0x00,
// group6 phase lengths and repeat count
0x00, 0x00, 0x00, 0x00, 0x00,
};
static const uint8_t __code lutSHA[] = {
// Voltages and other settings? Timing?
0xA0, 0x90, 0x50, 0x0, 0x0, 0x0, 0x0,
0x50, 0x90, 0xA0, 0x0, 0x0, 0x0, 0x0,
0xA0, 0x90, 0x50, 0x0, 0x0, 0x0, 0x0,
0x50, 0x90, 0xA0, 0x0, 0x0, 0x0, 0x0,
0x00, 0x00, 0x00, 0x0, 0x0, 0x0, 0x0,
// Update program
//
// Top three lines are the main program (bottom 4 have unknown function)
// Line 1: Negative image
// Line 2: White/Black flashing
// Line 3: Positive image
//
// Line construction
// First two bytes denote Intensity (range 0x00 to 0x0F)
// Second two bytes denote lenght of each 'pulse' (range 0x00 to 0xFF)
// Last byte denotes number of repeats (0 = line runs 1 time, range 0x00 to 0xFF)
// If you don't want a line to do anything, set all bytes to 0x0.
// This way you can make a quick update cycle between two screens.
// Maybe not as pretty/crisp but nice and fast is also awesome!
// Negative image
// first two bytes negative image, length white pulse (0-FF), length black pulse (0-FF), last byte repeats
0x0, 0x0, 0x0, 0x0, 0x0,
//0xF, 0xF, 0x0, 0x0, 0x0,
// White or black flash
// white flash intensity, black flash intensity, length white pulse (0-FF), length black pulse (0-FF), repeats
//0x0, 0x0, 0x0, 0x0, 0x00,
0xF, 0xF, 0x1, 0x1, 0x00,
//0xF, 0xF, 0x0, 0x0, 0x02,
// Positive image
// first byte or second byte positive image (don't know why you need both), rest same as above
0xF, 0xF, 0x0, 0x0, 0x0,
// Unknown what lines below actually do.
// They seem to be programs to, but have no visible effect on dislay.
0x0F, 0x0F, 0x0, 0x0, 0x0,
0x0F, 0x0F, 0x0, 0x0, 0x0,
0x0F, 0x0F, 0x0, 0x0, 0x0,
0x0F, 0x0F, 0x0, 0x0, 0x0,
};
static const uint8_t __code lutorig[] = {
0x00, 0x66, 0x21, 0x45, 0x40, 0x00, 0x00,
0x15, 0x66, 0x21, 0xA8, 0x20, 0xA0, 0x00,
0xA0, 0x66, 0x21, 0x85, 0x2B, 0x2F, 0x00,
0xA0, 0x66, 0x21, 0x85, 0x2B, 0x2F, 0x00,
0x00, 0x00, 0x12, 0x48, 0x00, 0x00, 0x00,
//0x04, 0x49, 0x2F, 0x2A, 0x00,
0x0, 0x0, 0x0, 0x0, 0x0,
0x02, 0x04, 0x01, 0x03, 0x00, // was 11 repeat
0x01, 0x14, 0x01, 0x14, 0x00, // was 3 repeat
0x02, 0x0A, 0x03, 0x0A, 0x00, // was 2 repeat
0x06, 0x04, 0x04, 0x20, 0x00, // was 3 rpeat
0x04, 0x04, 0x02, 0x26, 0x00, // was 3 repeat
0x00, 0x00, 0x00, 0x00, 0x0,
};
struct waveform10 {
struct lut10 lut[5];
struct group group[10];
uint8_t gatelevel;
uint8_t sourcelevel[3];
uint8_t dummyline;
uint8_t gatewidth;
} __packed;

View File

@@ -4,22 +4,140 @@
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "asmUtil.h"
#include "board.h"
#include "comms.h"
#include "cpu.h"
#include "drawing.h"
#include "comms.h" // for mLastLqi and mLastRSSI
#include "eeprom.h"
#include "epd.h"
#include "powermgt.h"
#include "printf.h"
#include "proto.h"
#include "radio.h"
#include "screen.h"
#include "sleep.h"
#include "timer.h"
#include "wdt.h"
#include "syncedproto.h"
#include "timer.h"
#include "userinterface.h"
#include "wdt.h"
//#define DEBUG_MODE
void main(void){
uint8_t showChannelSelect() {
uint8_t __xdata result[16];
memset(result, 0, sizeof(result));
showScanningWindow();
for (uint8_t i = 0; i < 8; i++) {
for (uint8_t c = 11; c < 27; c++) {
if (probeChannel(c)) {
if (mLastLqi > result[c - 11]) result[c - 11] = mLastLqi;
pr("Channel: %d - LQI: %d RSSI %d\n", c, mLastLqi, mLastRSSI);
}
}
epdWaitRdy();
for (uint8_t c = 0; c < 16; c++) {
addScanResult(11 + c, result[c]);
}
drawNoWait();
}
uint8_t __xdata highestLqi = 0;
uint8_t __xdata highestSlot = 0;
for (uint8_t c = 0; c < sizeof(result); c++) {
if (result[c] > highestLqi) {
highestSlot = c + 11;
highestLqi = result[c];
}
}
epdWaitRdy();
mLastLqi = highestLqi;
return highestSlot;
}
void mainProtocolLoop(void) {
clockingAndIntsInit();
timerInit();
boardInit();
if (!boardGetOwnMac(mSelfMac)) {
pr("failed to get MAC. Aborting\n");
while (1)
;
} else {
pr("MAC>%02X%02X", mSelfMac[0], mSelfMac[1]);
pr("%02X%02X", mSelfMac[2], mSelfMac[3]);
pr("%02X%02X", mSelfMac[4], mSelfMac[5]);
pr("%02X%02X\n", mSelfMac[6], mSelfMac[7]);
}
irqsOn();
boardInitStage2();
pr("BOOTED> (UI 0.03-1)\n\n");
if (!eepromInit()) {
pr("failed to init eeprom\n");
while (1)
;
} else {
initializeProto();
}
eepromDeepPowerDown();
// initialize Powers-saving-attempt-array with the default value;
initPowerSaving();
#ifndef DEBUG_MODE
// show the splashscreen
showSplashScreen();
eepromDeepPowerDown();
initRadio();
currentChannel = showChannelSelect();
if (currentChannel == 0) {
// couldn't find an AP :()
showNoAP();
} else {
radioSetChannel(currentChannel);
// Found an AP.
showAPFound();
}
#endif
#ifdef DEBUG_MODE
initRadio();
currentChannel = 11;
#endif
epdEnterSleep();
P1CHSTA &= ~(1 << 0);
while (1) {
radioRxEnable(true, true);
struct AvailDataInfo *__xdata avail = getAvailDataInfo();
if (avail == NULL) {
// no data :(
nextCheckInFromAP = 0; // let the power-saving algorithm determine the next sleep period
} else {
nextCheckInFromAP = avail->nextCheckIn;
// got some data from the AP!
if (avail->dataType != DATATYPE_NOUPDATE) {
// data transfer
if (doDataDownload(avail)) {
// succesful transfer, next wake time is determined by the NextCheckin;
} else {
// failed transfer, let the algorithm determine next sleep interval (not the AP)
nextCheckInFromAP = 0;
}
} else {
// no data transfer, just sleep.
}
}
// if the AP told us to sleep for a specific period, do so.
if (nextCheckInFromAP) {
doSleep(nextCheckInFromAP * 60000UL);
} else {
doSleep(getNextSleep() * 1000UL);
}
}
}
void main(void) {
mainProtocolLoop();
}

View File

@@ -1,6 +1,20 @@
@echo off
del fs154.bin
del fw29.bin
del fw42.bin
makeit clean
makeit
makeit BUILD=zbs154v033 CPU=8051 SOC=zbs243
pause
ren main.bin fw154.bin
makeit clean
makeit BUILD=zbs29v033 CPU=8051 SOC=zbs243
pause
ren main.bin fw29.bin
makeit clean
makeit BUILD=zbs42v033 CPU=8051 SOC=zbs243
pause
ren main.bin fw42.bin
del /s *.asm
del /s *.lst
del /s *.rst

92
tag_fw/powermgt.c Normal file
View File

@@ -0,0 +1,92 @@
#include "powermgt.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "asmUtil.h"
#include "board.h"
#include "comms.h"
#include "cpu.h"
#include "drawing.h"
#include "eeprom.h"
#include "epd.h"
#include "i2c.h"
#include "printf.h"
#include "proto.h"
#include "radio.h"
#include "sleep.h"
#include "syncedproto.h"
#include "timer.h"
#include "userinterface.h"
#include "wdt.h"
#include "settings.h"
uint16_t __xdata dataReqAttemptArr[POWER_SAVING_SMOOTHING] = {0}; // Holds the amount of attempts required per data_req/check-in
uint8_t __xdata dataReqAttemptArrayIndex = 0;
uint8_t __xdata dataReqLastAttempt = 0;
uint16_t __xdata nextCheckInFromAP = 0;
void initPowerSaving() {
for (uint8_t c = 0; c < POWER_SAVING_SMOOTHING; c++) {
dataReqAttemptArr[c] = INTERVAL_BASE;
}
}
// init/sleep
void initAfterWake() {
clockingAndIntsInit();
timerInit();
// partialInit();
boardInit();
epdEnterSleep();
irqsOn();
boardInitStage2();
initRadio();
}
void doSleep(uint32_t __xdata t) {
if (t > 1000) pr("s=%lu\n ", t / 1000);
powerPortsDownForSleep();
#ifdef HAS_BUTTON
// Button setup on TEST pin 1.0 (input pullup)
P1FUNC &= ~(1 << 0);
P1DIR |= (1 << 0);
P1PULL |= (1 << 0);
P1LVLSEL |= (1 << 0);
P1INTEN = (1 << 0);
P1CHSTA &= ~(1 << 0);
#endif
// sleepy
sleepForMsec(t);
#ifdef HAS_BUTTON
P1INTEN = 0;
#endif
initAfterWake();
}
uint16_t getNextSleep() {
uint16_t __xdata curval = INTERVAL_AT_MAX_ATTEMPTS - INTERVAL_BASE;
curval *= dataReqLastAttempt;
curval /= DATA_REQ_MAX_ATTEMPTS;
curval += INTERVAL_BASE;
dataReqAttemptArr[dataReqAttemptArrayIndex % POWER_SAVING_SMOOTHING] = curval;
dataReqAttemptArrayIndex++;
uint16_t avg = 0;
bool noNetwork = true;
for (uint8_t c = 0; c < POWER_SAVING_SMOOTHING; c++) {
avg += dataReqAttemptArr[c];
if (dataReqAttemptArr[c] != INTERVAL_AT_MAX_ATTEMPTS) {
noNetwork = false;
}
}
if (noNetwork == true) return INTERVAL_NO_SIGNAL;
avg /= POWER_SAVING_SMOOTHING;
return avg;
}

24
tag_fw/powermgt.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef _POWERMGT_H_
#define _POWERMGT_H_
#include <stdint.h>
// power saving algorithm
#define INTERVAL_BASE 40 // interval (in seconds) (when 1 packet is sent/received) for target current (7.2µA)
#define INTERVAL_AT_MAX_ATTEMPTS 600 // interval (in seconds) (at max attempts) for target average current
#define INTERVAL_NO_SIGNAL 1800 // interval (in seconds) when no answer for POWER_SAVING_SMOOTHING attempts,
// (INTERVAL_AT_MAX_ATTEMPTS * POWER_SAVING_SMOOTHING) seconds
#define DATA_REQ_RX_WINDOW_SIZE 5UL // How many milliseconds we should wait for a packet during the data_request.
// If the AP holds a long list of data for tags, it may need a little more time to lookup the mac address
#define DATA_REQ_MAX_ATTEMPTS 14 // How many attempts (at most) we should do to get something back from the AP
#define POWER_SAVING_SMOOTHING 8 // How many samples we should use to smooth the data request interval
#define MINIMUM_INTERVAL 45 // IMPORTANT: Minimum interval for check-in; this determines overal battery life!
extern void initAfterWake();
extern void doSleep(uint32_t __xdata t);
extern uint16_t getNextSleep();
extern void initPowerSaving();
extern uint16_t __xdata nextCheckInFromAP;
extern uint8_t __xdata dataReqLastAttempt;
#endif

View File

@@ -19,6 +19,9 @@
#pragma callee_saves pr
void pr(const char __code *fmt, ...) __reentrant;
#pragma callee_saves epdpr
void epdpr(const char __code *fmt, ...) __reentrant;
#pragma callee_saves spr
void spr(char __xdata* out, const char __code *fmt, ...) __reentrant;

View File

@@ -1,47 +1,8 @@
#ifndef _PROTO_H_
#define _PROTO_H_
#define __packed
#include <stdint.h>
/*
All communications are direct from tag to station, EXCEPT association (tag will broadcast).
All comms shall be encrypted and authenticated with AES-CCM. Shared key shall be burned into the firmware.
Master shall provision new key at association. All non-bcast packets shall have pan id compression.
Master may skip "from" field. Tag checking in confirms it got the master's provisioning reply.
Sadly filtering on MZ100 fails for long addr with no src addr. so short addr for src is used
T = tag, S = station
PACKET TYPE USE PAYLOAD STRUCT NOTES
ASSOC_REQ T2bcast TagInfo tag's info and assoc request (encrypted with shared key)
ASSOC_RESP S2T AssocInfo tag's association info (encrypted with shared key)
CHECKIN T2S CheckinInfo tag checking in occasionally
CHECKOUT S2T PendingInfo station's checkin reply telling tag what we have for it
CHUNK_REQ T2S ChunkReqInfo tag requesting a piece of data
CHUNK_RESP S2T ChunkInfo station provides chunk
*/
#define PROTO_PRESHARED_KEY {0x34D906D3, 0xE3E5298E, 0x3429BF58, 0xC1022081}
#define PROTO_PAN_ID (0x4447) //PAN ID compression shall be used
#define PKT_ASSOC_REQ (0xF0)
#define PKT_ASSOC_RESP (0xF1)
#define PKT_CHECKIN (0xF2)
#define PKT_CHECKOUT (0xF3)
#define PKT_CHUNK_REQ (0xF4)
#define PKT_CHUNK_RESP (0xF5)
#define PROTO_VER_0 (0)
#define PROTO_VER_CURRENT (PROTO_VER_0)
#define PROTO_COMPR_TYPE_LZ (0x0001)
#define PROTO_COMPR_TYPE_BITPACK (0x0002)
#define PROTO_MAX_DL_LEN (88)
enum TagScreenType {
TagScreenEink_BW_1bpp,
TagScreenEink_BW_2bpp,
@@ -72,111 +33,135 @@ enum TagScreenType {
#define __packed __attribute__((packed))
#endif
struct TagState {
uint64_t swVer;
uint16_t hwType;
uint16_t batteryMv;
#define PROTO_PAN_ID (0x4447) //PAN ID compression shall be used
#define RADIO_MAX_PACKET_LEN (125) //useful payload, not including the crc
#define ADDR_MODE_NONE (0)
#define ADDR_MODE_SHORT (2)
#define ADDR_MODE_LONG (3)
#define FRAME_TYPE_BEACON (0)
#define FRAME_TYPE_DATA (1)
#define FRAME_TYPE_ACK (2)
#define FRAME_TYPE_MAC_CMD (3)
#define SHORT_MAC_UNUSED (0x10000000UL) //for radioRxFilterCfg's myShortMac
struct MacFcs {
uint8_t frameType : 3;
uint8_t secure : 1;
uint8_t framePending : 1;
uint8_t ackReqd : 1;
uint8_t panIdCompressed : 1;
uint8_t rfu1 : 1;
uint8_t rfu2 : 2;
uint8_t destAddrType : 2;
uint8_t frameVer : 2;
uint8_t srcAddrType : 2;
} __packed ;
struct MacFrameFromMaster {
struct MacFcs fcs;
uint8_t seq;
uint16_t pan;
uint8_t dst[8];
uint16_t from;
} __packed;
struct TagInfo {
uint8_t protoVer; //PROTO_VER_*
struct TagState state;
uint8_t rfu1[1]; //shall be ignored for now
uint16_t screenPixWidth;
uint16_t screenPixHeight;
uint16_t screenMmWidth;
uint16_t screenMmHeight;
uint16_t compressionsSupported; //COMPR_TYPE_* bitfield
uint16_t maxWaitMsec; //how long tag will wait for packets before going to sleep
uint8_t screenType; //enum TagScreenType
uint8_t rfu[11]; //shall be zero for now
struct MacFrameNormal {
struct MacFcs fcs;
uint8_t seq;
uint16_t pan;
uint8_t dst[8];
uint8_t src[8];
} __packed;
struct AssocInfo {
uint32_t checkinDelay; //space between checkins, in msec
uint32_t retryDelay; //if download fails mid-way wait thi smany msec to retry (IFF progress was made)
uint16_t failedCheckinsTillBlank; //how many fails till we go blank
uint16_t failedCheckinsTillDissoc; //how many fails till we dissociate
uint32_t newKey[4];
uint8_t rfu[8]; //shall be zero for now
struct MacFrameBcast {
struct MacFcs fcs;
uint8_t seq;
uint16_t dstPan;
uint16_t dstAddr;
uint16_t srcPan;
uint8_t src[8];
} __packed;
#define CHECKIN_TEMP_OFFSET 0x7f
#define PKT_AVAIL_DATA_REQ 0xE5
#define PKT_AVAIL_DATA_INFO 0xE6
#define PKT_BLOCK_PARTIAL_REQUEST 0xE7
#define PKT_BLOCK_REQUEST_ACK 0xE9
#define PKT_BLOCK_REQUEST 0xE4
#define PKT_BLOCK_PART 0xE8
#define PKT_XFER_COMPLETE 0xEA
#define PKT_XFER_COMPLETE_ACK 0xEB
#define PKT_CANCEL_XFER 0xEC
struct CheckinInfo {
struct TagState state;
uint8_t lastPacketLQI; //zero if not reported/not supported to be reported
int8_t lastPacketRSSI; //zero if not reported/not supported to be reported
uint8_t temperature; //zero if not reported/not supported to be reported. else, this minus CHECKIN_TEMP_OFFSET is temp in degrees C
uint8_t rfu[6]; //shall be zero for now
struct AvailDataReq {
uint8_t checksum;
uint8_t lastPacketLQI; // zero if not reported/not supported to be reported
int8_t lastPacketRSSI; // zero if not reported/not supported to be reported
uint8_t temperature; // zero if not reported/not supported to be reported. else, this minus CHECKIN_TEMP_OFFSET is temp in degrees C
uint16_t batteryMv;
uint8_t softVer;
uint8_t hwType;
uint8_t protoVer;
uint8_t buttonState;
} __packed;
struct PendingInfo {
uint64_t imgUpdateVer;
uint32_t imgUpdateSize;
uint64_t osUpdateVer; //version of OS update avail
uint32_t osUpdateSize;
uint8_t rfu[8]; //shall be zero for now
#define DATATYPE_NOUPDATE 0
#define DATATYPE_IMG 1
#define DATATYPE_IMGRAW 2
#define DATATYPE_UPDATE 3
struct AvailDataInfo {
uint8_t checksum;
uint64_t dataVer;
uint32_t dataSize;
uint8_t dataType;
uint16_t nextCheckIn;
} __packed;
struct ChunkReqInfo {
uint64_t versionRequested;
uint32_t offset;
uint8_t len;
uint8_t osUpdatePlz : 1;
uint8_t rfu[6]; //shall be zero for now
struct blockPart {
uint8_t checksum;
uint8_t blockId;
uint8_t blockPart;
uint8_t data[];
} __packed;
struct ChunkInfo {
uint32_t offset;
uint8_t osUpdatePlz : 1;
uint8_t rfu; //shall be zero for now
uint8_t data[]; //no data means request is out of bounds of this version no longer exists
struct blockData {
uint16_t size;
uint16_t checksum;
uint8_t data[];
} __packed;
struct burstMacData {
uint16_t offset;
uint8_t targetMac[8];
} __packed;
#define BLOCK_PART_DATA_SIZE 99
#define BLOCK_MAX_PARTS 42
#define BLOCK_DATA_SIZE 4096UL
#define BLOCK_XFER_BUFFER_SIZE BLOCK_DATA_SIZE + sizeof(struct blockData)
#define BLOCK_REQ_PARTS_BYTES 6
struct blockRequest {
uint8_t checksum;
uint64_t ver;
uint8_t blockId;
uint8_t type;
uint8_t requestedParts[BLOCK_REQ_PARTS_BYTES];
} __packed;
struct blockRequestAck {
uint8_t checksum;
uint16_t pleaseWaitMs;
} __packed;
#define MACFMT "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x"
#define MACCVT(x) ((const uint8_t*)(x))[7], ((const uint8_t*)(x))[6], ((const uint8_t*)(x))[5], ((const uint8_t*)(x))[4], ((const uint8_t*)(x))[3], ((const uint8_t*)(x))[2], ((const uint8_t*)(x))[1], ((const uint8_t*)(x))[0]
#define VERSION_SIGNIFICANT_MASK (0x0000ffffffffffffull)
#define HW_TYPE_42_INCH_SAMSUNG (1)
#define HW_TYPE_42_INCH_SAMSUNG_ROM_VER_OFST (0xEFF8)
#define HW_TYPE_74_INCH_DISPDATA (2)
#define HW_TYPE_74_INCH_DISPDATA_FRAME_MODE (3)
#define HW_TYPE_74_INCH_DISPDATA_ROM_VER_OFST (0x008b)
#define HW_TYPE_ZBD_EPOP50 (4)
#define HW_TYPE_ZBD_EPOP50_ROM_VER_OFST (0x008b)
#define HW_TYPE_ZBD_EPOP900 (5)
#define HW_TYPE_ZBD_EPOP900_ROM_VER_OFST (0x008b)
#define HW_TYPE_29_INCH_DISPDATA (6)
#define HW_TYPE_29_INCH_DISPDATA_FRAME_MODE (7)
#define HW_TYPE_29_INCH_DISPDATA_ROM_VER_OFST (0x008b)
#define HW_TYPE_29_INCH_ZBS_026 (8)
#define HW_TYPE_29_INCH_ZBS_026_FRAME_MODE (9)
#define HW_TYPE_154_INCH_ZBS_033 (18)
#define HW_TYPE_154_INCH_ZBS_033_FRAME_MODE (19)
#define HW_TYPE_42_INCH_ZBS_026 (28)
#define HW_TYPE_42_INCH_ZBS_026_FRAME_MODE (29)
#define HW_TYPE_29_INCH_ZBS_025 (10)
#define HW_TYPE_29_INCH_ZBS_025_FRAME_MODE (11)
#define HW_TYPE_29_INCH_ZBS_ROM_VER_OFST (0x008b)
#endif
#endif

12
tag_fw/settings.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef SYNCED_H
#define SYNCED_H
#include <stdint.h>
#define FW_VERSION 012 // version number (max 2.5.5 :) )
#define FW_VERSION_SUFFIX "-rf15" // suffix, like -RC1 or whatever.
#define HAS_BUTTON // uncomment to enable reading a push button (connect between 'TEST' en 'GND' on the tag, along with a 100nF capacitor in parallel).
// #define DEBUGBLOCKS // uncomment to enable extra debug information on the block transfers
#endif

View File

@@ -5,6 +5,10 @@
#include <stdint.h>
//radio cfg
#define RADIO_FIRST_CHANNEL (11) //2.4-GHz channels start at 11
#define RADIO_NUM_CHANNELS (15)
#define RADIO_MAX_PACKET_LEN (125) //useful payload, not including the crc
@@ -20,22 +24,6 @@
#define SHORT_MAC_UNUSED (0x10000000UL) //for radioRxFilterCfg's myShortMac
struct MacFcs {
uint8_t frameType : 3;
uint8_t secure : 1;
uint8_t framePending : 1;
uint8_t ackReqd : 1;
uint8_t panIdCompressed : 1;
uint8_t rfu1 : 1;
uint8_t rfu2 : 2;
uint8_t destAddrType : 2;
uint8_t frameVer : 2;
uint8_t srcAddrType : 2;
};
void radioInit(void);
bool radioTx(const void __xdata* packet); //waits for tx end

View File

@@ -4,7 +4,7 @@
#include <stdint.h>
#define TIMER_TICKS_PER_SECOND (16000000 / 12) //overflows every 53 minutes
#define TIMER_TICKS_PER_MS 1333UL
//this is a requirement by SDCC. is this prototype is missing when compiling main(), we get no irq handler
void T0_ISR(void) __interrupt (1);

View File

@@ -12,10 +12,7 @@ void uartInit(void) {
UARTSTA = 0x12; // also set the "empty" bit else we wait forever for it to go up
}
extern void writeCharEPD(uint8_t c);
void uartTx(uint8_t val) {
writeCharEPD(val);
while (!(UARTSTA & (1 << 1)))
;
UARTSTA &= ~(1 << 1);

View File

@@ -6,122 +6,23 @@
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "asmUtil.h"
#include "board.h"
#include "comms.h"
#include "cpu.h"
#include "drawing.h"
#include "eeprom.h"
#include "i2c.h"
#include "powermgt.h"
#include "printf.h"
#include "proto.h"
#include "radio.h"
#include "epd.h"
#include "userinterface.h"
#include "settings.h"
#include "sleep.h"
#include "timer.h"
#include "userinterface.h"
#include "wdt.h"
struct MacFrameFromMaster {
struct MacFcs fcs;
uint8_t seq;
uint16_t pan;
uint8_t dst[8];
uint16_t from;
} __packed;
struct MacFrameNormal {
struct MacFcs fcs;
uint8_t seq;
uint16_t pan;
uint8_t dst[8];
uint8_t src[8];
} __packed;
struct MacFrameBcast {
struct MacFcs fcs;
uint8_t seq;
uint16_t dstPan;
uint16_t dstAddr;
uint16_t srcPan;
uint8_t src[8];
} __packed;
#define PKT_AVAIL_DATA_REQ 0xE5
#define PKT_AVAIL_DATA_INFO 0xE6
#define PKT_BLOCK_PARTIAL_REQUEST 0xE7
#define PKT_BLOCK_REQUEST_ACK 0xE9
#define PKT_BLOCK_REQUEST 0xE4
#define PKT_BLOCK_PART 0xE8
#define PKT_XFER_COMPLETE 0xEA
#define PKT_XFER_COMPLETE_ACK 0xEB
#define PKT_CANCEL_XFER 0xEC
struct AvailDataReq {
uint8_t checksum;
uint8_t lastPacketLQI; // zero if not reported/not supported to be reported
int8_t lastPacketRSSI; // zero if not reported/not supported to be reported
uint8_t temperature; // zero if not reported/not supported to be reported. else, this minus CHECKIN_TEMP_OFFSET is temp in degrees C
uint16_t batteryMv;
uint8_t softVer;
uint8_t hwType;
uint8_t protoVer;
uint8_t buttonState;
} __packed;
#define DATATYPE_NOUPDATE 0
#define DATATYPE_IMG 1
#define DATATYPE_IMGRAW 2
#define DATATYPE_UPDATE 3
struct AvailDataInfo {
uint8_t checksum;
uint64_t dataVer;
uint32_t dataSize;
uint8_t dataType;
uint16_t nextCheckIn;
} __packed;
struct blockPart {
uint8_t checksum;
uint8_t blockId;
uint8_t blockPart;
uint8_t data[];
} __packed;
struct blockData {
uint16_t size;
uint16_t checksum;
uint8_t data[];
} __packed;
struct burstMacData {
uint16_t offset;
uint8_t targetMac[8];
} __packed;
#define BLOCK_PART_DATA_SIZE 99
#define BLOCK_MAX_PARTS 42
#define BLOCK_DATA_SIZE 4096UL
#define BLOCK_XFER_BUFFER_SIZE BLOCK_DATA_SIZE + sizeof(struct blockData)
#define BLOCK_REQ_PARTS_BYTES 6
struct blockRequest {
uint8_t checksum;
uint64_t ver;
uint8_t blockId;
uint8_t type;
uint8_t requestedParts[BLOCK_REQ_PARTS_BYTES];
} __packed;
struct blockRequestAck {
uint8_t checksum;
uint16_t pleaseWaitMs;
} __packed;
#define TIMER_TICKS_PER_MS 1333UL
// #define DEBUGBLOCKS
// download-stuff
bool __xdata dataPending = true;
uint8_t __xdata blockXferBuffer[BLOCK_XFER_BUFFER_SIZE] = {0};
@@ -131,7 +32,7 @@ uint16_t __xdata dataRemaining = 0;
bool __xdata curXferComplete = false;
bool __xdata requestPartialBlock = false;
//uint8_t __xdata *tempBuffer = blockXferBuffer;
// uint8_t __xdata *tempBuffer = blockXferBuffer;
uint8_t __xdata curImgSlot = 0;
uint32_t __xdata curHighSlotId = 0;
uint8_t __xdata nextImgSlot = 0;
@@ -148,24 +49,7 @@ uint8_t __xdata APmac[8] = {0};
uint16_t __xdata APsrcPan = 0;
uint8_t __xdata mSelfMac[8] = {0};
uint8_t __xdata seq = 0;
// power saving algorithm
#define INTERVAL_BASE 40 // interval (in seconds) (when 1 packet is sent/received) for target current (7.2µA)
#define INTERVAL_AT_MAX_ATTEMPTS 600 // interval (in seconds) (at max attempts) for target average current
#define INTERVAL_NO_SIGNAL 1800 // interval (in seconds) when no answer for POWER_SAVING_SMOOTHING attempts,
// (INTERVAL_AT_MAX_ATTEMPTS * POWER_SAVING_SMOOTHING) seconds
#define DATA_REQ_RX_WINDOW_SIZE 5UL // How many milliseconds we should wait for a packet during the data_request.
// If the AP holds a long list of data for tags, it may need a little more time to lookup the mac address
#define DATA_REQ_MAX_ATTEMPTS 14 // How many attempts (at most) we should do to get something back from the AP
#define POWER_SAVING_SMOOTHING 8 // How many samples we should use to smooth the data request interval
#define MINIMUM_INTERVAL 45 // IMPORTANT: Minimum interval for check-in; this determines overal battery life!
#define HAS_BUTTON // uncomment to enable reading a push button (connect between 'TEST' en 'GND' on the tag, along with a 100nF capacitor in parallel).
uint16_t __xdata dataReqAttemptArr[POWER_SAVING_SMOOTHING] = {0}; // Holds the amount of attempts required per data_req/check-in
uint8_t __xdata dataReqAttemptArrayIndex = 0;
uint8_t __xdata dataReqLastAttempt = 0;
uint16_t __xdata nextCheckInFromAP = 0;
uint8_t __xdata currentChannel = 0;
// buffer we use to prepare/read packets
// static uint8_t __xdata mRxBuf[130];
@@ -224,11 +108,15 @@ void addCRC(void *p, uint8_t len) {
((uint8_t *)p)[0] = total;
}
// init/sleep
// radio stuff
void initRadio() {
radioInit();
radioRxFilterCfg(mSelfMac, 0x10000, PROTO_PAN_ID);
radioSetChannel(RADIO_FIRST_CHANNEL);
if (currentChannel >= 11 && currentChannel <= 25) {
radioSetChannel(currentChannel);
} else {
radioSetChannel(RADIO_FIRST_CHANNEL);
}
radioSetTxPower(10);
}
void killRadio() {
@@ -242,58 +130,14 @@ void killRadio() {
RADIO_command = 0xC5;
CFGPAGE = cfgPg;
}
void initAfterWake() {
clockingAndIntsInit();
timerInit();
// partialInit();
boardInit();
epdEnterSleep();
irqsOn();
boardInitStage2();
initRadio();
}
void doSleep(uint32_t __xdata t) {
if (t > 1000) pr("s=%lu\n ", t / 1000);
powerPortsDownForSleep();
bool probeChannel(uint8_t channel) {
radioRxEnable(false, true);
radioRxFlush();
radioSetChannel(channel);
radioRxEnable(true, true);
getAvailDataInfo();
return(dataReqLastAttempt != DATA_REQ_MAX_ATTEMPTS);
#ifdef HAS_BUTTON
// Button setup on TEST pin 1.0 (input pullup)
P1FUNC &= ~(1 << 0);
P1DIR |= (1 << 0);
P1PULL |= (1 << 0);
P1LVLSEL |= (1 << 0);
P1INTEN = (1 << 0);
P1CHSTA &= ~(1 << 0);
#endif
// sleepy
sleepForMsec(t);
#ifdef HAS_BUTTON
P1INTEN = 0;
#endif
initAfterWake();
}
uint16_t getNextSleep() {
uint16_t __xdata curval = INTERVAL_AT_MAX_ATTEMPTS - INTERVAL_BASE;
curval *= dataReqLastAttempt;
curval /= DATA_REQ_MAX_ATTEMPTS;
curval += INTERVAL_BASE;
dataReqAttemptArr[dataReqAttemptArrayIndex % POWER_SAVING_SMOOTHING] = curval;
dataReqAttemptArrayIndex++;
uint16_t avg = 0;
bool noNetwork = true;
for (uint8_t c = 0; c < POWER_SAVING_SMOOTHING; c++) {
avg += dataReqAttemptArr[c];
if (dataReqAttemptArr[c] != INTERVAL_AT_MAX_ATTEMPTS) {
noNetwork = false;
}
}
if (noNetwork == true) return INTERVAL_NO_SIGNAL;
avg /= POWER_SAVING_SMOOTHING;
return avg;
}
// data xfer stuff
@@ -311,7 +155,7 @@ void sendAvailDataReq() {
txframe->seq = seq++;
txframe->dstPan = 0xFFFF;
txframe->dstAddr = 0xFFFF;
txframe->srcPan = 0x4447;
txframe->srcPan = PROTO_PAN_ID;
// TODO: send some meaningful data
availreq->softVer = 1;
if (P1CHSTA && (1 << 0)) {
@@ -573,7 +417,6 @@ uint32_t getHighSlotId() {
return temp;
}
// #define DEBUGBLOCKS
// Main download function
bool doDataDownload(struct AvailDataInfo *__xdata avail) {
// this is the main function for the download process
@@ -835,86 +678,7 @@ bool doDataDownload(struct AvailDataInfo *__xdata avail) {
return true;
}
// main loop;
void mainProtocolLoop(void) {
clockingAndIntsInit();
timerInit();
boardInit();
if (!boardGetOwnMac(mSelfMac)) {
pr("failed to get MAC. Aborting\n");
while (1)
;
} else {
/*
for (uint8_t c = 0; c < 8; c++) {
mSelfMac[c] = c + 5;
}
*/
// really... if I do the call below, it'll cost me 8 bytes IRAM. Not the kind of 'optimization' I ever dreamed of doing
// pr("MAC>%02X%02X%02X%02X%02X%02X%02X%02X\n", mSelfMac[0], mSelfMac[1], mSelfMac[2], mSelfMac[3], mSelfMac[4], mSelfMac[5], mSelfMac[6], mSelfMac[7]);
pr("MAC>%02X%02X", mSelfMac[0], mSelfMac[1]);
pr("%02X%02X", mSelfMac[2], mSelfMac[3]);
pr("%02X%02X", mSelfMac[4], mSelfMac[5]);
pr("%02X%02X\n", mSelfMac[6], mSelfMac[7]);
}
irqsOn();
boardInitStage2();
pr("BOOTED> (UI 0.03-1)\n\n");
if (!eepromInit()) {
pr("failed to init eeprom\n");
while (1)
;
} else {
getNumSlots();
curHighSlotId = getHighSlotId();
}
// initialize attempt-array with the default value;
for (uint8_t c = 0; c < POWER_SAVING_SMOOTHING; c++) {
dataReqAttemptArr[c] = INTERVAL_BASE;
}
// show the splashscreen
showSplashScreen();
epdEnterSleep();
eepromDeepPowerDown();
initRadio();
P1CHSTA &= ~(1 << 0);
while (1) {
radioRxEnable(true, true);
struct AvailDataInfo *__xdata avail = getAvailDataInfo();
if (avail == NULL) {
// no data :(
nextCheckInFromAP = 0; // let the power-saving algorithm determine the next sleep period
} else {
nextCheckInFromAP = avail->nextCheckIn;
// got some data from the AP!
if (avail->dataType != DATATYPE_NOUPDATE) {
// data transfer
if (doDataDownload(avail)) {
// succesful transfer, next wake time is determined by the NextCheckin;
} else {
// failed transfer, let the algorithm determine next sleep interval (not the AP)
nextCheckInFromAP = 0;
}
} else {
// no data transfer, just sleep.
}
}
// if the AP told us to sleep for a specific period, do so.
if (nextCheckInFromAP) {
doSleep(nextCheckInFromAP * 60000UL);
} else {
doSleep(getNextSleep() * 1000UL);
}
}
void initializeProto() {
getNumSlots();
curHighSlotId = getHighSlotId();
}

View File

@@ -3,8 +3,18 @@
#include <stdint.h>
void mainProtocolLoop(void);
extern uint8_t __xdata mSelfMac[];
extern uint8_t __xdata mSelfMac[8];
extern uint8_t __xdata currentChannel;
extern uint8_t __xdata APmac[];
extern void initRadio();
extern void killRadio();
extern struct AvailDataInfo *__xdata getAvailDataInfo();
extern bool doDataDownload(struct AvailDataInfo *__xdata avail);
extern void initializeProto();
extern struct AvailDataInfo *__xdata getAvailDataInfo();
bool probeChannel(uint8_t channel);
#endif

View File

@@ -1,109 +1,285 @@
#include "userinterface.h"
#include <stdbool.h>
#include <string.h>
#include "asmUtil.h"
#include "bitmaps.h"
#include "board.h"
#include "comms.h"
#include "cpu.h"
#include "epd.h"
#include "font.h"
#include "lut.h"
#include "printf.h"
#include "screen.h"
#include "settings.h"
#include "sleep.h"
#include "spi.h"
#include "syncedproto.h" // for APmac / Channel
#include "timer.h"
extern uint8_t mSelfMac[];
extern uint8_t __xdata mSelfMac[8];
extern uint8_t __xdata currentChannel;
extern uint8_t __xdata APmac[];
static const uint8_t __code fwVersion = FW_VERSION;
static const char __code fwVersionSuffix[] = FW_VERSION_SUFFIX;
void showSplashScreen() {
epdSetup();
lutTest();
#if (SCREEN_WIDTH == 152) // 1.54"
selectLUT(1);
clearScreen();
setColorMode(EPD_MODE_NORMAL, EPD_MODE_INVERT);
drawLineHorizontal(EPD_COLOR_BLACK, 33, 1);
epdPrintBegin(0, 0, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
pr("Booting!");
selectLUT(1);
epdPrintBegin(12, 2, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
epdpr("Starting!");
epdPrintEnd();
loadRawBitmap(solum, 8, 34, EPD_COLOR_BLACK);
loadRawBitmap(hacked, 32, 46, EPD_COLOR_RED);
epdPrintBegin(5, 136, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_RED);
epdpr("%02X%02X", mSelfMac[7], mSelfMac[6]);
epdpr("%02X%02X", mSelfMac[5], mSelfMac[4]);
epdpr("%02X%02X", mSelfMac[3], mSelfMac[2]);
epdpr("%02X%02X", mSelfMac[1], mSelfMac[0]);
epdPrintEnd();
epdPrintBegin(2, 120, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
epdpr("zbs154v033 %d.%d.%d%s", fwVersion / 100, (fwVersion % 100) / 10, (fwVersion % 10), fwVersionSuffix);
epdPrintEnd();
draw();
timerDelay(1333000);
#endif
#if (SCREEN_WIDTH == 128) // 2.9"
selectLUT(1);
selectLUT(EPD_LUT_NO_REPEATS);
clearScreen();
setColorMode(EPD_MODE_NORMAL, EPD_MODE_INVERT);
epdPrintBegin(0, 200, EPD_DIRECTION_Y, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
pr("Booting!");
epdPrintBegin(0, 295, EPD_DIRECTION_Y, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
epdpr("Starting!");
epdPrintEnd();
epdPrintBegin(0, 294, EPD_DIRECTION_Y, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
pr("=");
epdPrintBegin(80, 295, EPD_DIRECTION_Y, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
epdpr("zbs29v033 %d.%d.%d%s", fwVersion / 100, (fwVersion % 100) / 10, (fwVersion % 10), fwVersionSuffix);
epdPrintEnd();
epdPrintBegin(32, 150, EPD_DIRECTION_Y, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
pr("-TESTING-");
epdPrintBegin(105, 270, EPD_DIRECTION_Y, EPD_SIZE_SINGLE, EPD_COLOR_RED);
epdpr("MAC: %02X:%02X", mSelfMac[7], mSelfMac[6]);
epdpr(":%02X:%02X", mSelfMac[5], mSelfMac[4]);
epdpr(":%02X:%02X", mSelfMac[3], mSelfMac[2]);
epdpr(":%02X:%02X", mSelfMac[1], mSelfMac[0]);
epdPrintEnd();
epdPrintBegin(115, 295, EPD_DIRECTION_Y, EPD_SIZE_SINGLE, EPD_COLOR_RED);
pr("MAC: %02X:%02X", mSelfMac[7], mSelfMac[6]);
pr(":%02X:%02X", mSelfMac[5], mSelfMac[4]);
pr(":%02X:%02X", mSelfMac[3], mSelfMac[2]);
pr(":%02X:%02X", mSelfMac[1], mSelfMac[0]);
epdPrintEnd();
uint8_t __xdata buffer[17];
spr(buffer, "%02X%02X", mSelfMac[7], mSelfMac[6]);
spr(buffer + 4, "%02X%02X", mSelfMac[5], mSelfMac[4]);
spr(buffer + 8, "%02X%02X", mSelfMac[3], mSelfMac[2]);
spr(buffer + 12, "%02X%02X", mSelfMac[1], mSelfMac[0]);
printBarcode(buffer, 120, 284);
epdPrintBegin(68, 294, EPD_DIRECTION_Y, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
pr("MAC64: %02X:%02X", mSelfMac[7], mSelfMac[6]);
pr(":%02X:%02X", mSelfMac[5], mSelfMac[4]);
pr(":%02X:%02X", mSelfMac[3], mSelfMac[2]);
pr(":%02X:%02X", mSelfMac[1], mSelfMac[0]);
epdPrintEnd();
drawLineVertical(EPD_COLOR_RED, 64, 10, 286);
drawLineVertical(EPD_COLOR_BLACK, 65, 10, 286);
loadRawBitmap(solum, 0, 0, EPD_COLOR_BLACK);
loadRawBitmap(hacked, 16, 12, EPD_COLOR_RED);
// lutTest();
// drawLineVertical(EPD_COLOR_RED, 64, 10, 286);
// drawLineVertical(EPD_COLOR_BLACK, 65, 10, 286);
draw();
timerDelay(1333000);
// timerDelay(TIMER_TICKS_PER_SECOND * 4);
#endif
#if (SCREEN_WIDTH == 400) // 2.9"
selectLUT(1);
clearScreen();
setColorMode(EPD_MODE_NORMAL, EPD_MODE_INVERT);
epdPrintBegin(64, 150, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
pr("TEST");
epdPrintEnd();
epdPrintBegin(300, 296, EPD_DIRECTION_Y, EPD_SIZE_DOUBLE, EPD_COLOR_RED);
pr("Booting!Y");
epdPrintEnd();
epdpr("Booting!Y");
epdpr();
epdPrintBegin(0, 0, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
pr("BootingX!");
epdpr("Starting!");
epdPrintEnd();
epdPrintBegin(16, 284, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_RED);
pr("MAC: %02X:%02X", mSelfMac[7], mSelfMac[6]);
pr(":%02X:%02X", mSelfMac[5], mSelfMac[4]);
pr(":%02X:%02X", mSelfMac[3], mSelfMac[2]);
pr(":%02X:%02X", mSelfMac[1], mSelfMac[0]);
epdPrintBegin(16, 252, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
epdpr("zbs42v033 %d.%d.%d%s", fwVersion / 100, (fwVersion % 100) / 10, (fwVersion % 10), fwVersionSuffix);
epdPrintEnd();
epdPrintBegin(16, 284, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_RED);
epdpr("MAC: %02X:%02X", mSelfMac[7], mSelfMac[6]);
epdpr(":%02X:%02X", mSelfMac[5], mSelfMac[4]);
epdpr(":%02X:%02X", mSelfMac[3], mSelfMac[2]);
epdpr(":%02X:%02X", mSelfMac[1], mSelfMac[0]);
epdPrintEnd();
loadRawBitmap(solum, 256, 10, EPD_COLOR_BLACK);
loadRawBitmap(hacked, 264, 22, EPD_COLOR_RED);
loadRawBitmap(solum, 253, 72, EPD_COLOR_BLACK);
loadRawBitmap(hacked, 261, 82, EPD_COLOR_RED);
draw();
timerDelay(1333000);
#endif
}
void showApplyUpdate() {
epdSetup();
setColorMode(EPD_MODE_NORMAL, EPD_MODE_INVERT);
selectLUT(1);
clearScreen();
setColorMode(EPD_MODE_IGNORE, EPD_MODE_NORMAL);
epdPrintBegin(8, 60, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
pr("Updating!");
epdpr("Updating!");
epdPrintEnd();
drawNoWait();
}
}
uint8_t __xdata resultcounter = 0;
void showScanningWindow() {
epdSetup();
setColorMode(EPD_MODE_NORMAL, EPD_MODE_INVERT);
selectLUT(EPD_LUT_FAST_NO_REDS);
clearScreen();
#if (SCREEN_WIDTH == 128) // 2.9"
epdPrintBegin(2, 275, EPD_DIRECTION_Y, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
epdpr("Scanning for APs");
epdPrintEnd();
epdPrintBegin(40, 262, EPD_DIRECTION_Y, EPD_SIZE_SINGLE, EPD_COLOR_RED);
epdpr("Channel - Quality");
epdPrintEnd();
loadRawBitmap(receive, 36, 24, EPD_COLOR_BLACK);
#endif
#if (SCREEN_WIDTH == 152) // 1.54"
loadRawBitmap(receive, 96, 28, EPD_COLOR_BLACK);
epdPrintBegin(3, 0, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
epdpr("Scanning...");
epdPrintEnd();
#endif
draw();
selectLUT(EPD_LUT_FAST);
resultcounter = 0;
}
void addScanResult(uint8_t channel, uint8_t lqi) {
if (channel == 11) resultcounter = 0;
#if (SCREEN_WIDTH == 128)
epdPrintBegin(56 + ((resultcounter % 4) * 16), 282 - (47 * (resultcounter / 4)), EPD_DIRECTION_Y, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
epdpr("%d-%d", channel, lqi);
epdPrintEnd();
#endif
#if (SCREEN_WIDTH == 152) // 1.54"
epdPrintBegin(4 + (47 * (resultcounter / 8)), 31 + (15 * (resultcounter % 8)), EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
epdpr("%d-%d", channel, lqi);
epdPrintEnd();
#endif
resultcounter++;
}
void showAPFound() {
selectLUT(EPD_LUT_FAST_NO_REDS);
clearScreen();
#if (SCREEN_WIDTH == 128)
epdPrintBegin(0, 285, EPD_DIRECTION_Y, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
epdpr("Waiting for data...");
epdPrintEnd();
epdPrintBegin(48, 278, EPD_DIRECTION_Y, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
epdpr("Found the following AP:");
epdPrintEnd();
epdPrintBegin(64, 293, EPD_DIRECTION_Y, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
epdpr("AP MAC: %02X:%02X", APmac[7], APmac[6]);
epdpr(":%02X:%02X", APmac[5], APmac[4]);
epdpr(":%02X:%02X", APmac[3], APmac[2]);
epdpr(":%02X:%02X", APmac[1], APmac[0]);
epdPrintEnd();
epdPrintBegin(80, 293, EPD_DIRECTION_Y, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
epdpr("Ch: %d RSSI: %d LQI: %d", currentChannel, mLastRSSI, mLastLqi);
epdPrintEnd();
epdPrintBegin(103, 258, EPD_DIRECTION_Y, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
epdpr("Tag MAC: %02X:%02X", mSelfMac[7], mSelfMac[6]);
epdpr(":%02X:%02X", mSelfMac[5], mSelfMac[4]);
epdpr(":%02X:%02X", mSelfMac[3], mSelfMac[2]);
epdpr(":%02X:%02X", mSelfMac[1], mSelfMac[0]);
epdPrintEnd();
uint8_t __xdata buffer[17];
spr(buffer, "%02X%02X", mSelfMac[7], mSelfMac[6]);
spr(buffer + 4, "%02X%02X", mSelfMac[5], mSelfMac[4]);
spr(buffer + 8, "%02X%02X", mSelfMac[3], mSelfMac[2]);
spr(buffer + 12, "%02X%02X", mSelfMac[1], mSelfMac[0]);
printBarcode(buffer, 120, 253);
loadRawBitmap(receive, 36, 14, EPD_COLOR_BLACK);
#endif
#if (SCREEN_WIDTH == 152) // 1.54"
epdPrintBegin(25, 0, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
epdpr("Waiting");
epdPrintEnd();
epdPrintBegin(3, 32, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
epdpr("for data...");
epdPrintEnd();
epdPrintBegin(5, 64, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
epdpr("AP MAC:");
epdPrintEnd();
epdPrintBegin(5, 80, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
epdpr("%02X%02X", APmac[7], APmac[6]);
epdpr("%02X%02X", APmac[5], APmac[4]);
epdpr("%02X%02X", APmac[3], APmac[2]);
epdpr("%02X%02X", APmac[1], APmac[0]);
epdPrintEnd();
epdPrintBegin(5, 96, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
epdpr("Ch:%d rssi:%d lqi:%d", currentChannel, mLastRSSI, mLastLqi);
epdPrintEnd();
epdPrintBegin(5, 120, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
epdpr("Tag MAC:");
epdPrintEnd();
epdPrintBegin(5, 136, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_RED);
epdpr("%02X%02X", mSelfMac[7], mSelfMac[6]);
epdpr("%02X%02X", mSelfMac[5], mSelfMac[4]);
epdpr("%02X%02X", mSelfMac[3], mSelfMac[2]);
epdpr("%02X%02X", mSelfMac[1], mSelfMac[0]);
epdPrintEnd();
#endif
draw();
}
void showNoAP() {
selectLUT(EPD_LUT_NO_REPEATS);
clearScreen();
#if (SCREEN_WIDTH == 128) // 1.54"
epdPrintBegin(0, 285, EPD_DIRECTION_Y, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
epdpr("No AP found :(");
epdPrintEnd();
epdPrintBegin(48, 285, EPD_DIRECTION_Y, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
epdpr("We'll try again in a");
epdPrintEnd();
epdPrintBegin(64, 285, EPD_DIRECTION_Y, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
epdpr("little while...");
epdPrintEnd();
loadRawBitmap(receive, 36, 24, EPD_COLOR_BLACK);
loadRawBitmap(failed, 42, 26, EPD_COLOR_RED);
#endif
#if (SCREEN_WIDTH == 152) // 1.54"
epdPrintBegin(40, 0, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
epdpr("No AP");
epdPrintEnd();
epdPrintBegin(22, 32, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
epdpr("found :(");
epdPrintEnd();
epdPrintBegin(8, 76, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
epdpr("We'll try again in");
epdPrintEnd();
epdPrintBegin(25, 92, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_BLACK);
epdpr("a little while");
epdPrintEnd();
#endif
draw();
}

View File

@@ -1,9 +1,11 @@
#ifndef _UI_H_
#define _UI_H_
#include <stdint.h>
void showSplashScreen();
void showApplyUpdate();
void showScanningWindow();
void addScanResult(uint8_t channel, uint8_t lqi);
void showAPFound();
void showNoAP();
#endif