From 52053386350b99f395369ddb190da762a6645646 Mon Sep 17 00:00:00 2001 From: Nic Limper Date: Wed, 22 Mar 2023 19:30:25 +0100 Subject: [PATCH] external image upload, 1bpp buffer, cleanup - if image only has BW, then sending red buffer is skipped - jpg image upload from external source: see /upload-test.html --- esp32_fw/data/www/upload-test.html | 19 ++ esp32_fw/include/commstructs.h | 4 +- esp32_fw/include/contentmanager.h | 18 +- esp32_fw/include/makeimage.h | 34 +-- esp32_fw/include/newproto.h | 1 - esp32_fw/src/contentmanager.cpp | 80 +++--- esp32_fw/src/makeimage.cpp | 402 +---------------------------- esp32_fw/src/newproto.cpp | 4 +- esp32_fw/src/web.cpp | 33 ++- 9 files changed, 108 insertions(+), 487 deletions(-) create mode 100644 esp32_fw/data/www/upload-test.html 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