mirror of
https://github.com/OpenEPaperLink/OpenEPaperLink.git
synced 2026-03-21 06:06:23 +01:00
@@ -60,7 +60,7 @@
|
||||
|
||||
<div id="taglist" class="taglist">
|
||||
<div class="tagcard" id="tagtemplate">
|
||||
<div class="currimg"><img class="tagimg" src=""></div>
|
||||
<div class="currimg"><canvas class="tagimg"></div>
|
||||
<div class="mac"></div>
|
||||
<div class="alias"></div>
|
||||
<div class="model"></div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
*{
|
||||
* {
|
||||
margin:0;
|
||||
padding:0;
|
||||
border:0;
|
||||
@@ -40,7 +40,6 @@ label {
|
||||
height: 50px;
|
||||
text-indent: 50px;
|
||||
overflow:hidden;
|
||||
background-size: 50px 50px;
|
||||
font-size: 2.5em;
|
||||
color: white;
|
||||
}
|
||||
@@ -84,9 +83,20 @@ label {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
textarea,
|
||||
input.text,
|
||||
input[type="text"],
|
||||
input[type="button"],
|
||||
input[type="submit"],
|
||||
button {
|
||||
-webkit-appearance: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
input {
|
||||
border: solid 1px #666666;
|
||||
padding: 4px;
|
||||
-webkit-border-radius: 0px;
|
||||
}
|
||||
|
||||
input[type=button] {
|
||||
@@ -99,6 +109,7 @@ input[type=button]:hover {
|
||||
}
|
||||
select {
|
||||
padding: 4px;
|
||||
-webkit-border-radius: 0px;
|
||||
}
|
||||
|
||||
#configbox {
|
||||
@@ -179,7 +190,7 @@ select {
|
||||
.tagcard {
|
||||
width: 225px;
|
||||
position: relative;
|
||||
height: 170px;
|
||||
min-height: 170px;
|
||||
margin: 5px;
|
||||
padding: 5px;
|
||||
background-color: #dddddd;
|
||||
@@ -203,7 +214,7 @@ select {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.currimg img {
|
||||
.currimg img, .currimg canvas {
|
||||
max-width: 50px;
|
||||
}
|
||||
|
||||
@@ -326,3 +337,46 @@ ul.messages li.new {
|
||||
50% { background-color: lightblue;}
|
||||
100% { background-color: lightgray;}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
/* styles for mobile devices in portrait mode */
|
||||
|
||||
body {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.tagcard {
|
||||
width: 100%;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.logbox #sysinfo {
|
||||
float: none;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#configbox {
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.currimg {
|
||||
float: none;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
}
|
||||
|
||||
.currimg img,
|
||||
.currimg canvas {
|
||||
max-width: 60px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
padding-top: 10px;
|
||||
text-indent: 0px;
|
||||
font-size: 1.8em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,6 +10,8 @@ const WAKEUP_REASON_WDT_RESET = 0xFE;
|
||||
|
||||
const contentModes = ["Static image", "Current date", "Counting days", "Counting hours", "Current weather", "Firmware update", "Memo text", "Image url", "Weather forecast","RSS feed"];
|
||||
const models = ["1.54\" 152x152px", "2.9\" 296x128px", "4.2\" 400x300px"];
|
||||
const displaySizeLookup = { 0: [152, 152], 1: [128, 296], 2: [400, 300] };
|
||||
const colorTable = { 0: [255, 255, 255], 1: [0, 0, 0], 2: [255, 0, 0], 3: [255, 0, 0] };
|
||||
const contentModeOptions = [];
|
||||
contentModeOptions[0] = ["filename","timetolive"];
|
||||
contentModeOptions[1] = [];
|
||||
@@ -63,6 +65,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;
|
||||
console.log("timediff: " + servertimediff);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -82,13 +85,10 @@ function processTags(tagArray) {
|
||||
div = $('#tagtemplate').cloneNode(true);
|
||||
div.setAttribute('id', 'tag'+tagmac);
|
||||
div.dataset.mac = tagmac;
|
||||
div.dataset.hwtype = -1;
|
||||
$('#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';
|
||||
@@ -97,10 +97,9 @@ function processTags(tagArray) {
|
||||
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];
|
||||
if (element.RSSI) {
|
||||
div.dataset.hwtype = element.hwType;
|
||||
$('#tag' + tagmac + ' .model').innerHTML = models[element.hwType];
|
||||
$('#tag' + tagmac + ' .rssi').innerHTML = element.RSSI;
|
||||
$('#tag' + tagmac + ' .lqi').innerHTML = element.LQI;
|
||||
@@ -112,6 +111,11 @@ function processTags(tagArray) {
|
||||
$('#tag' + tagmac + ' .received').style.opacity = "0";
|
||||
}
|
||||
|
||||
if (div.dataset.hash != element.hash && div.dataset.hwtype > -1) {
|
||||
loadImage(tagmac, '/current/' + tagmac + '.raw?' + (new Date()).getTime());
|
||||
div.dataset.hash = element.hash;
|
||||
}
|
||||
|
||||
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 };
|
||||
@@ -130,7 +134,6 @@ function processTags(tagArray) {
|
||||
$('#tag' + tagmac + ' .lastseen').style.color = "black";
|
||||
div.classList.remove("tagpending");
|
||||
div.dataset.lastseen = element.lastseen;
|
||||
div.dataset.hash = element.hash;
|
||||
div.dataset.wakeupreason = element.wakeupReason;
|
||||
$('#tag' + tagmac + ' .warningicon').style.display = 'none';
|
||||
$('#tag' + tagmac).style.background = "inherit";
|
||||
@@ -174,9 +177,9 @@ function updatecards() {
|
||||
let tagmac = item.dataset.mac;
|
||||
|
||||
if (item.dataset.lastseen && item.dataset.lastseen > 1672531200) {
|
||||
let idletime = (Date.now() / 1000) + servertimediff - item.dataset.lastseen;
|
||||
let idletime = (Date.now() / 1000) - servertimediff - item.dataset.lastseen;
|
||||
$('#tag' + tagmac + ' .lastseen').innerHTML = "<span>last seen</span>"+displayTime(Math.floor(idletime))+" ago";
|
||||
if ((Date.now() / 1000) + servertimediff - 300 > item.dataset.nextcheckin) {
|
||||
if ((Date.now() / 1000) - servertimediff - 300 > item.dataset.nextcheckin) {
|
||||
$('#tag' + tagmac + ' .warningicon').style.display='inline-block';
|
||||
$('#tag' + tagmac).classList.remove("tagpending")
|
||||
$('#tag' + tagmac).style.background = '#ffffcc';
|
||||
@@ -190,7 +193,7 @@ function updatecards() {
|
||||
}
|
||||
|
||||
if (item.dataset.nextcheckin > 1672531200 && parseInt(item.dataset.wakeupreason)==0) {
|
||||
let nextcheckin = item.dataset.nextcheckin - ((Date.now() / 1000) + servertimediff);
|
||||
let nextcheckin = item.dataset.nextcheckin - ((Date.now() / 1000) - servertimediff);
|
||||
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "<span>expected checkin</span>" + displayTime(Math.floor(nextcheckin));
|
||||
}
|
||||
})
|
||||
@@ -338,16 +341,36 @@ function processQueue() {
|
||||
}
|
||||
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;
|
||||
const canvas = $('#tag' + id + ' .tagimg');
|
||||
const hwtype = $('#tag' + id).dataset.hwtype;
|
||||
|
||||
fetch(imageSrc)
|
||||
.then(response => response.arrayBuffer())
|
||||
.then(buffer => {
|
||||
[canvas.width, canvas.height] = displaySizeLookup[hwtype] || [0,0];
|
||||
const ctx = canvas.getContext('2d');
|
||||
const imageData = ctx.createImageData(canvas.width, canvas.height);
|
||||
const data = new Uint8ClampedArray(buffer);
|
||||
const offsetRed = (data.length >= (canvas.width * canvas.height / 8) * 2) ? canvas.width * canvas.height / 8 : 0;
|
||||
var pixelValue = 0;
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
for (let j = 0; j < 8; j++) {
|
||||
const pixelIndex = i * 8 + j;
|
||||
if (offsetRed) {
|
||||
pixelValue = ((data[i] & (1 << (7 - j))) ? 1 : 0) | (((data[i + offsetRed] & (1 << (7 - j))) ? 1 : 0) << 1);
|
||||
} else {
|
||||
pixelValue = ((data[i] & (1 << (7 - j))) ? 1 : 0);
|
||||
}
|
||||
imageData.data[pixelIndex * 4] = colorTable[pixelValue][0];
|
||||
imageData.data[pixelIndex * 4 + 1] = colorTable[pixelValue][1];
|
||||
imageData.data[pixelIndex * 4 + 2] = colorTable[pixelValue][2];
|
||||
imageData.data[pixelIndex * 4 + 3] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
ctx.putImageData(imageData, 0, 0);
|
||||
processQueue();
|
||||
});
|
||||
}
|
||||
|
||||
function displayTime(seconds) {
|
||||
|
||||
@@ -47,9 +47,12 @@ struct espAvailDataReq {
|
||||
} __packed;
|
||||
|
||||
#define DATATYPE_NOUPDATE 0
|
||||
#define DATATYPE_IMG 1
|
||||
#define DATATYPE_IMGRAW 2
|
||||
#define DATATYPE_UPDATE 3
|
||||
#define DATATYPE_IMG_BMP 2
|
||||
#define DATATYPE_FW_UPDATE 3
|
||||
#define DATATYPE_IMG_DIFF 0x10 // always 1BPP
|
||||
#define DATATYPE_IMG_RAW_1BPP 0x20 // 2888 bytes for 1.54" / 4736 2.9" / 15000 4.2"
|
||||
#define DATATYPE_IMG_RAW_2BPP 0x21 // 5776 bytes for 1.54" / 9472 2.9" / 30000 4.2"
|
||||
#define DATATYPE_IMG_RAW_1BPP_DIRECT 0x3F // only for 1.54", don't write to EEPROM, but straightaway to the EPD
|
||||
|
||||
#define EPD_LUT_DEFAULT 0
|
||||
#define EPD_LUT_NO_REPEATS 1
|
||||
|
||||
@@ -30,6 +30,7 @@ enum EinkClut {
|
||||
};
|
||||
|
||||
void spr2grays(TFT_eSprite &spr, long w, long h, String &fileout);
|
||||
void jpg2grays(String filein, String fileout);
|
||||
void spr2buffer(TFT_eSprite &spr, String &fileout);
|
||||
void jpg2buffer(String filein, String fileout);
|
||||
void bmp2grays(String filein, String fileout);
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
#include "makeimage.h"
|
||||
#include "web.h"
|
||||
|
||||
#define PAL_BLACK 0
|
||||
#define PAL_WHITE 9
|
||||
#define PAL_RED 2
|
||||
|
||||
enum contentModes {
|
||||
Image,
|
||||
Today,
|
||||
@@ -27,7 +31,6 @@ enum contentModes {
|
||||
RSSFeed,
|
||||
};
|
||||
|
||||
|
||||
void contentRunner() {
|
||||
time_t now;
|
||||
time(&now);
|
||||
@@ -58,7 +61,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
|
||||
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";
|
||||
String filename = "/" + dst + ".raw";
|
||||
|
||||
struct tm time_info;
|
||||
getLocalTime(&time_info);
|
||||
@@ -78,9 +81,9 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
|
||||
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>())) {
|
||||
if (cfgobj["filename"].as<String>() && cfgobj["filename"].as<String>() != "null" && !cfgobj["#fetched"].as<bool>()) {
|
||||
jpg2buffer(cfgobj["filename"].as<String>(), filename);
|
||||
if (prepareDataAvail(&filename, DATATYPE_IMG_RAW_2BPP, mac, cfgobj["timetolive"].as<int>())) {
|
||||
cfgobj["#fetched"] = true;
|
||||
} else {
|
||||
wsErr("Error accessing " + filename);
|
||||
@@ -137,7 +140,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
|
||||
|
||||
filename = cfgobj["filename"].as<String>();
|
||||
if (filename && filename != "null" && !cfgobj["#fetched"].as<bool>()) {
|
||||
if (prepareDataAvail(&filename, DATATYPE_UPDATE, mac, cfgobj["timetolive"].as<int>())) {
|
||||
if (prepareDataAvail(&filename, DATATYPE_FW_UPDATE, mac, cfgobj["timetolive"].as<int>())) {
|
||||
cfgobj["#fetched"] = true;
|
||||
} else {
|
||||
wsErr("Error accessing " + filename);
|
||||
@@ -183,26 +186,26 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
|
||||
}
|
||||
|
||||
bool updateTagImage(String &filename, uint8_t *dst, uint16_t nextCheckin) {
|
||||
prepareDataAvail(&filename, DATATYPE_IMGRAW, dst, nextCheckin);
|
||||
prepareDataAvail(&filename, DATATYPE_IMG_RAW_2BPP, dst, nextCheckin);
|
||||
return true;
|
||||
}
|
||||
|
||||
void drawString(TFT_eSprite &spr, String content, uint16_t posx, uint16_t posy, String font, byte align,uint16_t color) {
|
||||
// drawString(spr,"test",100,10,"bahnschrift30",TC_DATUM,TFT_RED);
|
||||
// drawString(spr,"test",100,10,"bahnschrift30",TC_DATUM,PAL_RED);
|
||||
spr.setTextDatum(align);
|
||||
if (font != "") spr.loadFont(font, LittleFS);
|
||||
spr.setTextColor(color, TFT_WHITE);
|
||||
spr.setTextColor(color, PAL_WHITE);
|
||||
spr.drawString(content, posx, posy);
|
||||
if (font != "") spr.unloadFont();
|
||||
}
|
||||
|
||||
void initSprite(TFT_eSprite &spr, int w, int h) {
|
||||
spr.setColorDepth(8);
|
||||
spr.setColorDepth(4); // 4 bits per pixel, uses indexed color
|
||||
spr.createSprite(w, h);
|
||||
if (spr.getPointer() == nullptr) {
|
||||
wsErr("Failed to create sprite");
|
||||
}
|
||||
spr.fillSprite(TFT_WHITE);
|
||||
spr.fillSprite(PAL_WHITE);
|
||||
}
|
||||
|
||||
void drawDate(String &filename, tagRecord *&taginfo) {
|
||||
@@ -224,18 +227,18 @@ void drawDate(String &filename, tagRecord *&taginfo) {
|
||||
if (taginfo->hwType == SOLUM_29_033) {
|
||||
|
||||
initSprite(spr,296,128);
|
||||
drawString(spr, Dag[timeinfo.tm_wday], 296 / 2, 10, "fonts/calibrib62", TC_DATUM, TFT_RED);
|
||||
drawString(spr, Dag[timeinfo.tm_wday], 296 / 2, 10, "fonts/calibrib62", TC_DATUM, PAL_RED);
|
||||
drawString(spr, String(timeinfo.tm_mday) + " " + Maand[timeinfo.tm_mon], 296 / 2, 73, "fonts/calibrib50", TC_DATUM);
|
||||
|
||||
} else if (taginfo->hwType == SOLUM_154_033) {
|
||||
|
||||
initSprite(spr, 154, 154);
|
||||
drawString(spr, Dag[timeinfo.tm_wday], 154 / 2, 10, "fonts/calibrib30", TC_DATUM);
|
||||
drawString(spr, String(Maand[timeinfo.tm_mon]), 154 / 2, 120, "fonts/calibrib30", TC_DATUM);
|
||||
drawString(spr, String(timeinfo.tm_mday), 154 / 2, 42, "fonts/numbers2-1", TC_DATUM, TFT_RED);
|
||||
initSprite(spr, 152, 152);
|
||||
drawString(spr, Dag[timeinfo.tm_wday], 152 / 2, 10, "fonts/calibrib30", TC_DATUM);
|
||||
drawString(spr, String(Maand[timeinfo.tm_mon]), 152 / 2, 120, "fonts/calibrib30", TC_DATUM);
|
||||
drawString(spr, String(timeinfo.tm_mday), 152 / 2, 42, "fonts/numbers2-1", TC_DATUM, PAL_RED);
|
||||
}
|
||||
|
||||
spr2grays(spr, spr.width(), spr.height(), filename);
|
||||
spr2buffer(spr, filename);
|
||||
spr.deleteSprite();
|
||||
}
|
||||
|
||||
@@ -250,9 +253,9 @@ void drawNumber(String &filename, int32_t count, int32_t thresholdred, tagRecord
|
||||
initSprite(spr, 296, 128);
|
||||
spr.setTextDatum(MC_DATUM);
|
||||
if (count > thresholdred) {
|
||||
spr.setTextColor(TFT_RED, TFT_WHITE);
|
||||
spr.setTextColor(PAL_RED, PAL_WHITE);
|
||||
} else {
|
||||
spr.setTextColor(TFT_BLACK, TFT_WHITE);
|
||||
spr.setTextColor(PAL_BLACK, PAL_WHITE);
|
||||
}
|
||||
String font = "fonts/numbers1-2";
|
||||
if (count > 999) font = "fonts/numbers2-2";
|
||||
@@ -263,23 +266,23 @@ void drawNumber(String &filename, int32_t count, int32_t thresholdred, tagRecord
|
||||
|
||||
} else if (taginfo->hwType == SOLUM_154_033) {
|
||||
|
||||
initSprite(spr, 154, 154);
|
||||
initSprite(spr, 152, 152);
|
||||
spr.setTextDatum(MC_DATUM);
|
||||
if (count > thresholdred) {
|
||||
spr.setTextColor(TFT_RED, TFT_WHITE);
|
||||
spr.setTextColor(PAL_RED, PAL_WHITE);
|
||||
} else {
|
||||
spr.setTextColor(TFT_BLACK, TFT_WHITE);
|
||||
spr.setTextColor(PAL_BLACK, PAL_WHITE);
|
||||
}
|
||||
String font = "fonts/numbers1-1";
|
||||
if (count > 99) font = "fonts/numbers2-1";
|
||||
if (count > 999) font = "fonts/numbers3-1";
|
||||
spr.loadFont(font, LittleFS);
|
||||
spr.drawString(String(count), 154 / 2, 154 / 2 + 10);
|
||||
spr.drawString(String(count), 152 / 2, 152 / 2 + 7);
|
||||
spr.unloadFont();
|
||||
|
||||
}
|
||||
|
||||
spr2grays(spr, spr.width(), spr.height(), filename);
|
||||
spr2buffer(spr, filename);
|
||||
spr.deleteSprite();
|
||||
}
|
||||
|
||||
@@ -347,17 +350,17 @@ void drawWeather(String &filename, String location, tagRecord *&taginfo) {
|
||||
initSprite(spr, 296, 128);
|
||||
|
||||
drawString(spr, location, 5, 5, "fonts/bahnschrift30");
|
||||
drawString(spr, String(wind), 280, 5, "fonts/bahnschrift30", TR_DATUM, (wind > 4 ? TFT_RED : TFT_BLACK));
|
||||
drawString(spr, String(wind), 280, 5, "fonts/bahnschrift30", TR_DATUM, (wind > 4 ? PAL_RED : PAL_BLACK));
|
||||
|
||||
char tmpOutput[5];
|
||||
dtostrf(temperature, 2, 1, tmpOutput);
|
||||
drawString(spr, String(tmpOutput), 5, 65, "fonts/bahnschrift70", TL_DATUM, (temperature < 0 ? TFT_RED : TFT_BLACK));
|
||||
drawString(spr, String(tmpOutput), 5, 65, "fonts/bahnschrift70", TL_DATUM, (temperature < 0 ? PAL_RED : PAL_BLACK));
|
||||
|
||||
spr.loadFont("fonts/weathericons70", LittleFS);
|
||||
if (weathercode == 55 || weathercode == 65 || weathercode == 75 || weathercode == 82 || weathercode == 86 || weathercode == 95 || weathercode == 99) {
|
||||
spr.setTextColor(TFT_RED, TFT_WHITE);
|
||||
spr.setTextColor(PAL_RED, PAL_WHITE);
|
||||
} else {
|
||||
spr.setTextColor(TFT_BLACK, TFT_WHITE);
|
||||
spr.setTextColor(PAL_BLACK, PAL_WHITE);
|
||||
}
|
||||
|
||||
spr.setCursor(185, 32);
|
||||
@@ -365,11 +368,11 @@ void drawWeather(String &filename, String location, tagRecord *&taginfo) {
|
||||
spr.unloadFont();
|
||||
|
||||
spr.loadFont("fonts/weathericons30", LittleFS);
|
||||
spr.setTextColor(TFT_BLACK, TFT_WHITE);
|
||||
spr.setTextColor(PAL_BLACK, PAL_WHITE);
|
||||
spr.setCursor(235, -3);
|
||||
spr.printToSprite(windDirectionIcon(winddirection));
|
||||
if (weathercode > 10) {
|
||||
spr.setTextColor(TFT_RED, TFT_WHITE);
|
||||
spr.setTextColor(PAL_RED, PAL_WHITE);
|
||||
spr.setCursor(190, 0);
|
||||
spr.printToSprite("\uf084");
|
||||
}
|
||||
@@ -377,36 +380,36 @@ void drawWeather(String &filename, String location, tagRecord *&taginfo) {
|
||||
|
||||
} else if (taginfo->hwType == SOLUM_154_033) {
|
||||
|
||||
initSprite(spr, 154, 154);
|
||||
initSprite(spr, 152, 152);
|
||||
spr.setTextDatum(TL_DATUM);
|
||||
|
||||
spr.setTextFont(2);
|
||||
spr.setTextColor(TFT_BLACK, TFT_WHITE);
|
||||
spr.setTextColor(PAL_BLACK, PAL_WHITE);
|
||||
spr.drawString(location, 10, 130);
|
||||
|
||||
char tmpOutput[5];
|
||||
dtostrf(temperature, 2, 1, tmpOutput);
|
||||
drawString(spr, String(tmpOutput), 10, 10, "fonts/bahnschrift30", TL_DATUM, (temperature < 0 ? TFT_RED : TFT_BLACK));
|
||||
drawString(spr, String(tmpOutput), 10, 10, "fonts/bahnschrift30", TL_DATUM, (temperature < 0 ? PAL_RED : PAL_BLACK));
|
||||
|
||||
spr.loadFont("fonts/weathericons78", LittleFS);
|
||||
if (weathercode == 55 || weathercode == 65 || weathercode == 75 || weathercode == 82 || weathercode == 86 || weathercode == 95 || weathercode == 99) {
|
||||
spr.setTextColor(TFT_RED, TFT_WHITE);
|
||||
spr.setTextColor(PAL_RED, PAL_WHITE);
|
||||
} else {
|
||||
spr.setTextColor(TFT_BLACK, TFT_WHITE);
|
||||
spr.setTextColor(PAL_BLACK, PAL_WHITE);
|
||||
}
|
||||
|
||||
spr.setCursor(30, 33);
|
||||
spr.printToSprite(weatherIcons[weathercode]);
|
||||
spr.unloadFont();
|
||||
|
||||
drawString(spr, String(wind), 140, 10, "fonts/bahnschrift30", TR_DATUM, (wind > 4 ? TFT_RED : TFT_BLACK));
|
||||
drawString(spr, String(wind), 140, 10, "fonts/bahnschrift30", TR_DATUM, (wind > 4 ? PAL_RED : PAL_BLACK));
|
||||
|
||||
spr.loadFont("fonts/weathericons30", LittleFS);
|
||||
spr.setTextColor(TFT_BLACK, TFT_WHITE);
|
||||
spr.setTextColor(PAL_BLACK, PAL_WHITE);
|
||||
spr.setCursor(100, -2);
|
||||
spr.printToSprite(windDirectionIcon(winddirection));
|
||||
if (weathercode > 10) {
|
||||
spr.setTextColor(TFT_RED, TFT_WHITE);
|
||||
spr.setTextColor(PAL_RED, PAL_WHITE);
|
||||
spr.setCursor(115, 110);
|
||||
spr.printToSprite("\uf084");
|
||||
}
|
||||
@@ -414,7 +417,7 @@ void drawWeather(String &filename, String location, tagRecord *&taginfo) {
|
||||
|
||||
}
|
||||
|
||||
spr2grays(spr, spr.width(), spr.height(), filename);
|
||||
spr2buffer(spr, filename);
|
||||
spr.deleteSprite();
|
||||
}
|
||||
}
|
||||
@@ -474,28 +477,28 @@ void drawForecast(String &filename, String location, tagRecord *&taginfo) {
|
||||
initSprite(spr, 296, 128);
|
||||
|
||||
spr.setTextFont(2);
|
||||
spr.setTextColor(TFT_BLACK, TFT_WHITE);
|
||||
spr.setTextColor(PAL_BLACK, PAL_WHITE);
|
||||
spr.drawString(location, 5, 0);
|
||||
|
||||
for (uint8_t dag = 0; dag < 5; dag++) {
|
||||
time_t weatherday = doc["daily"]["time"][dag].as<time_t>();
|
||||
struct tm *datum = localtime(&weatherday);
|
||||
drawString(spr, String(weekday_name[datum->tm_wday]), dag * 59 + 30, 18, "fonts/twbold20", TC_DATUM, TFT_BLACK);
|
||||
drawString(spr, String(weekday_name[datum->tm_wday]), dag * 59 + 30, 18, "fonts/twbold20", TC_DATUM, PAL_BLACK);
|
||||
|
||||
uint8_t weathercode = doc["daily"]["weathercode"][dag].as<int>();
|
||||
if (weathercode > 40) weathercode -= 40;
|
||||
|
||||
spr.loadFont("fonts/weathericons30", LittleFS);
|
||||
if (weathercode == 55 || weathercode == 65 || weathercode == 75 || weathercode == 82 || weathercode == 86 || weathercode == 95 || weathercode == 99) {
|
||||
spr.setTextColor(TFT_RED, TFT_WHITE);
|
||||
spr.setTextColor(PAL_RED, PAL_WHITE);
|
||||
} else {
|
||||
spr.setTextColor(TFT_BLACK, TFT_WHITE);
|
||||
spr.setTextColor(PAL_BLACK, PAL_WHITE);
|
||||
}
|
||||
spr.setTextDatum(TL_DATUM);
|
||||
spr.setCursor(12 + dag * 59, 58);
|
||||
spr.printToSprite(weatherIcons[weathercode]);
|
||||
|
||||
spr.setTextColor(TFT_BLACK, TFT_WHITE);
|
||||
spr.setTextColor(PAL_BLACK, PAL_WHITE);
|
||||
spr.setCursor(17 + dag * 59, 27);
|
||||
spr.printToSprite(windDirectionIcon(doc["daily"]["winddirection_10m_dominant"][dag]));
|
||||
spr.unloadFont();
|
||||
@@ -505,19 +508,19 @@ void drawForecast(String &filename, String location, tagRecord *&taginfo) {
|
||||
uint8_t wind = windSpeedToBeaufort(doc["daily"]["windspeed_10m_max"][dag].as<double>());
|
||||
|
||||
spr.loadFont("fonts/GillSC20", LittleFS);
|
||||
drawString(spr, String(tmin) + " ", dag * 59 + 30, 108, "", TR_DATUM, (tmin < 0 ? TFT_RED : TFT_BLACK));
|
||||
drawString(spr, String(" ") + String(tmax), dag * 59 + 30, 108, "", TL_DATUM, (tmax < 0 ? TFT_RED : TFT_BLACK));
|
||||
drawString(spr, String(" ") + String(wind), dag * 59 + 30, 43, "", TL_DATUM, (wind > 5 ? TFT_RED : TFT_BLACK));
|
||||
drawString(spr, String(tmin) + " ", dag * 59 + 30, 108, "", TR_DATUM, (tmin < 0 ? PAL_RED : PAL_BLACK));
|
||||
drawString(spr, String(" ") + String(tmax), dag * 59 + 30, 108, "", TL_DATUM, (tmax < 0 ? PAL_RED : PAL_BLACK));
|
||||
drawString(spr, String(" ") + String(wind), dag * 59 + 30, 43, "", TL_DATUM, (wind > 5 ? PAL_RED : PAL_BLACK));
|
||||
spr.unloadFont();
|
||||
if (dag>0) {
|
||||
for (int i = 20; i < 128; i+=3) {
|
||||
spr.drawPixel(dag * 59, i, TFT_BLACK);
|
||||
spr.drawPixel(dag * 59, i, PAL_BLACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
spr2grays(spr, spr.width(), spr.height(), filename);
|
||||
spr2buffer(spr, filename);
|
||||
spr.deleteSprite();
|
||||
}
|
||||
}
|
||||
@@ -533,15 +536,15 @@ void drawIdentify(String &filename, tagRecord *&taginfo) {
|
||||
if (taginfo->hwType == SOLUM_29_033) {
|
||||
initSprite(spr, 296, 128);
|
||||
drawString(spr, taginfo->alias, 10, 10, "fonts/bahnschrift20");
|
||||
drawString(spr, mac62hex(taginfo->mac), 10, 50, "fonts/bahnschrift20", TL_DATUM, TFT_RED);
|
||||
drawString(spr, mac62hex(taginfo->mac), 10, 50, "fonts/bahnschrift20", TL_DATUM, PAL_RED);
|
||||
|
||||
} else if (taginfo->hwType == SOLUM_154_033) {
|
||||
initSprite(spr, 154, 154);
|
||||
initSprite(spr, 152, 152);
|
||||
drawString(spr, taginfo->alias, 5, 5, "fonts/bahnschrift20");
|
||||
drawString(spr, mac62hex(taginfo->mac), 10, 50, "fonts/bahnschrift20", TL_DATUM, TFT_RED);
|
||||
drawString(spr, mac62hex(taginfo->mac), 10, 50, "fonts/bahnschrift20", TL_DATUM, PAL_RED);
|
||||
}
|
||||
|
||||
spr2grays(spr, spr.width(), spr.height(), filename);
|
||||
spr2buffer(spr, filename);
|
||||
spr.deleteSprite();
|
||||
}
|
||||
|
||||
@@ -557,11 +560,11 @@ bool getImgURL(String &filename, String URL, time_t fetched) {
|
||||
http.setTimeout(5000); //timeout in ms
|
||||
int httpCode = http.GET();
|
||||
if (httpCode == 200) {
|
||||
File f = LittleFS.open(filename, "w");
|
||||
File f = LittleFS.open("/temp/temp.jpg", "w");
|
||||
if (f) {
|
||||
http.writeToStream(&f);
|
||||
f.close();
|
||||
jpg2grays(filename, filename);
|
||||
jpg2buffer("/temp/temp.jpg", filename);
|
||||
}
|
||||
} else {
|
||||
if (httpCode!=304) {
|
||||
@@ -601,13 +604,13 @@ bool getRSSfeed(String &filename, String URL, String title, tagRecord *&taginfo)
|
||||
if (taginfo->hwType == SOLUM_29_033) {
|
||||
initSprite(spr, 296, 128);
|
||||
if (title=="" || title=="null") title="RSS feed";
|
||||
drawString(spr, title, 5, 3, "fonts/bahnschrift20", TL_DATUM, TFT_RED);
|
||||
drawString(spr, title, 5, 3, "fonts/bahnschrift20", TL_DATUM, PAL_RED);
|
||||
|
||||
u8f.setFont(u8g2_font_glasstown_nbp_tr); // select u8g2 font from here: https://github.com/olikraus/u8g2/wiki/fntlistall
|
||||
u8f.setFontMode(0);
|
||||
u8f.setFontDirection(0);
|
||||
u8f.setForegroundColor(TFT_BLACK);
|
||||
u8f.setBackgroundColor(TFT_WHITE);
|
||||
u8f.setForegroundColor(PAL_BLACK);
|
||||
u8f.setBackgroundColor(PAL_WHITE);
|
||||
u8f.setCursor(220, 20);
|
||||
u8f.print(header);
|
||||
|
||||
@@ -624,7 +627,7 @@ bool getRSSfeed(String &filename, String URL, String title, tagRecord *&taginfo)
|
||||
}
|
||||
}
|
||||
|
||||
spr2grays(spr, spr.width(), spr.height(), filename);
|
||||
spr2buffer(spr, filename);
|
||||
spr.deleteSprite();
|
||||
|
||||
return true;
|
||||
|
||||
@@ -22,7 +22,7 @@ void timeTask(void* parameter) {
|
||||
Serial.println("Waiting for valid time from NTP-server");
|
||||
} else {
|
||||
if (now % 10 == 0) wsSendSysteminfo();
|
||||
if (now % 60 == 3) Ping();
|
||||
if (now % 30 == 3) Ping();
|
||||
if (now % 300 == 6) saveDB("/current/tagDB.json");
|
||||
|
||||
contentRunner();
|
||||
|
||||
@@ -14,24 +14,27 @@ bool spr_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t *bitmap)
|
||||
return 1;
|
||||
}
|
||||
|
||||
void jpg2grays(String filein, String fileout) {
|
||||
void jpg2buffer(String filein, String fileout) {
|
||||
LittleFS.begin();
|
||||
TJpgDec.setSwapBytes(true);
|
||||
TJpgDec.setJpgScale(1);
|
||||
TJpgDec.setCallback(spr_output);
|
||||
uint16_t w = 0, h = 0;
|
||||
if (filein.c_str()[0] != '/') {
|
||||
filein = "/" + filein;
|
||||
}
|
||||
TJpgDec.getFsJpgSize(&w, &h, filein, LittleFS);
|
||||
Serial.println("jpeg conversion " + String(w) + "x" + String(h));
|
||||
|
||||
spr.setColorDepth(8);
|
||||
spr.createSprite(w, h);
|
||||
if (spr.getPointer() == nullptr) {
|
||||
wsErr("Failed to create sprite in jpg2grays");
|
||||
wsErr("Failed to create sprite in jpg2buffer");
|
||||
}
|
||||
spr.setColorDepth(8);
|
||||
spr.fillSprite(TFT_WHITE);
|
||||
TJpgDec.drawFsJpg(0, 0, filein, LittleFS);
|
||||
|
||||
spr2grays(spr, w, h, fileout);
|
||||
spr2buffer(spr, fileout);
|
||||
spr.deleteSprite();
|
||||
}
|
||||
|
||||
@@ -45,8 +48,6 @@ static uint32_t repackPackedVals(uint32_t val, uint32_t pixelsPerPackedUnit, uin
|
||||
}
|
||||
|
||||
void spr2grays(TFT_eSprite &spr, long w, long h, String &fileout) {
|
||||
// based on bmp2grays function by Dmitry.GR
|
||||
|
||||
long t = millis();
|
||||
LittleFS.begin();
|
||||
|
||||
@@ -247,6 +248,120 @@ void spr2grays(TFT_eSprite &spr, long w, long h, String &fileout) {
|
||||
Serial.println("finished writing BMP " + String(millis() - t) + "ms");
|
||||
}
|
||||
|
||||
struct Color {
|
||||
uint8_t r, g, b;
|
||||
Color() : r(0), g(0), b(0) {}
|
||||
Color(uint16_t value_) : r((value_ >> 8) & 0xF8 | (value_ >> 13) & 0x07), g((value_ >> 3) & 0xFC | (value_ >> 9) & 0x03), b((value_ << 3) & 0xF8 | (value_ >> 2) & 0x07) {}
|
||||
Color(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
|
||||
};
|
||||
|
||||
struct Error {
|
||||
float r;
|
||||
float g;
|
||||
float b;
|
||||
};
|
||||
|
||||
uint32_t colorDistance(const Color &c1, const Color &c2, const Error &e1) {
|
||||
float r_diff = c1.r + e1.r - c2.r;
|
||||
float g_diff = c1.g + e1.g - c2.g;
|
||||
float b_diff = c1.b + e1.b - c2.b;
|
||||
return round(r_diff * r_diff + g_diff * g_diff + b_diff * b_diff);
|
||||
}
|
||||
|
||||
void spr2buffer(TFT_eSprite &spr, String &fileout) {
|
||||
long t = millis();
|
||||
LittleFS.begin();
|
||||
|
||||
fs::File f_out = LittleFS.open(fileout, "w");
|
||||
|
||||
bool dither = true, rotated = false;
|
||||
long bufw = spr.width(), bufh = spr.height();
|
||||
|
||||
if (bufw > bufh) {
|
||||
rotated = true;
|
||||
bufw = spr.height();
|
||||
bufh = spr.width();
|
||||
}
|
||||
|
||||
int bufferSize = (bufw * bufh) / 8;
|
||||
uint8_t *blackBuffer = new uint8_t[bufferSize];
|
||||
uint8_t *redBuffer = new uint8_t[bufferSize];
|
||||
memset(blackBuffer, 0, bufferSize);
|
||||
memset(redBuffer, 0, bufferSize);
|
||||
|
||||
std::vector<Color> palette = {
|
||||
{255, 255, 255}, // White
|
||||
{0, 0, 0}, // Black
|
||||
{255, 0, 0} // Red
|
||||
};
|
||||
int num_colors = palette.size();
|
||||
Color color;
|
||||
Error error_bufferold[bufw];
|
||||
Error error_buffernew[bufw];
|
||||
|
||||
memset(error_bufferold, 0, sizeof(error_bufferold));
|
||||
for (uint16_t y = 0; y < bufh; y++) {
|
||||
memset(error_buffernew, 0, sizeof(error_buffernew));
|
||||
for (uint16_t x = 0; x < bufw; x++) {
|
||||
if (rotated) {
|
||||
color = Color(spr.readPixel(bufh - 1 - y, x));
|
||||
} else {
|
||||
color = Color(spr.readPixel(x, y));
|
||||
}
|
||||
|
||||
int best_color_index = 0;
|
||||
uint32_t best_color_distance = colorDistance(color, palette[0], error_bufferold[x]);
|
||||
for (int i = 1; i < num_colors; i++) {
|
||||
uint32_t distance = colorDistance(color, palette[i], error_bufferold[x]);
|
||||
if (distance < best_color_distance) {
|
||||
best_color_distance = distance;
|
||||
best_color_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t bitIndex = 7 - (x % 8);
|
||||
uint16_t byteIndex = (y * bufw + x) / 8;
|
||||
if (best_color_index & 1) {
|
||||
blackBuffer[byteIndex] |= (1 << bitIndex);
|
||||
} else if (best_color_index & 2) {
|
||||
redBuffer[byteIndex] |= (1 << bitIndex);
|
||||
}
|
||||
|
||||
Error error = {
|
||||
((float)color.r + error_bufferold[x].r - palette[best_color_index].r) / 16.0f,
|
||||
((float)color.g + error_bufferold[x].g - palette[best_color_index].g) / 16.0f,
|
||||
((float)color.b + error_bufferold[x].b - palette[best_color_index].b) / 16.0f};
|
||||
|
||||
error_buffernew[x].r += error.r * 5.0f;
|
||||
error_buffernew[x].g += error.g * 5.0f;
|
||||
error_buffernew[x].b += error.b * 5.0f;
|
||||
if (x > 0) {
|
||||
error_buffernew[x - 1].r += error.r * 3.0f;
|
||||
error_buffernew[x - 1].g += error.g * 3.0f;
|
||||
error_buffernew[x - 1].b += error.b * 3.0f;
|
||||
}
|
||||
if (x < bufw - 1) {
|
||||
error_buffernew[x + 1].r += error.r * 1.0f;
|
||||
error_buffernew[x + 1].g += error.g * 1.0f;
|
||||
error_buffernew[x + 1].b += error.b * 1.0f;
|
||||
error_bufferold[x + 1].r += error.r * 7.0f;
|
||||
error_bufferold[x + 1].g += error.g * 7.0f;
|
||||
error_bufferold[x + 1].b += error.b * 7.0f;
|
||||
}
|
||||
}
|
||||
memcpy(error_bufferold, error_buffernew, sizeof(error_buffernew));
|
||||
}
|
||||
|
||||
f_out.write(blackBuffer, bufferSize);
|
||||
f_out.write(redBuffer, bufferSize);
|
||||
|
||||
delete[] blackBuffer;
|
||||
delete[] redBuffer;
|
||||
|
||||
f_out.close();
|
||||
Serial.println("finished writing buffer " + String(millis() - t) + "ms");
|
||||
}
|
||||
|
||||
void bmp2grays(String filein, String fileout) {
|
||||
// based on bmp2grays function by Dmitry.GR
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "tag_db.h"
|
||||
#include "web.h"
|
||||
|
||||
extern void sendBlock(const void* data, const uint16_t len);
|
||||
extern uint16_t sendBlock(const void* data, const uint16_t len);
|
||||
|
||||
void addCRC(void* p, uint8_t len) {
|
||||
uint8_t total = 0;
|
||||
@@ -74,80 +74,79 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t
|
||||
nextCheckin = MIN_RESPONSE_TIME;
|
||||
}
|
||||
|
||||
*filename = "/" + *filename;
|
||||
LittleFS.begin();
|
||||
if (!LittleFS.exists(*filename)) return false;
|
||||
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;
|
||||
md5.begin();
|
||||
md5.addStream(file, file.size());
|
||||
md5.calculate();
|
||||
md5.getBytes(md5bytes);
|
||||
}
|
||||
|
||||
uint16_t attempts = 60 * 24;
|
||||
uint8_t lut = EPD_LUT_NO_REPEATS;
|
||||
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 (taginfo == nullptr) {
|
||||
wsErr("Tag not found, this shouldn't happen.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (memcmp(md5bytes, taginfo->md5pending, 16) == 0) {
|
||||
wsLog("new image is the same as current or already pending image. not updating tag.");
|
||||
wsSendTaginfo(mac);
|
||||
return true;
|
||||
*filename = "/" + *filename;
|
||||
LittleFS.begin();
|
||||
|
||||
if (!LittleFS.exists(*filename)) {
|
||||
wsErr("File not found. " + *filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
fs::File file = LittleFS.open(*filename);
|
||||
uint32_t filesize = file.size();
|
||||
if (filesize == 0) {
|
||||
file.close();
|
||||
wsErr("File has size 0. " + *filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t md5bytes[16];
|
||||
{
|
||||
MD5Builder md5;
|
||||
md5.begin();
|
||||
md5.addStream(file, filesize);
|
||||
md5.calculate();
|
||||
md5.getBytes(md5bytes);
|
||||
}
|
||||
|
||||
file.close();
|
||||
uint16_t attempts = 60 * 24;
|
||||
uint8_t lut = EPD_LUT_NO_REPEATS;
|
||||
|
||||
if (memcmp(md5bytes, taginfo->md5pending, 16) == 0) {
|
||||
wsLog("new image is the same as current or already pending image. not updating tag.");
|
||||
wsSendTaginfo(mac);
|
||||
return true;
|
||||
}
|
||||
|
||||
time_t now;
|
||||
time(&now);
|
||||
time_t last_midnight = now - now % (24 * 60 * 60) + 3 * 3600; // somewhere in the middle of the night
|
||||
if (taginfo->lastfullupdate < last_midnight) {
|
||||
lut = EPD_LUT_DEFAULT; // full update once a day
|
||||
taginfo->lastfullupdate = now;
|
||||
}
|
||||
|
||||
if (dataType != DATATYPE_FW_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]);
|
||||
if (LittleFS.exists(dst_path)) {
|
||||
LittleFS.remove(dst_path);
|
||||
}
|
||||
LittleFS.rename(*filename, dst_path);
|
||||
*filename = String(dst_path);
|
||||
|
||||
wsLog("new image pending: " + String(dst_path));
|
||||
time_t now;
|
||||
time(&now);
|
||||
time_t last_midnight = now - now % (24 * 60 * 60) + 3 * 3600; // somewhere in the middle of the night
|
||||
if (taginfo->lastfullupdate < last_midnight) {
|
||||
lut = EPD_LUT_DEFAULT; // full update once a day
|
||||
taginfo->lastfullupdate = now;
|
||||
}
|
||||
taginfo->pending = true;
|
||||
taginfo->expectedNextCheckin = now + nextCheckin * 60 + 60;
|
||||
memcpy(taginfo->md5pending, md5bytes, sizeof(md5bytes));
|
||||
|
||||
} else {
|
||||
wsErr("Tag not found, this shouldn't happen.");
|
||||
wsLog("firmware upload pending");
|
||||
}
|
||||
|
||||
// the message that will be sent to the AP to tell the tag there is data pending
|
||||
@@ -155,7 +154,7 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t
|
||||
memcpy(pending.targetMac, dst, 8);
|
||||
pending.availdatainfo.dataType = dataType;
|
||||
pending.availdatainfo.dataVer = *((uint64_t*)md5bytes);
|
||||
pending.availdatainfo.dataSize = file.size();
|
||||
pending.availdatainfo.dataSize = filesize;
|
||||
pending.availdatainfo.dataTypeArgument = lut;
|
||||
pending.availdatainfo.nextCheckIn = nextCheckin;
|
||||
pending.attemptsLeft = attempts;
|
||||
@@ -169,36 +168,8 @@ 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);
|
||||
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) {
|
||||
time_t now;
|
||||
time(&now);
|
||||
taginfo->pending = true;
|
||||
taginfo->expectedNextCheckin = now + nextCheckin * 60 + 60;
|
||||
memcpy(taginfo->md5pending, md5bytes, sizeof(md5bytes));
|
||||
}
|
||||
} else {
|
||||
wsLog("firmware upload pending");
|
||||
}
|
||||
file.close();
|
||||
|
||||
wsSendTaginfo(mac);
|
||||
|
||||
return true;
|
||||
@@ -229,7 +200,7 @@ void processBlockRequest(struct espBlockRequest* br) {
|
||||
file.close();
|
||||
} else {
|
||||
// file is already cached, refresh the timeout
|
||||
pd->datatimeout = PENDING_DATA_TIMEOUT;
|
||||
//pd->datatimeout = PENDING_DATA_TIMEOUT;
|
||||
}
|
||||
}
|
||||
// check if we're not exceeding max blocks (to prevent sendBlock from exceeding its boundary)
|
||||
@@ -241,11 +212,11 @@ void processBlockRequest(struct espBlockRequest* br) {
|
||||
|
||||
uint32_t len = pd->len - (BLOCK_DATA_SIZE * br->blockId);
|
||||
if (len > BLOCK_DATA_SIZE) len = BLOCK_DATA_SIZE;
|
||||
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);
|
||||
uint16_t checksum = sendBlock(pd->data + (br->blockId * BLOCK_DATA_SIZE), len);
|
||||
char buffer[150];
|
||||
sprintf(buffer, "< Block Request received for file %s block %d, len %d checksum %u\0", pd->filename.c_str(), br->blockId, len, checksum);
|
||||
wsLog((String)buffer);
|
||||
Serial.printf("<BlockId=%d\n", br->blockId);
|
||||
Serial.printf("< Block Request received for MD5 %llu, file %s block %d, len %d checksum %u\n", br->ver, pd->filename.c_str(), br->blockId, len, checksum);
|
||||
}
|
||||
|
||||
void processXferComplete(struct espXferComplete* xfc) {
|
||||
@@ -260,10 +231,8 @@ void processXferComplete(struct espXferComplete* xfc) {
|
||||
|
||||
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]);
|
||||
sprintf(dst_path, "/current/%02X%02X%02X%02X%02X%02X.raw\0", src[2], src[3], src[4], src[5], src[6], src[7]);
|
||||
if (LittleFS.exists(dst_path) && LittleFS.exists(src_path)) {
|
||||
LittleFS.remove(dst_path);
|
||||
}
|
||||
@@ -272,9 +241,6 @@ void processXferComplete(struct espXferComplete* xfc) {
|
||||
} else {
|
||||
wsErr("hm, weird, no pending image found after xfercomplete.");
|
||||
}
|
||||
if (LittleFS.exists(tmp_path)) {
|
||||
LittleFS.remove(tmp_path);
|
||||
}
|
||||
|
||||
time_t now;
|
||||
time(&now);
|
||||
@@ -362,6 +328,11 @@ void processDataReq(struct espAvailDataReq* eadr) {
|
||||
taginfo->hwType = eadr->adr.hwType;
|
||||
taginfo->wakeupReason = eadr->adr.wakeupReason;
|
||||
taginfo->capabilities = eadr->adr.capabilities;
|
||||
if (eadr->adr.wakeupReason == WAKEUP_REASON_FIRSTBOOT && !taginfo->pending) {
|
||||
taginfo->nextupdate = 0;
|
||||
memset(taginfo->md5, 0, 16 * sizeof(uint8_t));
|
||||
memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t));
|
||||
}
|
||||
}
|
||||
|
||||
sprintf(buffer, "<ADR %02X%02X%02X%02X%02X%02X\n\0", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
|
||||
@@ -90,6 +90,7 @@ uint8_t pktindex = 0;
|
||||
char lastchar = 0;
|
||||
uint8_t charindex = 0;
|
||||
uint64_t waitingForVersion = 0;
|
||||
uint8_t crashcounter = 0;
|
||||
uint16_t version;
|
||||
|
||||
void ShortRXWaitLoop() {
|
||||
@@ -200,25 +201,12 @@ void SerialRXLoop() {
|
||||
break;
|
||||
case ZBS_RX_WAIT_VER:
|
||||
waitingForVersion = 0;
|
||||
crashcounter = 0;
|
||||
cmdbuffer[charindex] = lastchar;
|
||||
charindex++;
|
||||
if (charindex == 4) {
|
||||
charindex = 0;
|
||||
version = (uint16_t)strtoul(cmdbuffer, NULL, 16);
|
||||
/*
|
||||
uint16_t fsversion;
|
||||
lookupFirmwareFile(fsversion);
|
||||
if ((fsversion) && (version != fsversion)) {
|
||||
Serial.printf("ZBS/Zigbee FW version: %04X, version on SPIFFS: %04X\n", version, fsversion);
|
||||
Serial.printf("Performing flash update in about 30 seconds");
|
||||
vTaskDelay(30000 / portTICK_PERIOD_MS);
|
||||
performDeviceFlash();
|
||||
} else if (!fsversion) {
|
||||
Serial.println("No ZBS/Zigbee FW binary found on SPIFFS, please upload a zigbeebase000X.bin - format binary to enable flashing");
|
||||
} else {
|
||||
Serial.printf("ZBS/Zigbee FW version: %04X\n", version);
|
||||
}
|
||||
*/
|
||||
RXState = ZBS_RX_WAIT_HEADER;
|
||||
}
|
||||
break;
|
||||
@@ -229,13 +217,14 @@ void SerialRXLoop() {
|
||||
extern uint8_t* getDataForFile(File* file);
|
||||
|
||||
void zbsRxTask(void* parameter) {
|
||||
Serial1.begin(230400, SERIAL_8N1, RXD1, TXD1);
|
||||
Serial1.begin(228571, SERIAL_8N1, RXD1, TXD1);
|
||||
|
||||
simplePowerOn();
|
||||
bool firstrun = true;
|
||||
|
||||
Serial1.print("VER?");
|
||||
waitingForVersion = esp_timer_get_time();
|
||||
|
||||
while (1) {
|
||||
SerialRXLoop();
|
||||
|
||||
@@ -245,14 +234,19 @@ void zbsRxTask(void* parameter) {
|
||||
vTaskDelay(1 / portTICK_PERIOD_MS);
|
||||
|
||||
if (waitingForVersion) {
|
||||
if (esp_timer_get_time() - waitingForVersion > 10000*1000ULL) {
|
||||
if (esp_timer_get_time() - waitingForVersion > 5000*1000ULL) {
|
||||
waitingForVersion = 0;
|
||||
//performDeviceFlash();
|
||||
Serial.println("I wasn't able to connect to a ZBS tag, trying to reboot the tag.");
|
||||
Serial.println("If this problem persists, please check wiring and definitions in the settings.h file, and presence of the right firmware");
|
||||
simplePowerOn();
|
||||
wsErr("The AP tag crashed. Restarting tag, regenerating all pending info.");
|
||||
refreshAllPending();
|
||||
wsLog("AP doesn't respond... "+String(crashcounter + 1));
|
||||
if (++crashcounter >= 4) {
|
||||
crashcounter = 0;
|
||||
Serial.println("I wasn't able to connect to a ZBS tag, trying to reboot the tag.");
|
||||
Serial.println("If this problem persists, please check wiring and definitions in the settings.h file, and presence of the right firmware");
|
||||
simplePowerOn();
|
||||
wsErr("The AP tag crashed. Restarting tag, regenerating all pending info.");
|
||||
refreshAllPending();
|
||||
} else {
|
||||
Ping();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,12 +82,12 @@ void showSplashScreen() {
|
||||
setColorMode(EPD_MODE_NORMAL, EPD_MODE_INVERT);
|
||||
|
||||
#if (SCREEN_WIDTH == 152) // 1.54"
|
||||
epdPrintBegin(12, 52, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
|
||||
epdPrintBegin(5, 55, EPD_DIRECTION_X, EPD_SIZE_DOUBLE, EPD_COLOR_BLACK);
|
||||
epdpr("Starting");
|
||||
epdPrintEnd();
|
||||
|
||||
loadRawBitmap(oepli, 8, 12, EPD_COLOR_BLACK);
|
||||
loadRawBitmap(cloud, 8, 0, EPD_COLOR_RED);
|
||||
loadRawBitmap(oepli, 12, 12, EPD_COLOR_BLACK);
|
||||
loadRawBitmap(cloud, 12, 0, EPD_COLOR_RED);
|
||||
|
||||
epdPrintBegin(5, 136, EPD_DIRECTION_X, EPD_SIZE_SINGLE, EPD_COLOR_RED);
|
||||
epdpr("%02X%02X", mSelfMac[7], mSelfMac[6]);
|
||||
|
||||
Reference in New Issue
Block a user