diff --git a/esp32_fw/data/www/upload-test.html b/esp32_fw/data/www/upload-test.html
new file mode 100644
index 00000000..dc460b2f
--- /dev/null
+++ b/esp32_fw/data/www/upload-test.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+ Image Upload Form
+
+
+
+
+
+
+
diff --git a/esp32_fw/include/commstructs.h b/esp32_fw/include/commstructs.h
index 868548ec..d3753a64 100644
--- a/esp32_fw/include/commstructs.h
+++ b/esp32_fw/include/commstructs.h
@@ -37,7 +37,7 @@ struct AvailDataReq {
uint16_t batteryMv;
uint8_t hwType;
uint8_t wakeupReason;
- uint8_t capabilities; // undefined, as of now
+ uint8_t capabilities;
} __packed;
struct espAvailDataReq {
@@ -53,6 +53,8 @@ struct espAvailDataReq {
#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 DATATYPE_NFC_RAW_CONTENT 0xA0 // raw memory content for the NT3H1101
+#define DATATYPE_NFC_URL_DIRECT 0xA1 // URL format for NT3H1101
#define EPD_LUT_DEFAULT 0
#define EPD_LUT_NO_REPEATS 1
diff --git a/esp32_fw/include/contentmanager.h b/esp32_fw/include/contentmanager.h
index ef6a41d0..3a2d1978 100644
--- a/esp32_fw/include/contentmanager.h
+++ b/esp32_fw/include/contentmanager.h
@@ -13,20 +13,20 @@ struct contentTypes {
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);
+bool updateTagImage(String &filename, uint8_t *dst, uint16_t nextCheckin, imgParam &imageParams);
void drawString(TFT_eSprite &spr, String content, uint16_t posx, uint16_t posy, String font, byte align = 0, uint16_t color = TFT_BLACK);
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);
+void drawDate(String &filename, tagRecord *&taginfo, imgParam &imageParams);
+void drawNumber(String &filename, int32_t count, int32_t thresholdred, tagRecord *&taginfo, imgParam &imageParams);
+void drawWeather(String &filename, String location, tagRecord *&taginfo, imgParam &imageParams);
+void drawForecast(String &filename, String location, tagRecord *&taginfo, imgParam &imageParams);
+void drawIdentify(String &filename, tagRecord *&taginfo, imgParam &imageParams);
+bool getImgURL(String &filename, String URL, time_t fetched, imgParam &imageParams);
+bool getRSSfeed(String &filename, String URL, String title, tagRecord *&taginfo, imgParam &imageParams);
char *formatHttpDate(time_t t);
String urlEncode(const char *msg);
int windSpeedToBeaufort(float windSpeed);
diff --git a/esp32_fw/include/makeimage.h b/esp32_fw/include/makeimage.h
index 3363e945..d6d33029 100644
--- a/esp32_fw/include/makeimage.h
+++ b/esp32_fw/include/makeimage.h
@@ -3,34 +3,10 @@
#pragma once
-struct BitmapFileHeader {
- uint8_t sig[2];
- uint32_t fileSz;
- uint8_t rfu[4];
- uint32_t dataOfst;
- uint32_t headerSz; //40
- int32_t width;
- int32_t height;
- uint16_t colorplanes; //must be one
- uint16_t bpp;
- uint32_t compression;
- uint32_t dataLen; //may be 0
- uint32_t pixelsPerMeterX;
- uint32_t pixelsPerMeterY;
- uint32_t numColors; //if zero, assume 2^bpp
- uint32_t numImportantColors;
-
-} __attribute__((packed));
-
-enum EinkClut {
- EinkClutTwoBlacks = 0,
- EinkClutTwoBlacksAndRed,
- EinkClutFourBlacks,
- EinkClutThreeBlacksAndRed,
+struct imgParam {
+ bool hasRed;
+ uint8_t dataType;
};
-void spr2grays(TFT_eSprite &spr, long w, long h, String &fileout);
-void spr2buffer(TFT_eSprite &spr, String &fileout);
-void jpg2buffer(String filein, String fileout);
-void bmp2grays(String filein, String fileout);
-
+void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams);
+void jpg2buffer(String filein, String fileout, imgParam &imageParams);
diff --git a/esp32_fw/include/newproto.h b/esp32_fw/include/newproto.h
index 306c050b..9ecafc9e 100644
--- a/esp32_fw/include/newproto.h
+++ b/esp32_fw/include/newproto.h
@@ -6,7 +6,6 @@ extern bool checkCRC(void* p, uint8_t len);
extern void processBlockRequest(struct espBlockRequest* br);
extern void prepareIdleReq(uint8_t* dst, uint16_t nextCheckin);
extern bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t nextCheckin);
-extern void processJoinNetwork(struct espJoinNetwork* xjn);
extern void processXferComplete(struct espXferComplete* xfc);
extern void processXferTimeout(struct espXferComplete* xfc);
extern void processDataReq(struct espAvailDataReq* adr);
diff --git a/esp32_fw/src/contentmanager.cpp b/esp32_fw/src/contentmanager.cpp
index 95a3b58d..f16f6db4 100644
--- a/esp32_fw/src/contentmanager.cpp
+++ b/esp32_fw/src/contentmanager.cpp
@@ -78,12 +78,17 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
wsLog("Updating " + dst);
taginfo->nextupdate = now + 60;
- switch (taginfo->contentMode) {
+ imgParam imageParams;
+ imageParams.hasRed = false;
+ imageParams.dataType = DATATYPE_IMG_RAW_1BPP;
+
+ switch (taginfo->contentMode) {
case Image:
if (cfgobj["filename"].as() && cfgobj["filename"].as() != "null" && !cfgobj["#fetched"].as()) {
- jpg2buffer(cfgobj["filename"].as(), filename);
- if (prepareDataAvail(&filename, DATATYPE_IMG_RAW_2BPP, mac, cfgobj["timetolive"].as())) {
+ jpg2buffer(cfgobj["filename"].as(), filename, imageParams);
+ if (imageParams.hasRed) imageParams.dataType = DATATYPE_IMG_RAW_2BPP;
+ if (prepareDataAvail(&filename, imageParams.dataType, mac, cfgobj["timetolive"].as())) {
cfgobj["#fetched"] = true;
} else {
wsErr("Error accessing " + filename);
@@ -94,26 +99,26 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
case Today:
- drawDate(filename, taginfo);
+ drawDate(filename, taginfo, imageParams);
taginfo->nextupdate = midnight;
- updateTagImage(filename, mac, (midnight - now) / 60 - 10);
+ updateTagImage(filename, mac, (midnight - now) / 60 - 10, imageParams);
break;
case CountDays:
if (buttonPressed) cfgobj["counter"] = 0;
- drawNumber(filename, (int32_t)cfgobj["counter"], (int32_t)cfgobj["thresholdred"], taginfo);
+ drawNumber(filename, (int32_t)cfgobj["counter"], (int32_t)cfgobj["thresholdred"], taginfo, imageParams);
taginfo->nextupdate = midnight;
- updateTagImage(filename, mac, (buttonPressed ? 0 : 15));
+ updateTagImage(filename, mac, (buttonPressed ? 0 : 15), imageParams);
cfgobj["counter"] = (int32_t)cfgobj["counter"] + 1;
break;
case CountHours:
if (buttonPressed) cfgobj["counter"] = 0;
- drawNumber(filename, (int32_t)cfgobj["counter"], (int32_t)cfgobj["thresholdred"], taginfo);
+ drawNumber(filename, (int32_t)cfgobj["counter"], (int32_t)cfgobj["thresholdred"], taginfo, imageParams);
taginfo->nextupdate = now + 3600;
- updateTagImage(filename, mac, (buttonPressed ? 0 : 5));
+ updateTagImage(filename, mac, (buttonPressed ? 0 : 5), imageParams);
cfgobj["counter"] = (int32_t)cfgobj["counter"] + 1;
break;
@@ -124,16 +129,16 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
// https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41¤t_weather=true
// https://github.com/erikflowers/weather-icons
- drawWeather(filename, cfgobj["location"], taginfo);
+ drawWeather(filename, cfgobj["location"], taginfo, imageParams);
taginfo->nextupdate = now + 3600;
- updateTagImage(filename, mac, 15);
+ updateTagImage(filename, mac, 15, imageParams);
break;
case Forecast:
- drawForecast(filename, cfgobj["location"], taginfo);
+ drawForecast(filename, cfgobj["location"], taginfo, imageParams);
taginfo->nextupdate = now + 3 * 3600;
- updateTagImage(filename, mac, 15);
+ updateTagImage(filename, mac, 15, imageParams);
break;
case Firmware:
@@ -155,16 +160,16 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
case Memo:
- drawIdentify(filename, taginfo);
+ drawIdentify(filename, taginfo, imageParams);
taginfo->nextupdate = now + 12 * 3600;
- updateTagImage(filename, mac, 0);
+ updateTagImage(filename, mac, 0, imageParams);
break;
case ImageUrl:
- if (getImgURL(filename, cfgobj["url"], (time_t)cfgobj["#fetched"])) {
+ if (getImgURL(filename, cfgobj["url"], (time_t)cfgobj["#fetched"], imageParams)) {
taginfo->nextupdate = now + 60 * (cfgobj["interval"].as() < 5 ? 5 : cfgobj["interval"].as());
- updateTagImage(filename, mac, cfgobj["interval"].as());
+ updateTagImage(filename, mac, cfgobj["interval"].as(), imageParams);
cfgobj["#fetched"] = now;
} else {
taginfo->nextupdate = now + 300;
@@ -173,9 +178,9 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
case RSSFeed:
- if (getRSSfeed(filename, cfgobj["url"], cfgobj["title"], taginfo)) {
+ if (getRSSfeed(filename, cfgobj["url"], cfgobj["title"], taginfo, imageParams)) {
taginfo->nextupdate = now + 60 * (cfgobj["interval"].as() < 5 ? 5 : cfgobj["interval"].as());
- updateTagImage(filename, mac, cfgobj["interval"].as());
+ updateTagImage(filename, mac, cfgobj["interval"].as(), imageParams);
} else {
taginfo->nextupdate = now + 300;
}
@@ -185,8 +190,9 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
taginfo->modeConfigJson = doc.as();
}
-bool updateTagImage(String &filename, uint8_t *dst, uint16_t nextCheckin) {
- prepareDataAvail(&filename, DATATYPE_IMG_RAW_2BPP, dst, nextCheckin);
+bool updateTagImage(String &filename, uint8_t *dst, uint16_t nextCheckin, imgParam &imageParams) {
+ if (imageParams.hasRed) imageParams.dataType = DATATYPE_IMG_RAW_2BPP;
+ prepareDataAvail(&filename, imageParams.dataType, dst, nextCheckin);
return true;
}
@@ -208,8 +214,7 @@ void initSprite(TFT_eSprite &spr, int w, int h) {
spr.fillSprite(PAL_WHITE);
}
-void drawDate(String &filename, tagRecord *&taginfo) {
-
+void drawDate(String &filename, tagRecord *&taginfo, imgParam &imageParams) {
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite spr = TFT_eSprite(&tft);
time_t now;
@@ -238,12 +243,11 @@ void drawDate(String &filename, tagRecord *&taginfo) {
drawString(spr, String(timeinfo.tm_mday), 152 / 2, 42, "fonts/numbers2-1", TC_DATUM, PAL_RED);
}
- spr2buffer(spr, filename);
+ spr2buffer(spr, filename, imageParams);
spr.deleteSprite();
}
-void drawNumber(String &filename, int32_t count, int32_t thresholdred, tagRecord *&taginfo) {
-
+void drawNumber(String &filename, int32_t count, int32_t thresholdred, tagRecord *&taginfo, imgParam &imageParams) {
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite spr = TFT_eSprite(&tft);
LittleFS.begin();
@@ -282,12 +286,11 @@ void drawNumber(String &filename, int32_t count, int32_t thresholdred, tagRecord
}
- spr2buffer(spr, filename);
+ spr2buffer(spr, filename, imageParams);
spr.deleteSprite();
}
-void drawWeather(String &filename, String location, tagRecord *&taginfo) {
-
+void drawWeather(String &filename, String location, tagRecord *&taginfo, imgParam &imageParams) {
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite spr = TFT_eSprite(&tft);
@@ -417,14 +420,14 @@ void drawWeather(String &filename, String location, tagRecord *&taginfo) {
}
- spr2buffer(spr, filename);
+ spr2buffer(spr, filename, imageParams);
spr.deleteSprite();
}
}
http.end();
}
-void drawForecast(String &filename, String location, tagRecord *&taginfo) {
+void drawForecast(String &filename, String location, tagRecord *&taginfo, imgParam &imageParams) {
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite spr = TFT_eSprite(&tft);
@@ -520,15 +523,14 @@ void drawForecast(String &filename, String location, tagRecord *&taginfo) {
}
}
- spr2buffer(spr, filename);
+ spr2buffer(spr, filename, imageParams);
spr.deleteSprite();
}
}
http.end();
}
-void drawIdentify(String &filename, tagRecord *&taginfo) {
-
+void drawIdentify(String &filename, tagRecord *&taginfo, imgParam &imageParams) {
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite spr = TFT_eSprite(&tft);
LittleFS.begin();
@@ -544,11 +546,11 @@ void drawIdentify(String &filename, tagRecord *&taginfo) {
drawString(spr, mac62hex(taginfo->mac), 10, 50, "fonts/bahnschrift20", TL_DATUM, PAL_RED);
}
- spr2buffer(spr, filename);
+ spr2buffer(spr, filename, imageParams);
spr.deleteSprite();
}
-bool getImgURL(String &filename, String URL, time_t fetched) {
+bool getImgURL(String &filename, String URL, time_t fetched, imgParam &imageParams) {
// https://images.klari.net/kat-bw29.jpg
LittleFS.begin();
@@ -564,7 +566,7 @@ bool getImgURL(String &filename, String URL, time_t fetched) {
if (f) {
http.writeToStream(&f);
f.close();
- jpg2buffer("/temp/temp.jpg", filename);
+ jpg2buffer("/temp/temp.jpg", filename, imageParams);
}
} else {
if (httpCode!=304) {
@@ -579,7 +581,7 @@ bool getImgURL(String &filename, String URL, time_t fetched) {
rssClass reader;
-bool getRSSfeed(String &filename, String URL, String title, tagRecord *&taginfo) {
+bool getRSSfeed(String &filename, String URL, String title, tagRecord *&taginfo, imgParam &imageParams) {
// https://github.com/garretlab/shoddyxml2
// http://feeds.feedburner.com/tweakers/nieuws
@@ -627,7 +629,7 @@ bool getRSSfeed(String &filename, String URL, String title, tagRecord *&taginfo)
}
}
- spr2buffer(spr, filename);
+ spr2buffer(spr, filename, imageParams);
spr.deleteSprite();
return true;
diff --git a/esp32_fw/src/makeimage.cpp b/esp32_fw/src/makeimage.cpp
index 28d89016..e1179081 100644
--- a/esp32_fw/src/makeimage.cpp
+++ b/esp32_fw/src/makeimage.cpp
@@ -14,7 +14,7 @@ bool spr_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t *bitmap)
return 1;
}
-void jpg2buffer(String filein, String fileout) {
+void jpg2buffer(String filein, String fileout, imgParam &imageParams) {
LittleFS.begin();
TJpgDec.setSwapBytes(true);
TJpgDec.setJpgScale(1);
@@ -34,7 +34,7 @@ void jpg2buffer(String filein, String fileout) {
spr.fillSprite(TFT_WHITE);
TJpgDec.drawFsJpg(0, 0, filein, LittleFS);
- spr2buffer(spr, fileout);
+ spr2buffer(spr, fileout, imageParams);
spr.deleteSprite();
}
@@ -47,207 +47,6 @@ static uint32_t repackPackedVals(uint32_t val, uint32_t pixelsPerPackedUnit, uin
return ret;
}
-void spr2grays(TFT_eSprite &spr, long w, long h, String &fileout) {
- long t = millis();
- LittleFS.begin();
-
- fs::File f_out = LittleFS.open(fileout, "w");
-
- uint32_t c, rowBytesOut, rowBytesIn, outBpp, i, numRows, pixelsPerPackedUnit = 1, packedMultiplyVal = 0x01000000, packedOutBpp = 0;
- uint32_t numGrays, extraColor = 0;
- struct BitmapFileHeader hdr;
- memset(&hdr, 0, sizeof(hdr));
- enum EinkClut clutType;
- uint8_t clut[256][3];
- bool dither = true, rotated = false;
- int skipBytes;
- srand(0);
-
- clutType = EinkClutTwoBlacksAndRed;
-
- if (w > h) {
- hdr.width = h;
- hdr.height = w;
- rotated = true;
- } else {
- hdr.width = w;
- hdr.height = h;
- }
- hdr.bpp = 24;
- hdr.sig[0] = 'B';
- hdr.sig[1] = 'M';
- hdr.colorplanes = 1;
-
- switch (clutType) {
- case EinkClutTwoBlacks:
- numGrays = 2;
- outBpp = 1;
- break;
-
- case EinkClutTwoBlacksAndRed:
- extraColor = 0xff0000;
- numGrays = 2;
- outBpp = 2;
- break;
-
- case EinkClutFourBlacks:
- numGrays = 4;
- outBpp = 2;
- break;
-
- case EinkClutThreeBlacksAndRed:
- numGrays = 3;
- extraColor = 0xff0000;
- outBpp = 2;
- break;
- }
-
- packedOutBpp = outBpp;
-
- rowBytesIn = (hdr.width * hdr.bpp + 31) / 32 * 4;
- rowBytesOut = ((hdr.width + pixelsPerPackedUnit - 1) / pixelsPerPackedUnit) * packedOutBpp;
- rowBytesOut = (rowBytesOut + 31) / 32 * 4;
-
- numRows = hdr.height < 0 ? -hdr.height : hdr.height;
- hdr.bpp = outBpp;
- hdr.numColors = 1 << outBpp;
- hdr.numImportantColors = 1 << outBpp;
- hdr.dataOfst = sizeof(struct BitmapFileHeader) + 4 * hdr.numColors;
- hdr.dataLen = numRows * rowBytesOut;
- hdr.fileSz = hdr.dataOfst + hdr.dataLen;
- hdr.headerSz = 40;
- hdr.compression = 0;
-
- f_out.write((uint8_t *)&hdr, sizeof(hdr));
-
- // emit & record grey clut entries
- for (i = 0; i < numGrays; i++) {
- uint32_t val = 255 * i / (numGrays - 1);
-
- f_out.write(val);
- f_out.write(val);
- f_out.write(val);
- f_out.write(val);
-
- clut[i][0] = val;
- clut[i][1] = val;
- clut[i][2] = val;
- }
-
- if (extraColor) {
- f_out.write((extraColor >> 0) & 0xff); // B
- f_out.write((extraColor >> 8) & 0xff); // G
- f_out.write((extraColor >> 16) & 0xff); // R
- f_out.write(0x00); // A
-
- clut[i][0] = (extraColor >> 0) & 0xff;
- clut[i][1] = (extraColor >> 8) & 0xff;
- clut[i][2] = (extraColor >> 16) & 0xff;
- }
-
- // pad clut to size
- for (i = numGrays + (extraColor ? 1 : 0); i < hdr.numColors; i++) {
- f_out.write(0x00);
- f_out.write(0x00);
- f_out.write(0x00);
- f_out.write(0x00);
- }
-
- 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;
-
- for (c = 0; c < hdr.width; c++, bytesIn += 3) {
- int64_t bestDist = 0x7fffffffffffffffll;
- uint8_t bestIdx = 0;
- int32_t ditherFudge = 0;
- uint16_t color565;
- if (rotated) {
- color565 = spr.readPixel(hdr.height - 1 - numRows, c);
- } else {
- color565 = spr.readPixel(c, numRows);
- }
-
- uint8_t red = ((color565 >> 11) & 0x1F) * 8;
- uint8_t green = ((color565 >> 5) & 0x3F) * 4;
- uint8_t blue = (color565 & 0x1F) * 8;
-
- if (dither) {
- // ditherFudge = (rand() % 255 - 127) / (int)numGrays; // -64 to 64
- ditherFudge = (dither_matrix[numRows % 4][c % 4] - 8) * 16 / (int)numGrays;
- }
-
- for (i = 0; i < hdr.numColors; i++) {
- int64_t dist = 0;
-
- dist += (blue - clut[i][0] + ditherFudge) * (blue - clut[i][0] + ditherFudge) * 4750ll;
- dist += (green - clut[i][1] + ditherFudge) * (green - clut[i][1] + ditherFudge) * 47055ll;
- dist += (red - clut[i][2] + ditherFudge) * (red - clut[i][2] + ditherFudge) * 13988ll;
-
- if (dist < bestDist) {
- bestDist = dist;
- bestIdx = i;
- }
- }
-
- // pack pixels as needed
- pixelValsPackedSoFar = pixelValsPackedSoFar * packedMultiplyVal + bestIdx;
- if (++numPixelsPackedSoFar != pixelsPerPackedUnit)
- continue;
-
- numPixelsPackedSoFar = 0;
-
- // it is easier to display when low val is first pixel. currently last pixel is low - reverse this
- pixelValsPackedSoFar = repackPackedVals(pixelValsPackedSoFar, pixelsPerPackedUnit, packedMultiplyVal);
-
- valSoFar = (valSoFar << packedOutBpp) | pixelValsPackedSoFar;
- pixelValsPackedSoFar = 0;
- bitsSoFar += packedOutBpp;
-
- if (bitsSoFar >= 8) {
- f_out.write(valSoFar >> (bitsSoFar -= 8));
- valSoFar &= (1 << bitsSoFar) - 1;
- bytesOut++;
- }
- }
-
- // see if we have unfinished pixel packages to write
- if (numPixelsPackedSoFar) {
- while (numPixelsPackedSoFar++ != pixelsPerPackedUnit)
- pixelValsPackedSoFar *= packedMultiplyVal;
-
- // it is easier to display when low val is first pixel. currently last pixel is low - reverse this
- pixelValsPackedSoFar = repackPackedVals(pixelValsPackedSoFar, pixelsPerPackedUnit, packedMultiplyVal);
-
- valSoFar = (valSoFar << packedOutBpp) | pixelValsPackedSoFar;
- pixelValsPackedSoFar = 0;
- bitsSoFar += packedOutBpp;
-
- if (bitsSoFar >= 8) {
- f_out.write(valSoFar >> (bitsSoFar -= 8));
- valSoFar &= (1 << bitsSoFar) - 1;
- bytesOut++;
- }
- }
-
- if (bitsSoFar) {
- valSoFar <<= 8 - bitsSoFar; // left-align it as is expected
- f_out.write(valSoFar);
- bytesOut++;
- }
-
- while (bytesOut++ < rowBytesOut)
- f_out.write(0);
- }
- f_out.close();
- Serial.println("finished writing BMP " + String(millis() - t) + "ms");
-}
-
struct Color {
uint8_t r, g, b;
Color() : r(0), g(0), b(0) {}
@@ -268,7 +67,7 @@ uint32_t colorDistance(const Color &c1, const Color &c2, const Error &e1) {
return round(r_diff * r_diff + g_diff * g_diff + b_diff * b_diff);
}
-void spr2buffer(TFT_eSprite &spr, String &fileout) {
+void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
long t = millis();
LittleFS.begin();
@@ -324,6 +123,7 @@ void spr2buffer(TFT_eSprite &spr, String &fileout) {
if (best_color_index & 1) {
blackBuffer[byteIndex] |= (1 << bitIndex);
} else if (best_color_index & 2) {
+ imageParams.hasRed = true;
redBuffer[byteIndex] |= (1 << bitIndex);
}
@@ -353,7 +153,7 @@ void spr2buffer(TFT_eSprite &spr, String &fileout) {
}
f_out.write(blackBuffer, bufferSize);
- f_out.write(redBuffer, bufferSize);
+ if (imageParams.hasRed) f_out.write(redBuffer, bufferSize);
delete[] blackBuffer;
delete[] redBuffer;
@@ -361,195 +161,3 @@ void spr2buffer(TFT_eSprite &spr, String &fileout) {
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
-
- long t = millis();
- LittleFS.begin();
-
- fs::File f_in = LittleFS.open(filein, "r");
- fs::File f_out = LittleFS.open(fileout, "w");
-
- uint32_t c, rowBytesOut, rowBytesIn, outBpp, i, numRows, pixelsPerPackedUnit = 1, packedMultiplyVal = 0x01000000, packedOutBpp = 0;
- uint32_t numGrays, extraColor = 0;
- struct BitmapFileHeader hdr;
- enum EinkClut clutType;
- uint8_t clut[256][3];
- bool dither = false;
- int skipBytes;
- srand(0);
-
- clutType = EinkClutTwoBlacksAndRed;
-
- f_in.read((uint8_t *)&hdr, sizeof(hdr));
-
- if (hdr.sig[0] != 'B' || hdr.sig[1] != 'M' || hdr.headerSz < 40 || hdr.colorplanes != 1 || hdr.bpp != 24 || hdr.compression) {
- Serial.println("BITMAP HEADER INVALID, use uncompressed 24 bits RGB");
- return;
- }
-
- switch (clutType) {
- case EinkClutTwoBlacks:
- numGrays = 2;
- outBpp = 1;
- break;
-
- case EinkClutTwoBlacksAndRed:
- extraColor = 0xff0000;
- numGrays = 2;
- outBpp = 2;
- break;
-
- case EinkClutFourBlacks:
- numGrays = 4;
- outBpp = 2;
- break;
-
- case EinkClutThreeBlacksAndRed:
- numGrays = 3;
- extraColor = 0xff0000;
- outBpp = 2;
- break;
- }
-
- packedOutBpp = outBpp;
-
- skipBytes = hdr.dataOfst - sizeof(hdr);
- if (skipBytes < 0) {
- fprintf(stderr, "file header was too short!\n");
- exit(-1);
- }
- f_in.read(NULL, skipBytes);
-
- rowBytesIn = (hdr.width * hdr.bpp + 31) / 32 * 4;
- rowBytesOut = ((hdr.width + pixelsPerPackedUnit - 1) / pixelsPerPackedUnit) * packedOutBpp;
- rowBytesOut = (rowBytesOut + 31) / 32 * 4;
-
- numRows = hdr.height < 0 ? -hdr.height : hdr.height;
- hdr.bpp = outBpp;
- hdr.numColors = 1 << outBpp;
- hdr.numImportantColors = 1 << outBpp;
- hdr.dataOfst = sizeof(struct BitmapFileHeader) + 4 * hdr.numColors;
- hdr.dataLen = numRows * rowBytesOut;
- hdr.fileSz = hdr.dataOfst + hdr.dataLen;
- hdr.headerSz = 40;
- hdr.compression = 0;
-
- f_out.write((uint8_t *)&hdr, sizeof(hdr));
-
- // emit & record grey clut entries
- for (i = 0; i < numGrays; i++) {
- uint32_t val = 255 * i / (numGrays - 1);
-
- f_out.write(val);
- f_out.write(val);
- f_out.write(val);
- f_out.write(val);
-
- clut[i][0] = val;
- clut[i][1] = val;
- clut[i][2] = val;
- }
-
- // if there is a color CLUT entry, emit that
- if (extraColor) {
- f_out.write((extraColor >> 0) & 0xff); // B
- f_out.write((extraColor >> 8) & 0xff); // G
- f_out.write((extraColor >> 16) & 0xff); // R
- f_out.write(0x00); // A
-
- clut[i][0] = (extraColor >> 0) & 0xff;
- clut[i][1] = (extraColor >> 8) & 0xff;
- clut[i][2] = (extraColor >> 16) & 0xff;
- }
-
- // pad clut to size
- for (i = numGrays + (extraColor ? 1 : 0); i < hdr.numColors; i++) {
- f_out.write(0x00);
- f_out.write(0x00);
- f_out.write(0x00);
- f_out.write(0x00);
- }
-
- while (numRows--) {
- uint32_t pixelValsPackedSoFar = 0, numPixelsPackedSoFar = 0, valSoFar = 0, bytesIn = 0, bytesOut = 0, bitsSoFar = 0;
-
- for (c = 0; c < hdr.width; c++, bytesIn += 3) {
- int64_t bestDist = 0x7fffffffffffffffll;
- uint8_t rgb[3], bestIdx = 0;
- int32_t ditherFudge = 0;
-
- f_in.read(rgb, sizeof(rgb));
-
- if (dither)
- ditherFudge = (rand() % 255 - 127) / (int)numGrays;
-
- for (i = 0; i < hdr.numColors; i++) {
- int64_t dist = 0;
-
- dist += (rgb[0] - clut[i][0] + ditherFudge) * (rgb[0] - clut[i][0] + ditherFudge) * 4750ll;
- dist += (rgb[1] - clut[i][1] + ditherFudge) * (rgb[1] - clut[i][1] + ditherFudge) * 47055ll;
- dist += (rgb[2] - clut[i][2] + ditherFudge) * (rgb[2] - clut[i][2] + ditherFudge) * 13988ll;
-
- if (dist < bestDist) {
- bestDist = dist;
- bestIdx = i;
- }
- }
-
- // pack pixels as needed
- pixelValsPackedSoFar = pixelValsPackedSoFar * packedMultiplyVal + bestIdx;
- if (++numPixelsPackedSoFar != pixelsPerPackedUnit)
- continue;
-
- numPixelsPackedSoFar = 0;
-
- // it is easier to display when low val is first pixel. currently last pixel is low - reverse this
- pixelValsPackedSoFar = repackPackedVals(pixelValsPackedSoFar, pixelsPerPackedUnit, packedMultiplyVal);
-
- valSoFar = (valSoFar << packedOutBpp) | pixelValsPackedSoFar;
- pixelValsPackedSoFar = 0;
- bitsSoFar += packedOutBpp;
-
- if (bitsSoFar >= 8) {
- f_out.write(valSoFar >> (bitsSoFar -= 8));
- valSoFar &= (1 << bitsSoFar) - 1;
- bytesOut++;
- }
- }
-
- // see if we have unfinished pixel packages to write
- if (numPixelsPackedSoFar) {
- while (numPixelsPackedSoFar++ != pixelsPerPackedUnit)
- pixelValsPackedSoFar *= packedMultiplyVal;
-
- // it is easier to display when low val is first pixel. currently last pixel is low - reverse this
- pixelValsPackedSoFar = repackPackedVals(pixelValsPackedSoFar, pixelsPerPackedUnit, packedMultiplyVal);
-
- valSoFar = (valSoFar << packedOutBpp) | pixelValsPackedSoFar;
- pixelValsPackedSoFar = 0;
- bitsSoFar += packedOutBpp;
-
- if (bitsSoFar >= 8) {
- f_out.write(valSoFar >> (bitsSoFar -= 8));
- valSoFar &= (1 << bitsSoFar) - 1;
- bytesOut++;
- }
- }
-
- if (bitsSoFar) {
- valSoFar <<= 8 - bitsSoFar; // left-align it as is expected
- f_out.write(valSoFar);
- bytesOut++;
- }
-
- while (bytesIn++ < rowBytesIn)
- f_in.read(NULL, 1);
- while (bytesOut++ < rowBytesOut)
- f_out.write(0);
- }
- f_in.close();
- f_out.close();
- Serial.println("finished converting BMP " + String(millis() - t) + "ms");
-}
\ No newline at end of file
diff --git a/esp32_fw/src/newproto.cpp b/esp32_fw/src/newproto.cpp
index 3f438102..1932185f 100644
--- a/esp32_fw/src/newproto.cpp
+++ b/esp32_fw/src/newproto.cpp
@@ -328,8 +328,8 @@ 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;
+ if (eadr->adr.wakeupReason >= 0xF0) {
+ if (!taginfo->pending) taginfo->nextupdate = 0;
memset(taginfo->md5, 0, 16 * sizeof(uint8_t));
memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t));
}
diff --git a/esp32_fw/src/web.cpp b/esp32_fw/src/web.cpp
index 4da1b13f..496e2909 100644
--- a/esp32_fw/src/web.cpp
+++ b/esp32_fw/src/web.cpp
@@ -285,8 +285,12 @@ void init_web() {
void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
if (!index) {
+ if (request->hasParam("mac", true)) {
+ filename = request->getParam("mac", true)->value() + ".jpg";
+ } else {
+ filename = "unknown.jpg";
+ }
Serial.print((String) "UploadStart: " + filename);
- // open the file on first call and store the file handle in the request object
request->_tempFile = LittleFS.open("/" + filename, "w");
}
if (len) {
@@ -294,15 +298,26 @@ void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index
request->_tempFile.write(data, len);
}
if (final) {
- Serial.print((String) "UploadEnd: " + filename + "," + index + len);
- // close the file handle as the upload is now done
+ Serial.println((String) " End: " + filename + ", " + index + len);
request->_tempFile.close();
- request->send(200, "text/plain", "File Uploaded !");
- /*
- sscanf() if (request->hasParam("id") && request->hasParam("file")) {
- id = request->getParam("id")->value().toInt();
- filename = request->getParam("file")->value();
+ if (request->hasParam("mac", true)) {
+ String dst = request->getParam("mac", true)->value();
+ uint8_t mac[6];
+ if (sscanf(dst.c_str(), "%02X%02X%02X%02X%02X%02X", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) == 6) {
+ tagRecord *taginfo = nullptr;
+ taginfo = tagRecord::findByMAC(mac);
+ if (taginfo != nullptr) {
+ taginfo->modeConfigJson = "{\"filename\":\"" + dst + ".jpg\",\"timetolive\":\"0\"}";
+ taginfo->contentMode = 0;
+ taginfo->nextupdate = 0;
+ wsSendTaginfo(mac);
+ request->send(200, "text/plain", "Ok, saved");
+ } else {
+ request->send(200, "text/plain", "Error while saving: mac not found");
}
- */
+ }
+ } else {
+ request->send(500, "text/plain", "parameters incomplete");
+ }
}
}
\ No newline at end of file