diff --git a/esp32_fw/data/index.html b/esp32_fw/data/index.html
index ea1ffddd..9cd7fc7a 100644
--- a/esp32_fw/data/index.html
+++ b/esp32_fw/data/index.html
@@ -18,12 +18,12 @@
✖
00000000
-
-
+
+
-
-
-
-
+
+
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+ Currently active tags:
+
@@ -96,9 +68,10 @@
-
- last:
+
+
+
⚠
diff --git a/esp32_fw/data/kat-bw29.jpg b/esp32_fw/data/kat-bw29.jpg
new file mode 100644
index 00000000..ae6c765e
Binary files /dev/null and b/esp32_fw/data/kat-bw29.jpg differ
diff --git a/esp32_fw/data/main.css b/esp32_fw/data/main.css
index f7ae66c3..183074a4 100644
--- a/esp32_fw/data/main.css
+++ b/esp32_fw/data/main.css
@@ -82,7 +82,7 @@ input {
input[type=button] {
border: 0px;
- padding: 4px;
+ padding: 4px 10px;
cursor:pointer;
}
input[type=button]:hover {
@@ -113,6 +113,15 @@ select {
font-weight: bold;
}
+#configbox input {
+ border: solid 1px #666666;
+ padding: 4px;
+}
+
+#configbox label {
+ text-transform: capitalize;
+}
+
#cfgdelete {
position: absolute;
bottom: 15px;
@@ -167,6 +176,10 @@ select {
background-color: #dddddd;
}
+.tagcard .pending {
+ padding-bottom:15px;
+}
+
.currimg {
float: right;
}
diff --git a/esp32_fw/data/main.js b/esp32_fw/data/main.js
index 64410a45..bf4c1890 100644
--- a/esp32_fw/data/main.js
+++ b/esp32_fw/data/main.js
@@ -1,21 +1,35 @@
const $ = document.querySelector.bind(document);
-const contentModes = ["static image", "current date", "count days", "count hours","current weather","public transport","memo text"];
-const models = ["unknown", "1.54\" 152x152px", "2.9\" 296x128px", "4.2\" 400x300px"];
+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 () {
- fetch("/get_db")
+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('Error: ' + error));
-});
-
+ //.catch(error => showMessage('loadTags error: ' + error));
+}
function connect() {
socket = new WebSocket("ws://" + location.host + "/ws");
@@ -35,6 +49,7 @@ function connect() {
}
if (msg.sys) {
$('#sysinfo').innerHTML = 'free heap: ' + msg.sys.heap + ' bytes ┇ db size: ' + msg.sys.dbsize + ' bytes ┇ db record count: ' + msg.sys.recordcount + ' ┇ littlefs free: ' + msg.sys.littlefsfree + ' bytes';
+ servertimediff = (Date.now() / 1000) - msg.sys.currtime;
}
});
@@ -69,52 +84,51 @@ function processTags(tagArray) {
if (!alias) alias = tagmac;
$('#tag' + tagmac + ' .alias').innerHTML = alias;
- var img = $('#tag' + tagmac + ' .tagimg');
- img.style.display = 'block';
- img.src = '/edit?edit=current/' + tagmac + '.bmp&' + (new Date()).getTime();
+ 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];
- var date = new Date(element.nextupdate * 1000);
- var options = { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false };
- $('#tag' + tagmac + ' .nextupdate').innerHTML = date.toLocaleString('nl-NL', options).replace(',', '');
+ 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 = "";
+ }
- date = new Date(element.lastseen * 1000);
- var options = { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false};
- $('#tag' + tagmac + ' .lastseen').innerHTML = date.toLocaleString('nl-NL', options).replace(',', '');
+ if (element.nextcheckin > 1672531200) {
+ div.dataset.nextcheckin = element.nextcheckin;
+ } else {
+ div.dataset.nextcheckin = element.lastseen + 1800;
+ }
div.dataset.lastseen = element.lastseen;
- div.dataset.lastseenlocaltime = Date.now();
+ div.dataset.hash = element.hash;
$('#tag' + tagmac + ' .warningicon').style.display = 'none';
- if (element.pending) $('#tag' + tagmac + ' .pending').innerHTML = "pending..."; else $('#tag' + tagmac + ' .pending').innerHTML = "";
+ if (element.pending) $('#tag' + tagmac + ' .pending').innerHTML = "pending update..."; else $('#tag' + tagmac + ' .pending').innerHTML = "";
}
}
function updatecards() {
document.querySelectorAll('[data-mac]').forEach(item => {
- var tagmac = item.dataset.mac;
- var idletime = Date.now() - item.dataset.lastseenlocaltime;
- $('#tag' + tagmac + ' .idletime').innerHTML = int(idletime);
- if (idletime > 300000) $('#tag' + tagmac + ' .warningicon').style.display='inline-block';
- if (idletime > 1800000) $('#tag' + tagmac).style.display = 'none';
- })
-}
+ let tagmac = item.dataset.mac;
-$('#send_image').onclick = function() {
- let formData = new FormData();
- formData.append("dst", $("#dstmac").value);
- formData.append("filename", $("#imgfile").value);
- formData.append("ttl", $("#ttl").value);
- fetch("/send_image", {
- method: "POST",
- body: formData
+ 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));
+ }
})
- .then(response => response.text())
- .then(data => showMessage(data))
- .catch(error => showMessage('Error: ' + error));
}
$('#send_fw').onclick = function () {
@@ -130,18 +144,6 @@ $('#send_fw').onclick = function () {
.catch(error => showMessage('Error: ' + error));
}
-$('#req_checkin').onclick = function () {
- let formData = new FormData();
- formData.append("dst", $("#dstmac").value);
- fetch("/req_checkin", {
- method: "POST",
- body: formData
- })
- .then(response => response.text())
- .then(data => showMessage(data))
- .catch(error => showMessage('Error: ' + error));
-}
-
$('#clearlog').onclick = function () {
$('#messages').innerHTML='';
}
@@ -176,6 +178,8 @@ $('#taglist').addEventListener("click", (event) => {
$('#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));
@@ -183,11 +187,20 @@ $('#taglist').addEventListener("click", (event) => {
})
$('#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", $('#cfgcontent').value);
+ formData.append("contentmode", contentmode);
formData.append("model", $('#cfgmodel').value);
+ formData.append("modecfgjson", JSON.stringify(obj));
fetch("/save_cfg", {
method: "POST",
body: formData
@@ -202,6 +215,30 @@ $('#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) {
const messages = $('#messages');
var date = new Date(),
@@ -213,4 +250,37 @@ function htmlEncode(input) {
const textArea = document.createElement("textarea");
textArea.innerText = input;
return textArea.innerHTML.split("
").join("\n");
-}
\ No newline at end of file
+}
+
+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')}`;
+}
diff --git a/esp32_fw/data/numbers1-1.vlw b/esp32_fw/data/numbers1-1.vlw
new file mode 100644
index 00000000..e5e2e87a
Binary files /dev/null and b/esp32_fw/data/numbers1-1.vlw differ
diff --git a/esp32_fw/data/numbers1-2.vlw b/esp32_fw/data/numbers1-2.vlw
new file mode 100644
index 00000000..c6f615be
Binary files /dev/null and b/esp32_fw/data/numbers1-2.vlw differ
diff --git a/esp32_fw/data/numbers2-1.vlw b/esp32_fw/data/numbers2-1.vlw
new file mode 100644
index 00000000..1dbfa21b
Binary files /dev/null and b/esp32_fw/data/numbers2-1.vlw differ
diff --git a/esp32_fw/data/numbers2-2.vlw b/esp32_fw/data/numbers2-2.vlw
new file mode 100644
index 00000000..60ade58b
Binary files /dev/null and b/esp32_fw/data/numbers2-2.vlw differ
diff --git a/esp32_fw/data/numbers3-1.vlw b/esp32_fw/data/numbers3-1.vlw
new file mode 100644
index 00000000..fff661e3
Binary files /dev/null and b/esp32_fw/data/numbers3-1.vlw differ
diff --git a/esp32_fw/data/numbers3-2.vlw b/esp32_fw/data/numbers3-2.vlw
new file mode 100644
index 00000000..1dbfa21b
Binary files /dev/null and b/esp32_fw/data/numbers3-2.vlw differ
diff --git a/esp32_fw/include/commstructs.h b/esp32_fw/include/commstructs.h
index bf1f2a24..ac93dba7 100644
--- a/esp32_fw/include/commstructs.h
+++ b/esp32_fw/include/commstructs.h
@@ -56,4 +56,6 @@ struct pendingData {
} __packed;
#define BLOCK_DATA_SIZE 4096
-#define BLOCK_XFER_BUFFER_SIZE BLOCK_DATA_SIZE + sizeof(struct blockData)
\ No newline at end of file
+#define BLOCK_XFER_BUFFER_SIZE BLOCK_DATA_SIZE + sizeof(struct blockData)
+
+#pragma pack(pop)
\ No newline at end of file
diff --git a/esp32_fw/include/contentmanager.h b/esp32_fw/include/contentmanager.h
new file mode 100644
index 00000000..64fe7a25
--- /dev/null
+++ b/esp32_fw/include/contentmanager.h
@@ -0,0 +1,15 @@
+#include
+
+#include
+#include "makeimage.h"
+#include
+#include "tag_db.h"
+#include
+
+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);
\ No newline at end of file
diff --git a/esp32_fw/include/makeimage.h b/esp32_fw/include/makeimage.h
index 71d747ef..3ce9ef15 100644
--- a/esp32_fw/include/makeimage.h
+++ b/esp32_fw/include/makeimage.h
@@ -1,6 +1,8 @@
#include
#include
+#pragma once
+
struct BitmapFileHeader {
uint8_t sig[2];
uint32_t fileSz;
@@ -27,7 +29,6 @@ enum EinkClut {
EinkClutThreeBlacksAndRed,
};
-void tftinit();
void spr2grays(TFT_eSprite &spr, long w, long h, String fileout);
void jpg2grays(String filein, String fileout);
void bmp2grays(String filein, String fileout);
diff --git a/esp32_fw/include/pendingdata.h b/esp32_fw/include/pendingdata.h
index b4116bfb..1bbc1e9f 100644
--- a/esp32_fw/include/pendingdata.h
+++ b/esp32_fw/include/pendingdata.h
@@ -21,3 +21,5 @@ class pendingdata {
void garbageCollection(void* parameter);
extern std::vector pendingfiles;
+
+#pragma pack(pop)
\ No newline at end of file
diff --git a/esp32_fw/include/tag_db.h b/esp32_fw/include/tag_db.h
index 2d44313a..3c6a77c2 100644
--- a/esp32_fw/include/tag_db.h
+++ b/esp32_fw/include/tag_db.h
@@ -1,4 +1,5 @@
#include
+#include
#include
@@ -11,26 +12,36 @@ enum contentModes {
CountDays,
CountHours,
Weather,
- PubTrans,
+ Firmware,
Memo,
+ ImageUrl,
};
class tagRecord {
- public:
- tagRecord() : mac{0}, model(0), alias(""), lastseen(0), nextupdate(0), contentMode(Image), pending(false), button(false), currFilename(""), pendingFilename("") {}
+ 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;
- String currFilename;
- String pendingFilename;
- static tagRecord* findByMAC(uint8_t mac[6]);
+ 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 tagDB;
-String tagDBtoJson(uint8_t mac[6] = nullptr);
\ No newline at end of file
+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)
\ No newline at end of file
diff --git a/esp32_fw/include/web.h b/esp32_fw/include/web.h
index 71c40f5c..7badb987 100644
--- a/esp32_fw/include/web.h
+++ b/esp32_fw/include/web.h
@@ -1,11 +1,12 @@
-#include
+
+#include
#include
#include
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 webSocketSendProcess(void *parameter);
void wsString(String text);
void wsSendTaginfo(uint8_t mac[6]);
void wsSendSysteminfo();
diff --git a/esp32_fw/src/contentmanager.cpp b/esp32_fw/src/contentmanager.cpp
new file mode 100644
index 00000000..1558159a
--- /dev/null
+++ b/esp32_fw/src/contentmanager.cpp
@@ -0,0 +1,228 @@
+#include "contentmanager.h"
+
+#include
+#include
+#include
+#include "newproto.h"
+#include
+#include
+#include
+
+#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);
+ struct tm *time_info = gmtime(&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";
+
+ 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();
+
+ switch (taginfo->contentMode) {
+ case Image:
+
+ filename = cfgobj["filename"].as();
+ if (filename && filename !="null" && !cfgobj["#fetched"].as()) {
+ if (prepareDataAvail(&filename, DATATYPE_IMGRAW, mac, cfgobj["timetolive"].as())) {
+ cfgobj["#fetched"] = true;
+ } else {
+ wsString("Error accessing " + filename);
+ }
+ taginfo->nextupdate = 3216153600;
+ }
+ break;
+
+ case Today:
+
+ drawDate(filename);
+ updateTagImage(filename, mac, (midnight - now) / 60 - 10);
+ taginfo->nextupdate = midnight;
+ break;
+
+ case CountDays:
+
+ if (buttonPressed) cfgobj["counter"] = 0;
+ drawNumber(filename, (int32_t)cfgobj["counter"], (int32_t)cfgobj["thresholdred"]);
+ updateTagImage(filename, mac, (midnight - now) / 60 - 5);
+ 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, 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();
+ if (filename && filename != "null" && !cfgobj["#fetched"].as()) {
+ if (prepareDataAvail(&filename, DATATYPE_UPDATE, mac, cfgobj["timetolive"].as())) {
+ cfgobj["#fetched"] = true;
+ } else {
+ wsString("Error accessing " + filename);
+ }
+ taginfo->nextupdate = 3216153600;
+ taginfo->contentMode = Image;
+ }
+ break;
+
+ case Memo:
+ break;
+ case ImageUrl:
+
+ if (getImgURL(filename, cfgobj["url"], (time_t)cfgobj["#fetched"])) {
+ updateTagImage(filename, mac, cfgobj["interval"].as());
+ cfgobj["#fetched"] = now;
+ }
+ taginfo->nextupdate = now + 60 * cfgobj["interval"].as();
+ break;
+ }
+
+ taginfo->modeConfigJson = doc.as();
+}
+
+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);
+ spr.setColorDepth(8);
+ spr.fillSprite(TFT_WHITE);
+ spr.setTextDatum(TC_DATUM);
+ spr.loadFont("calibrib62", LittleFS);
+ spr.setTextColor(TFT_RED, TFT_WHITE);
+ spr.drawString(Dag[timeinfo.tm_wday], w / 2, 10);
+ spr.loadFont("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);
+ 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 = "numbers1-2";
+ if (count>999) font="numbers2-2";
+ if (count>9999) font="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 {
+ Serial.println("http " + 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;
+}
diff --git a/esp32_fw/src/main.cpp b/esp32_fw/src/main.cpp
index 71f04968..e46ea598 100644
--- a/esp32_fw/src/main.cpp
+++ b/esp32_fw/src/main.cpp
@@ -3,6 +3,7 @@
#include
#include
+#include "contentmanager.h"
#include "flasher.h"
#include "makeimage.h"
#include "pendingdata.h"
@@ -11,31 +12,35 @@
#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());
- //time_t now;
+ time_t now;
+ time(&now);
tm tm;
if (!getLocalTime(&tm)) {
Serial.println("Failed to obtain time");
+ } else {
+ if (now % 10 == 0) wsSendSysteminfo();
+ contentRunner();
}
- wsSendSysteminfo();
- vTaskDelay(30000 / portTICK_PERIOD_MS);
+ vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void setup() {
Serial.begin(115200);
Serial.print(">\n");
- init_web();
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
- xTaskCreate(freeHeapTask, "print free heap", 10000, NULL, 2, NULL);
+ init_web();
+ loadDB("/tagDB.json");
+
+ 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);
+ xTaskCreate(webSocketSendProcess, "ws", 5000, NULL,configMAX_PRIORITIES-10, NULL);
}
void loop() {
diff --git a/esp32_fw/src/makeimage.cpp b/esp32_fw/src/makeimage.cpp
index ff01570a..90cc66ee 100644
--- a/esp32_fw/src/makeimage.cpp
+++ b/esp32_fw/src/makeimage.cpp
@@ -8,28 +8,6 @@
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite spr = TFT_eSprite(&tft);
-void tftinit() {
- //tijdelijk: voorbeeld voor aanmaak van plaatje
-
- LittleFS.begin();
- long w = 296, h = 128; // mag staand of liggend
- spr.createSprite(w, h);
- spr.setColorDepth(8);
- spr.fillSprite(TFT_WHITE);
- spr.setTextDatum(TC_DATUM);
- spr.loadFont("calibrib62", LittleFS);
- spr.setTextColor(TFT_RED, TFT_WHITE);
- spr.drawString("zondag", w / 2, 10);
- spr.loadFont("calibrib50", LittleFS);
- spr.setTextColor(TFT_BLACK, TFT_WHITE);
- spr.drawString("29 januari", w / 2, 73);
- spr.unloadFont();
-
- spr2grays(spr, w, h, "/testspr3.bmp");
-
- spr.deleteSprite();
-}
-
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;
@@ -40,6 +18,7 @@ void jpg2grays(String filein, String fileout) {
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);
spr.setColorDepth(8);
diff --git a/esp32_fw/src/newproto.cpp b/esp32_fw/src/newproto.cpp
index 64f48fd7..297dd2b1 100644
--- a/esp32_fw/src/newproto.cpp
+++ b/esp32_fw/src/newproto.cpp
@@ -1,4 +1,3 @@
-#pragma pack(push, 1)
#include "newproto.h"
#include
@@ -51,6 +50,12 @@ 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;
fs::File file = LittleFS.open(*filename);
@@ -101,6 +106,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) {
+ wsString("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);
@@ -124,21 +143,23 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t
pendinginfo->timeout = 1800;
pendingfiles.push_back(pendinginfo);
- 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]);
- file = LittleFS.open(dst_path, "w");
- int bytes_written = file.write(pendinginfo->data, pendinginfo->len);
- file.close();
+ 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]);
+ file = LittleFS.open(dst_path, "w");
+ int bytes_written = file.write(pendinginfo->data, pendinginfo->len);
+ file.close();
- 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) {
- taginfo->pending = true;
+ wsString("new image pending: " + String(dst_path));
+ if (taginfo != nullptr) {
+ taginfo->pending = true;
+ taginfo->CheckinInMinPending = nextCheckin + 1;
+ memcpy(taginfo->md5pending, md5bytes, sizeof(md5bytes));
+ }
+ } else {
+ Serial.println("firmware upload pending");
}
+
wsSendTaginfo(mac);
return true;
@@ -196,23 +217,30 @@ void processXferComplete(struct espXferComplete* xfc) {
uint8_t mac[6];
memcpy(mac, src + 2, sizeof(mac));
- tagRecord* taginfo = nullptr;
- taginfo = tagRecord::findByMAC(mac);
- if (taginfo != nullptr) {
- taginfo->pending = false;
- }
- wsSendTaginfo(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) {
@@ -225,19 +253,20 @@ void processDataReq(struct espAvailDataReq* eadr) {
memcpy(mac, src + 2, sizeof(mac));
taginfo = tagRecord::findByMAC(mac);
- time_t now;
- time(&now);
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, "
#include
#include
diff --git a/esp32_fw/src/tag_db.cpp b/esp32_fw/src/tag_db.cpp
index 162ec82c..e1af5f24 100644
--- a/esp32_fw/src/tag_db.cpp
+++ b/esp32_fw/src/tag_db.cpp
@@ -1,9 +1,12 @@
-#include
-
#include "tag_db.h"
+
+#include
#include
+
#include
+#include "LittleFS.h"
+
std::vector tagDB;
tagRecord* tagRecord::findByMAC(uint8_t mac[6]) {
@@ -17,35 +20,149 @@ tagRecord* tagRecord::findByMAC(uint8_t mac[6]) {
return nullptr;
}
-String tagDBtoJson(uint8_t mac[6]) {
- DynamicJsonDocument doc(1000);
+String tagDBtoJson(uint8_t mac[6], uint8_t startPos) {
+ DynamicJsonDocument doc(2500);
JsonArray tags = doc.createNestedArray("tags");
- for (int16_t c = 0; c < tagDB.size(); c++) {
+ for (int16_t c = startPos; c < tagDB.size(); c++) {
tagRecord* taginfo = nullptr;
taginfo = tagDB.at(c);
- bool select = false;
- if (mac) {
+ bool select = false;
+ if (mac) {
if (memcmp(taginfo->mac, mac, 6) == 0) {
select = true;
}
} else {
- select = true;
- }
- if (select) {
- JsonObject tag = tags.createNestedObject();
- char buffer[64];
- 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;
- tag["lastseen"] = taginfo->lastseen;
- tag["nextupdate"] = taginfo->nextupdate;
- tag["model"] = taginfo->model;
- tag["pending"] = taginfo->pending;
- tag["button"] = taginfo->button;
- tag["alias"] = taginfo->alias;
- tag["contentmode"] = taginfo->contentMode;
+ 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();
-}
\ No newline at end of file
+}
+
+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);
+
+ Serial.println("start writing DB to file");
+ 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 file");
+
+ 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();
+ 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();
+ taginfo->contentMode = static_cast(tag["contentmode"]);
+ taginfo->modeConfigJson = tag["modecfgjson"].as();
+ }
+ } 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;
+}
diff --git a/esp32_fw/src/web.cpp b/esp32_fw/src/web.cpp
index 3eb35563..5b8618a3 100644
--- a/esp32_fw/src/web.cpp
+++ b/esp32_fw/src/web.cpp
@@ -2,6 +2,7 @@
#include
#include
+
#include
#include
#include
@@ -54,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>(len);
serializeJson(doc, buffer->data(), len);
// ws.textAll((char*)buffer->data());
- //ws.textAll("ohai");
xSemaphoreGive(wsMutex);
}
}
@@ -214,28 +168,14 @@ void wsSendSysteminfo() {
}
void wsSendTaginfo(uint8_t mac[6]) {
- DynamicJsonDocument doc(1000);
- JsonArray tags = doc.createNestedArray("tags");
- JsonObject tag = tags.createNestedObject();
- char buffer[64];
- sprintf(buffer, "%02X%02X%02X%02X%02X%02X\0", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
- tag["mac"] = (String)buffer;
- tagRecord *taginfo = nullptr;
- taginfo = tagRecord::findByMAC(mac);
- if (taginfo != nullptr) {
- tag["lastseen"] = taginfo->lastseen;
- tag["nextupdate"] = taginfo->nextupdate;
- tag["model"] = taginfo->model;
- tag["pending"] = taginfo->pending;
- tag["button"] = taginfo->button;
- tag["alias"] = taginfo->alias;
- tag["contentmode"] = taginfo->contentMode;
- }
+ String json = "";
+ json = tagDBtoJson(mac);
xSemaphoreTake(wsMutex, portMAX_DELAY);
- ws.textAll(doc.as());
+ ws.textAll(json);
xSemaphoreGive(wsMutex);
+
}
void init_web() {
@@ -282,70 +222,6 @@ void init_web() {
},
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;
@@ -383,8 +259,12 @@ void init_web() {
json = tagDBtoJson(mac);
}
} else {
- json = tagDBtoJson();
- }
+ 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);
});
@@ -397,9 +277,12 @@ void init_web() {
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("/tagDB.json");
request->send(200, "text/plain", "Ok, saved");
} else {
request->send(200, "text/plain", "Error while saving: mac not found");
@@ -414,55 +297,6 @@ void init_web() {
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);
});