diff --git a/esp32_fw/data/fonts/GillSC16.vlw b/esp32_fw/data/fonts/GillSC16.vlw
new file mode 100644
index 00000000..04ebc30c
Binary files /dev/null and b/esp32_fw/data/fonts/GillSC16.vlw differ
diff --git a/esp32_fw/data/fonts/GillSC20.vlw b/esp32_fw/data/fonts/GillSC20.vlw
new file mode 100644
index 00000000..80d75f5e
Binary files /dev/null and b/esp32_fw/data/fonts/GillSC20.vlw differ
diff --git a/esp32_fw/data/fonts/weathericons50.vlw b/esp32_fw/data/fonts/weathericons50.vlw
deleted file mode 100644
index 4019f131..00000000
Binary files a/esp32_fw/data/fonts/weathericons50.vlw and /dev/null differ
diff --git a/esp32_fw/data/fonts/weathericons70.vlw b/esp32_fw/data/fonts/weathericons70.vlw
new file mode 100644
index 00000000..1807f1ec
Binary files /dev/null and b/esp32_fw/data/fonts/weathericons70.vlw differ
diff --git a/esp32_fw/data/www/index.html b/esp32_fw/data/www/index.html
index 81c2c6da..0cfbbcfa 100644
--- a/esp32_fw/data/www/index.html
+++ b/esp32_fw/data/www/index.html
@@ -30,6 +30,8 @@
+
+
diff --git a/esp32_fw/data/www/main.js b/esp32_fw/data/www/main.js
index 504bd8cf..7e29a4bd 100644
--- a/esp32_fw/data/www/main.js
+++ b/esp32_fw/data/www/main.js
@@ -1,6 +1,13 @@
const $ = document.querySelector.bind(document);
-const contentModes = ["Static image", "Current date", "Counting days", "Counting hours", "Current weather", "Firmware update", "Memo text", "Image url"];
+const WAKEUP_REASON_TIMED = 0;
+const WAKEUP_REASON_GPIO = 2;
+const WAKEUP_REASON_NFC = 3;
+const WAKEUP_REASON_FIRSTBOOT = 0xFC;
+const WAKEUP_REASON_NETWORK_SCAN = 0xFD;
+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 contentModeOptions = [];
contentModeOptions[0] = ["filename","timetolive"];
@@ -11,6 +18,8 @@ contentModeOptions[4] = ["location"];
contentModeOptions[5] = ["filename"];
contentModeOptions[6] = ["text"];
contentModeOptions[7] = ["url","interval"];
+contentModeOptions[8] = ["location"];
+contentModeOptions[9] = ["title", "url", "interval"];
const imageQueue = [];
let isProcessing = false;
@@ -123,6 +132,28 @@ function processTags(tagArray) {
div.dataset.hash = element.hash;
$('#tag' + tagmac + ' .warningicon').style.display = 'none';
$('#tag' + tagmac).style.background = "inherit";
+ switch (element.wakeupReason) {
+ case WAKEUP_REASON_TIMED:
+ break;
+ case WAKEUP_REASON_GPIO:
+ $('#tag' + tagmac + ' .nextcheckin').innerHTML = "GPIO wakeup"
+ break;
+ case WAKEUP_REASON_NFC:
+ $('#tag' + tagmac + ' .nextcheckin').innerHTML = "NFC wakeup"
+ break;
+ case WAKEUP_REASON_FIRSTBOOT:
+ $('#tag' + tagmac + ' .nextcheckin').innerHTML = "First boot"
+ $('#tag' + tagmac).style.background = "purple";
+ break;
+ case WAKEUP_REASON_NETWORK_SCAN:
+ $('#tag' + tagmac + ' .nextcheckin').innerHTML = "Network scan"
+ $('#tag' + tagmac).style.background = "green";
+ break;
+ case WAKEUP_REASON_WDT_RESET:
+ $('#tag' + tagmac + ' .nextcheckin').innerHTML = "Watchdog reset!"
+ $('#tag' + tagmac).style.background = "red";
+ break;
+ }
$('#tag' + tagmac + ' .pendingicon').style.display = (element.pending ? 'inline-block' : 'none');
div.classList.add("tagflash");
(function(tagmac) {
diff --git a/esp32_fw/include/commstructs.h b/esp32_fw/include/commstructs.h
index 7918df07..a52e6c4b 100644
--- a/esp32_fw/include/commstructs.h
+++ b/esp32_fw/include/commstructs.h
@@ -22,10 +22,12 @@ struct blockData {
#define SOLUM_29_033 1
#define SOLUM_42_033 2
-#define WAKEUP_REASON_TIMED 0
-#define WAKEUP_REASON_BOOTUP 1
-#define WAKEUP_REASON_GPIO 2
-#define WAKEUP_REASON_NFC 3
+#define WAKEUP_REASON_TIMED 0
+#define WAKEUP_REASON_GPIO 2
+#define WAKEUP_REASON_NFC 3
+#define WAKEUP_REASON_FIRSTBOOT 0xFC
+#define WAKEUP_REASON_NETWORK_SCAN 0xFD
+#define WAKEUP_REASON_WDT_RESET 0xFE
struct AvailDataReq {
uint8_t checksum;
@@ -49,6 +51,11 @@ struct espAvailDataReq {
#define DATATYPE_IMGRAW 2
#define DATATYPE_UPDATE 3
+#define EPD_LUT_DEFAULT 0
+#define EPD_LUT_NO_REPEATS 1
+#define EPD_LUT_FAST_NO_REDS 2
+#define EPD_LUT_FAST 3
+
struct AvailDataInfo {
uint8_t checksum;
uint64_t dataVer; // MD5 of potential traffic
diff --git a/esp32_fw/include/contentmanager.h b/esp32_fw/include/contentmanager.h
index 02b58235..ef6a41d0 100644
--- a/esp32_fw/include/contentmanager.h
+++ b/esp32_fw/include/contentmanager.h
@@ -6,6 +6,15 @@
#include "tag_db.h"
#include
+struct contentTypes {
+ uint16_t id;
+ String name;
+ uint16_t tagTypes;
+ void (*functionname)();
+ String description;
+ String optionList;
+};
+
void contentRunner();
void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo);
bool updateTagImage(String &filename, uint8_t *dst, uint16_t nextCheckin);
@@ -14,8 +23,10 @@ void initSprite(TFT_eSprite &spr, int w, int h);
void drawDate(String &filename, tagRecord *&taginfo);
void drawNumber(String &filename, int32_t count, int32_t thresholdred, tagRecord *&taginfo);
void drawWeather(String &filename, String location, tagRecord *&taginfo);
+void drawForecast(String &filename, String location, tagRecord *&taginfo);
void drawIdentify(String &filename, tagRecord *&taginfo);
bool getImgURL(String &filename, String URL, time_t fetched);
+bool getRSSfeed(String &filename, String URL, String title, tagRecord *&taginfo);
char *formatHttpDate(time_t t);
String urlEncode(const char *msg);
int windSpeedToBeaufort(float windSpeed);
diff --git a/esp32_fw/include/tag_db.h b/esp32_fw/include/tag_db.h
index 4e8f147b..3e7e713e 100644
--- a/esp32_fw/include/tag_db.h
+++ b/esp32_fw/include/tag_db.h
@@ -18,7 +18,7 @@
class tagRecord {
public:
uint16_t nextCheckinpending;
- tagRecord() : mac{0}, alias(""), lastseen(0), nextupdate(0), contentMode(0), pending(false), md5{0}, md5pending{0}, CheckinInMinPending(0), expectedNextCheckin(0), modeConfigJson(""), LQI(0), RSSI(0), temperature(0), batteryMv(0), hwType(0), wakeupReason(0), capabilities(0) {}
+ tagRecord() : mac{0}, alias(""), lastseen(0), nextupdate(0), contentMode(0), pending(false), md5{0}, md5pending{0}, expectedNextCheckin(0), modeConfigJson(""), LQI(0), RSSI(0), temperature(0), batteryMv(0), hwType(0), wakeupReason(0), capabilities(0), lastfullupdate(0) {}
uint8_t mac[6];
String alias;
@@ -28,7 +28,6 @@ class tagRecord {
bool pending;
uint8_t md5[16];
uint8_t md5pending[16];
- uint16_t CheckinInMinPending;
uint32_t expectedNextCheckin;
String modeConfigJson;
uint8_t LQI;
@@ -37,7 +36,8 @@ class tagRecord {
uint16_t batteryMv;
uint8_t hwType;
uint8_t wakeupReason;
- uint8_t capabilities;
+ uint8_t capabilities;
+ uint32_t lastfullupdate;
static tagRecord* findByMAC(uint8_t mac[6]);
};
diff --git a/esp32_fw/platformio.ini b/esp32_fw/platformio.ini
index 77108edd..e92c1d78 100644
--- a/esp32_fw/platformio.ini
+++ b/esp32_fw/platformio.ini
@@ -24,5 +24,7 @@ lib_deps =
bblanchon/ArduinoJson
bodmer/TFT_eSPI
https://github.com/Bodmer/TJpg_Decoder.git
+ https://github.com/garretlab/shoddyxml2
+ https://github.com/Bodmer/U8g2_for_TFT_eSPI
upload_port = COM12
monitor_port = COM12
diff --git a/esp32_fw/src/contentmanager.cpp b/esp32_fw/src/contentmanager.cpp
index fb5e96df..a5d0fdf4 100644
--- a/esp32_fw/src/contentmanager.cpp
+++ b/esp32_fw/src/contentmanager.cpp
@@ -6,8 +6,10 @@
#include "newproto.h"
#include
#include
+#include
#include
+#include "U8g2_for_TFT_eSPI.h"
#include "commstructs.h"
#include "makeimage.h"
#include "web.h"
@@ -21,6 +23,8 @@ enum contentModes {
Firmware,
Memo,
ImageUrl,
+ Forecast,
+ RSSFeed,
};
@@ -122,6 +126,13 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
updateTagImage(filename, mac, 15);
break;
+ case Forecast:
+
+ drawForecast(filename, cfgobj["location"], taginfo);
+ taginfo->nextupdate = now + 3 * 3600;
+ updateTagImage(filename, mac, 15);
+ break;
+
case Firmware:
filename = cfgobj["filename"].as();
@@ -156,6 +167,16 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
taginfo->nextupdate = now + 300;
}
break;
+
+ case RSSFeed:
+
+ if (getRSSfeed(filename, cfgobj["url"], cfgobj["title"], taginfo)) {
+ taginfo->nextupdate = now + 60 * (cfgobj["interval"].as() < 5 ? 5 : cfgobj["interval"].as());
+ updateTagImage(filename, mac, cfgobj["interval"].as());
+ } else {
+ taginfo->nextupdate = now + 300;
+ }
+ break;
}
taginfo->modeConfigJson = doc.as();
@@ -169,10 +190,10 @@ bool updateTagImage(String &filename, uint8_t *dst, uint16_t nextCheckin) {
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);
spr.setTextDatum(align);
- spr.loadFont(font, LittleFS);
+ if (font != "") spr.loadFont(font, LittleFS);
spr.setTextColor(color, TFT_WHITE);
spr.drawString(content, posx, posy);
- spr.unloadFont();
+ if (font != "") spr.unloadFont();
}
void initSprite(TFT_eSprite &spr, int w, int h) {
@@ -316,8 +337,6 @@ void drawWeather(String &filename, String location, tagRecord *&taginfo) {
weatherIcons[2] = "\0uf086";
}
- String windIcons[] = {"\uf0b7", "\uf0b8", "\uf0b9", "\uf0ba", "\uf0bb", "\uf0bc", "\uf0bd", "\uf0be", "\uf0bf", "\uf0c0", "\uf0c1", "\uf0c2", "\uf0c3"};
-
doc.clear();
LittleFS.begin();
@@ -327,33 +346,27 @@ void drawWeather(String &filename, String location, tagRecord *&taginfo) {
initSprite(spr, 296, 128);
- drawString(spr, location, 10, 10, "fonts/bahnschrift30");
+ drawString(spr, location, 5, 5, "fonts/bahnschrift30");
+ drawString(spr, String(wind), 280, 5, "fonts/bahnschrift30", TR_DATUM, (wind > 4 ? TFT_RED : TFT_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));
- spr.loadFont("fonts/weathericons78", LittleFS);
+ 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);
} else {
spr.setTextColor(TFT_BLACK, TFT_WHITE);
}
- spr.setCursor(185, 20);
+
+ spr.setCursor(185, 32);
spr.printToSprite(weatherIcons[weathercode]);
spr.unloadFont();
spr.loadFont("fonts/weathericons30", LittleFS);
- if (wind > 4) {
- spr.setTextColor(TFT_RED, TFT_WHITE);
- } else {
- spr.setTextColor(TFT_BLACK, TFT_WHITE);
- }
- spr.setCursor(255, 0);
- spr.printToSprite(windIcons[wind]);
-
spr.setTextColor(TFT_BLACK, TFT_WHITE);
- spr.setCursor(230, -5);
+ spr.setCursor(235, -3);
spr.printToSprite(windDirectionIcon(winddirection));
if (weathercode > 10) {
spr.setTextColor(TFT_RED, TFT_WHITE);
@@ -381,21 +394,16 @@ void drawWeather(String &filename, String location, tagRecord *&taginfo) {
} else {
spr.setTextColor(TFT_BLACK, TFT_WHITE);
}
- spr.setCursor(35, 25);
+
+ spr.setCursor(30, 33);
spr.printToSprite(weatherIcons[weathercode]);
spr.unloadFont();
- spr.loadFont("fonts/weathericons30", LittleFS);
- if (wind > 4) {
- spr.setTextColor(TFT_RED, TFT_WHITE);
- } else {
- spr.setTextColor(TFT_BLACK, TFT_WHITE);
- }
- spr.setCursor(115, -5);
- spr.printToSprite(windIcons[wind]);
+ drawString(spr, String(wind), 140, 10, "fonts/bahnschrift30", TR_DATUM, (wind > 4 ? TFT_RED : TFT_BLACK));
+ spr.loadFont("fonts/weathericons30", LittleFS);
spr.setTextColor(TFT_BLACK, TFT_WHITE);
- spr.setCursor(90, -5);
+ spr.setCursor(100, -2);
spr.printToSprite(windDirectionIcon(winddirection));
if (weathercode > 10) {
spr.setTextColor(TFT_RED, TFT_WHITE);
@@ -413,6 +421,109 @@ void drawWeather(String &filename, String location, tagRecord *&taginfo) {
http.end();
}
+void drawForecast(String &filename, String location, tagRecord *&taginfo) {
+ TFT_eSPI tft = TFT_eSPI();
+ TFT_eSprite spr = TFT_eSprite(&tft);
+
+ wsLog("get weather");
+ HTTPClient http;
+ http.begin("https://geocoding-api.open-meteo.com/v1/search?name=" + urlEncode(location.c_str()) + "&count=1");
+ http.setTimeout(5000); // timeout in ms
+ int httpCode = http.GET();
+ if (httpCode == 200) {
+ DynamicJsonDocument doc(2000);
+ DeserializationError error = deserializeJson(doc, http.getStream());
+ http.end();
+
+ http.begin("https://api.open-meteo.com/v1/forecast?latitude=" + doc["results"][0]["latitude"].as() + "&longitude=" + doc["results"][0]["longitude"].as() + "&daily=weathercode,temperature_2m_max,temperature_2m_min,precipitation_sum,windspeed_10m_max,winddirection_10m_dominant&windspeed_unit=ms&timeformat=unixtime&timezone=" + doc["results"][0]["timezone"].as());
+
+ doc.clear();
+ http.setTimeout(5000); // timeout in ms
+ int httpCode = http.GET();
+
+ if (httpCode == 200) {
+ StaticJsonDocument<500> filter;
+ filter["daily"]["time"][0] = true;
+ filter["daily"]["weathercode"][0] = true;
+ filter["daily"]["temperature_2m_max"][0] = true;
+ filter["daily"]["temperature_2m_min"][0] = true;
+ filter["daily"]["precipitation_sum"][0] = true;
+ filter["daily"]["windspeed_10m_max"][0] = true;
+ filter["daily"]["winddirection_10m_dominant"][0] = true;
+
+ //DeserializationError error = deserializeJson(doc, http.getString(), DeserializationOption::Filter(filter));
+ DeserializationError error = deserializeJson(doc, http.getString());
+ if (error) {
+ Serial.println(F("deserializeJson() failed: "));
+ Serial.println(error.c_str());
+ }
+
+ static const char *weekday_name[] = {"ZO", "MA", "DI", "WO", "DO", "VR", "ZA"};
+
+ String weatherIcons[] = {"\uf00d", "\uf00c", "\uf002", "\uf013", "\uf013", "\uf014", "-", "-", "\uf014", "-", "-",
+ "\uf01a", "-", "\uf01a", "-", "\uf01a", "\uf017", "\uf017", "-", "-", "-",
+ "\uf019", "-", "\uf019", "-", "\uf019", "\uf015", "\uf015", "-", "-", "-",
+ "\uf01b", "-", "\uf01b", "-", "\uf01b", "-", "\uf076", "-", "-", "\uf01a",
+ "\uf01a", "\uf01a", "-", "-", "\uf064", "\uf064", "-", "-", "-", "-",
+ "-", "-", "-", "-", "\uf01e", "\uf01d", "-", "-", "\uf01e"};
+
+ LittleFS.begin();
+ tft.setTextWrap(false, false);
+
+ if (taginfo->hwType == SOLUM_29_033) {
+ initSprite(spr, 296, 128);
+
+ spr.setTextFont(2);
+ spr.setTextColor(TFT_BLACK, TFT_WHITE);
+ spr.drawString(location, 5, 0);
+
+ for (uint8_t dag = 0; dag < 5; dag++) {
+ time_t weatherday = doc["daily"]["time"][dag].as();
+ struct tm *datum = localtime(&weatherday);
+ drawString(spr, String(weekday_name[datum->tm_wday]), dag * 59 + 30, 18, "fonts/twbold20", TC_DATUM, TFT_BLACK);
+
+ uint8_t weathercode = doc["daily"]["weathercode"][dag].as();
+ 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);
+ } else {
+ spr.setTextColor(TFT_BLACK, TFT_WHITE);
+ }
+ spr.setTextDatum(TL_DATUM);
+ spr.setCursor(12 + dag * 59, 58);
+ spr.printToSprite(weatherIcons[weathercode]);
+
+ spr.setTextColor(TFT_BLACK, TFT_WHITE);
+ spr.setCursor(17 + dag * 59, 27);
+ spr.printToSprite(windDirectionIcon(doc["daily"]["winddirection_10m_dominant"][dag]));
+ spr.unloadFont();
+
+ int8_t tmin = round(doc["daily"]["temperature_2m_min"][dag].as());
+ int8_t tmax = round(doc["daily"]["temperature_2m_max"][dag].as());
+ uint8_t wind = windSpeedToBeaufort(doc["daily"]["windspeed_10m_max"][dag].as());
+
+ 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));
+ spr.unloadFont();
+ if (dag>0) {
+ for (int i = 20; i < 128; i+=3) {
+ spr.drawPixel(dag * 59, i, TFT_BLACK);
+ }
+ }
+ }
+
+ }
+ spr2grays(spr, spr.width(), spr.height(), filename);
+ spr.deleteSprite();
+ }
+ }
+ http.end();
+}
+
void drawIdentify(String &filename, tagRecord *&taginfo) {
TFT_eSPI tft = TFT_eSPI();
@@ -463,6 +574,62 @@ bool getImgURL(String &filename, String URL, time_t fetched) {
return (httpCode == 200 || httpCode == 304);
}
+rssClass reader;
+
+bool getRSSfeed(String &filename, String URL, String title, tagRecord *&taginfo) {
+ // https://github.com/garretlab/shoddyxml2
+
+ // http://feeds.feedburner.com/tweakers/nieuws
+ // https://www.nu.nl/rss/Algemeen
+
+ Serial.println("RSS feed");
+ struct tm timeInfo;
+ char header[32];
+ getLocalTime(&timeInfo);
+ sprintf(header, "%02d-%02d-%04d %02d:%02d", timeInfo.tm_mday, timeInfo.tm_mon + 1, timeInfo.tm_year + 1900, timeInfo.tm_hour, timeInfo.tm_min);
+
+ const char *url = URL.c_str();
+ const char *tag = "title";
+ const int rssArticleSize = 128;
+ const int rssNumArticle = 8;
+
+ TFT_eSPI tft = TFT_eSPI();
+ TFT_eSprite spr = TFT_eSprite(&tft);
+ U8g2_for_TFT_eSPI u8f;
+ u8f.begin(spr);
+
+ 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);
+
+ 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.setCursor(220, 20);
+ u8f.print(header);
+
+ // u8g2_font_nine_by_five_nbp_tr
+ // u8g2_font_7x14_tr
+ // u8g2_font_crox1h_tr
+ // u8g2_font_miranda_nbp_tr
+ u8f.setFont(u8g2_font_glasstown_nbp_tr); // select u8g2 font from here: https://github.com/olikraus/u8g2/wiki/fntlistall
+
+ int n = reader.getArticles(url, tag, rssArticleSize, rssNumArticle);
+ for (int i = 0; i < n; i++) {
+ u8f.setCursor(5, 34+i*13); // start writing at this position
+ u8f.print(reader.itemData[i]);
+ }
+ }
+
+ spr2grays(spr, spr.width(), spr.height(), filename);
+ spr.deleteSprite();
+
+ return true;
+}
+
char *formatHttpDate(time_t t) {
static char buf[40];
struct tm *timeinfo;
diff --git a/esp32_fw/src/makeimage.cpp b/esp32_fw/src/makeimage.cpp
index 54e8660d..2a35bcbe 100644
--- a/esp32_fw/src/makeimage.cpp
+++ b/esp32_fw/src/makeimage.cpp
@@ -152,6 +152,12 @@ void spr2grays(TFT_eSprite &spr, long w, long h, String &fileout) {
f_out.write(0x00);
}
+ const int dither_matrix[4][4] = {
+ {1, 9, 3, 11},
+ {13, 5, 15, 7},
+ {4, 12, 2, 10},
+ {0, 8, 14, 6}};
+
while (numRows--) {
uint32_t pixelValsPackedSoFar = 0, numPixelsPackedSoFar = 0, valSoFar = 0, bytesIn = 0, bytesOut = 0, bitsSoFar = 0;
@@ -170,8 +176,10 @@ void spr2grays(TFT_eSprite &spr, long w, long h, String &fileout) {
uint8_t green = ((color565 >> 5) & 0x3F) * 4;
uint8_t blue = (color565 & 0x1F) * 8;
- if (dither)
- ditherFudge = (rand() % 255 - 127) / (int)numGrays;
+ if (dither) {
+ // ditherFudge = (rand() % 255 - 127) / (int)numGrays; // -64 to 64
+ ditherFudge = (dither_matrix[numRows % 4][c % 4] - 8) * 24 / (int)numGrays;
+ }
for (i = 0; i < hdr.numColors; i++) {
int64_t dist = 0;
@@ -253,7 +261,7 @@ void bmp2grays(String filein, String fileout) {
struct BitmapFileHeader hdr;
enum EinkClut clutType;
uint8_t clut[256][3];
- bool dither = true;
+ bool dither = false;
int skipBytes;
srand(0);
diff --git a/esp32_fw/src/newproto.cpp b/esp32_fw/src/newproto.cpp
index efad9f80..735d9782 100644
--- a/esp32_fw/src/newproto.cpp
+++ b/esp32_fw/src/newproto.cpp
@@ -124,7 +124,8 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t
md5.getBytes(md5bytes);
}
- uint16_t attempts = 60;
+ uint16_t attempts = 60 * 24;
+ uint8_t lut = EPD_LUT_DEFAULT;
uint8_t src[8];
*((uint64_t*)src) = swap64(*((uint64_t*)dst));
uint8_t mac[6];
@@ -141,9 +142,13 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t
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_NO_REPEATS; // fast updates during the day
+ /*
uint16_t minutesUntilNextCheckin = 0;
if (taginfo->expectedNextCheckin > now) minutesUntilNextCheckin = (taginfo->expectedNextCheckin - now) / 60;
attempts += minutesUntilNextCheckin;
+ */
} else {
wsErr("Tag not found, this shouldn't happen.");
}
@@ -154,6 +159,7 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t
pending.availdatainfo.dataType = dataType;
pending.availdatainfo.dataVer = *((uint64_t*)md5bytes);
pending.availdatainfo.dataSize = file.size();
+ pending.availdatainfo.dataTypeArgument = EPD_LUT_NO_REPEATS;
pending.availdatainfo.nextCheckIn = nextCheckin;
pending.attemptsLeft = attempts;
sendDataAvail(&pending);
@@ -185,14 +191,15 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t
wsLog("new image pending: " + String(dst_path));
if (taginfo != nullptr) {
+ time_t now;
+ time(&now);
taginfo->pending = true;
- taginfo->CheckinInMinPending = nextCheckin;
+ taginfo->expectedNextCheckin = now + nextCheckin * 60 + 60;
memcpy(taginfo->md5pending, md5bytes, sizeof(md5bytes));
- }
- }
- else {
- wsLog("firmware upload pending");
}
+ } else {
+ wsLog("firmware upload pending");
+ }
file.close();
wsSendTaginfo(mac);
@@ -275,17 +282,13 @@ void processXferComplete(struct espXferComplete* xfc) {
if (taginfo != nullptr) {
uint16_t minutesUntilNextUpdate = 0;
- if (taginfo->nextupdate > now + 60 * taginfo->CheckinInMinPending + 3) {
- minutesUntilNextUpdate = (taginfo->nextupdate - now) / 60 - taginfo->CheckinInMinPending;
- if (minutesUntilNextUpdate > taginfo->CheckinInMinPending) minutesUntilNextUpdate = taginfo->CheckinInMinPending;
+ if (taginfo->nextupdate > now + 2) {
+ minutesUntilNextUpdate = (taginfo->nextupdate - now) / 60;
if (minutesUntilNextUpdate > MIN_RESPONSE_TIME) minutesUntilNextUpdate = MIN_RESPONSE_TIME;
-
taginfo->expectedNextCheckin = now + 60 * minutesUntilNextUpdate + 60;
- if (minutesUntilNextUpdate > 0) prepareIdleReq (xfc->src, minutesUntilNextUpdate);
- taginfo->CheckinInMinPending = minutesUntilNextUpdate;
+ if (minutesUntilNextUpdate > 1) prepareIdleReq (xfc->src, minutesUntilNextUpdate);
} else {
taginfo->expectedNextCheckin = now + 60;
- taginfo->CheckinInMinPending = 0;
}
taginfo->pending = false;
@@ -310,7 +313,6 @@ void processXferTimeout(struct espXferComplete* xfc) {
taginfo = tagRecord::findByMAC(mac);
if (taginfo != nullptr) {
taginfo->expectedNextCheckin = now + 60;
- taginfo->CheckinInMinPending = 0;
taginfo->pending = false;
memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t));
}
@@ -341,15 +343,13 @@ void processDataReq(struct espAvailDataReq* eadr) {
taginfo->lastseen = now;
uint16_t minutesUntilNextUpdate = 0;
- if (taginfo->nextupdate > now + 60 * taginfo->CheckinInMinPending + 3 && taginfo->pending == false) {
- minutesUntilNextUpdate = (taginfo->nextupdate - now) / 60 - taginfo->CheckinInMinPending;
- if (minutesUntilNextUpdate > taginfo->CheckinInMinPending) minutesUntilNextUpdate = taginfo->CheckinInMinPending;
+ if (taginfo->nextupdate > now + 2) {
+ minutesUntilNextUpdate = (taginfo->nextupdate - now) / 60;
if (minutesUntilNextUpdate > MIN_RESPONSE_TIME) minutesUntilNextUpdate = MIN_RESPONSE_TIME;
taginfo->expectedNextCheckin = now + 60 * minutesUntilNextUpdate + 60;
- if (minutesUntilNextUpdate > 0) prepareIdleReq(eadr->src, minutesUntilNextUpdate);
- taginfo->CheckinInMinPending = minutesUntilNextUpdate;
+ if (minutesUntilNextUpdate > 1 && taginfo->pending == false) prepareIdleReq(eadr->src, minutesUntilNextUpdate);
} else {
- taginfo->expectedNextCheckin = now + 60 * taginfo->CheckinInMinPending + 60;
+ taginfo->expectedNextCheckin = now + 60;
}
if (eadr->adr.lastPacketRSSI != 0) {
diff --git a/esp32_fw/src/web.cpp b/esp32_fw/src/web.cpp
index e18d6f74..0bdcf4be 100644
--- a/esp32_fw/src/web.cpp
+++ b/esp32_fw/src/web.cpp
@@ -284,7 +284,6 @@ void init_web() {
taginfo->modeConfigJson = request->getParam("modecfgjson", true)->value();
taginfo->contentMode = atoi(request->getParam("contentmode", true)->value().c_str());
taginfo->nextupdate = 0;
- taginfo->CheckinInMinPending = 0;
memset(taginfo->md5, 0, 16 * sizeof(uint8_t));
memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t));
wsSendTaginfo(mac);