From 4107d83bc20db4b57589db417609c04aabf9dd2d Mon Sep 17 00:00:00 2001 From: Thorinair Date: Fri, 24 Jul 2020 13:33:25 +0200 Subject: [PATCH] Bitmap from web, first working prototype. --- Inkplate.cpp | 170 +++++++++++++++++- Inkplate.h | 12 +- .../10-Inkplate_Download_And_Show.ino | 157 ++++++++++++++++ 3 files changed, 330 insertions(+), 9 deletions(-) create mode 100644 examples/2. Advanced Inkplate Features/10-Inkplate_Download_And_Show/10-Inkplate_Download_And_Show.ino diff --git a/Inkplate.cpp b/Inkplate.cpp index 5bb7d15..2ed7084 100644 --- a/Inkplate.cpp +++ b/Inkplate.cpp @@ -1,6 +1,8 @@ #include #include "Adafruit_GFX.h" +#include "WiFi.h" +#include "HTTPClient.h" #include "Inkplate.h" Adafruit_MCP23017 mcp; SPIClass spi2(HSPI); @@ -331,7 +333,7 @@ uint8_t Inkplate::getDisplayMode() { int Inkplate::drawBitmapFromSD(SdFile* p, int x, int y) { if(sdCardOk == 0) return 0; struct bitmapHeader bmpHeader; - readBmpHeader(p, &bmpHeader); + readBmpHeaderSd(p, &bmpHeader); if (bmpHeader.signature != 0x4D42 || bmpHeader.compression != 0 || !(bmpHeader.color == 1 || bmpHeader.color == 24)) return 0; if ((bmpHeader.color == 24 || bmpHeader.color == 32) && getDisplayMode() != INKPLATE_3BIT) { @@ -342,8 +344,8 @@ int Inkplate::drawBitmapFromSD(SdFile* p, int x, int y) { selectDisplayMode(INKPLATE_1BIT); } - if (bmpHeader.color == 1) drawMonochromeBitmap(p, bmpHeader, x, y); - if (bmpHeader.color == 24) drawGrayscaleBitmap24(p, bmpHeader, x, y); + if (bmpHeader.color == 1) drawMonochromeBitmapSd(p, bmpHeader, x, y); + if (bmpHeader.color == 24) drawGrayscaleBitmap24Sd(p, bmpHeader, x, y); return 1; } @@ -358,6 +360,44 @@ int Inkplate::drawBitmapFromSD(char* fileName, int x, int y) { } } +int Inkplate::drawBitmapFromWeb(WiFiClient* s, int x, int y, int len) { + struct bitmapHeader bmpHeader; + readBmpHeaderWeb(s, &bmpHeader); + if (bmpHeader.signature != 0x4D42 || bmpHeader.compression != 0 || !(bmpHeader.color == 1 || bmpHeader.color == 24)) return 0; + + if ((bmpHeader.color == 24 || bmpHeader.color == 32) && getDisplayMode() != INKPLATE_3BIT) { + selectDisplayMode(INKPLATE_3BIT); + } + + if (bmpHeader.color == 1 && getDisplayMode() != INKPLATE_1BIT) { + selectDisplayMode(INKPLATE_1BIT); + } + + if (bmpHeader.color == 1) drawMonochromeBitmapWeb(s, bmpHeader, x, y, len); + if (bmpHeader.color == 24) drawGrayscaleBitmap24Web(s, bmpHeader, x, y, len); + + return 1; +} + +int Inkplate::drawBitmapFromWeb(char* url, int x, int y) { + if (WiFi.status() != WL_CONNECTED) return 0; + HTTPClient http; + + http.getStream().setNoDelay(true); + //http.getStream().setTimeout(1); + http.begin(url); + int httpCode = http.GET(); + if (httpCode != 200) return 0; + + int32_t len = http.getSize(); + if (len <= 0) return 0; + + WiFiClient * dat = http.getStreamPtr(); + //dat->setTimeout(20); + //dat->setNoDelay(true); + return drawBitmapFromWeb(dat, x, y, len); +} + int Inkplate::sdCardInit() { spi2.begin(14, 12, 13, 15); sdCardOk = sd.begin(15, SD_SCK_MHZ(25)); @@ -768,7 +808,7 @@ uint16_t Inkplate::read16(uint8_t* c) { return (*(c) | (*(c + 1) << 8)); } -void Inkplate::readBmpHeader(SdFile *_f, struct bitmapHeader *_h) { +void Inkplate::readBmpHeaderSd(SdFile *_f, struct bitmapHeader *_h) { uint8_t header[100]; _f->rewind(); _f->read(header, 100); @@ -783,7 +823,21 @@ void Inkplate::readBmpHeader(SdFile *_f, struct bitmapHeader *_h) { return; } -int Inkplate::drawMonochromeBitmap(SdFile *f, struct bitmapHeader bmpHeader, int x, int y) { +void Inkplate::readBmpHeaderWeb(WiFiClient *_s, struct bitmapHeader *_h) { + uint8_t header[34]; + _s->read(header, 34); + _h->signature = read16(header + 0); + _h->fileSize = read32(header + 2); + _h->startRAW = read32(header + 10); + _h->dibHeaderSize = read32(header + 14); + _h->width = read32(header + 18); + _h->height = read32(header + 22); + _h->color = read16(header + 28); + _h->compression = read32(header + 30); + return; +} + +int Inkplate::drawMonochromeBitmapSd(SdFile *f, struct bitmapHeader bmpHeader, int x, int y) { int w = bmpHeader.width; int h = bmpHeader.height; uint8_t paddingBits = w % 32; @@ -809,7 +863,7 @@ int Inkplate::drawMonochromeBitmap(SdFile *f, struct bitmapHeader bmpHeader, int return 1; } -int Inkplate::drawGrayscaleBitmap24(SdFile *f, struct bitmapHeader bmpHeader, int x, int y) { +int Inkplate::drawGrayscaleBitmap24Sd(SdFile *f, struct bitmapHeader bmpHeader, int x, int y) { int w = bmpHeader.width; int h = bmpHeader.height; char padding = w % 4; @@ -838,6 +892,110 @@ int Inkplate::drawGrayscaleBitmap24(SdFile *f, struct bitmapHeader bmpHeader, in return 1; } +int Inkplate::drawMonochromeBitmapWeb(WiFiClient *s, struct bitmapHeader bmpHeader, int x, int y, int len) { + int w = bmpHeader.width; + int h = bmpHeader.height; + uint8_t paddingBits = w % 32; + w /= 32; + + uint8_t buffer[100]; + s->readBytes(buffer, bmpHeader.startRAW); + int i, j; + for (j = 0; j < h; j++) { + for (i = 0; i < w; i++) { + uint32_t pixelRow = s->read() << 24 | s->read() << 16 | s->read() << 8 | s->read(); + for (int n = 0; n < 32; n++) { + drawPixel((i * 32) + n + x, h - j + y, !(pixelRow & (1ULL << (31 - n)))); + } + } + if (paddingBits) { + uint32_t pixelRow = s->read() << 24 | s->read() << 16 | s->read() << 8 | s->read(); + for (int n = 0; n < paddingBits; n++) { + drawPixel((i * 32) + n + x, h - j + y, !(pixelRow & (1ULL << (31 - n)))); + } + } + } + return 1; +} + +int Inkplate::drawGrayscaleBitmap24Web(WiFiClient *s, struct bitmapHeader bmpHeader, int x, int y, int len) { + int w = bmpHeader.width; + int h = bmpHeader.height; + char padding = w % 4; + + Serial.println(len); + Serial.println(bmpHeader.startRAW); + + int total = len - 34; + + uint8_t* buf = (uint8_t*) ps_malloc(total); + if (buf == NULL) + return 0; + + long t_start = millis(); + //int i, j; + //size_t read = s->read(buf, len - 34); + //for (i = 0; i < len - 34; i++) + // s->read(); + int read; + int pnt = 0; + int cnk = 512; + while (pnt < total) { + if (total - pnt < cnk) + cnk = total - pnt; + read = s->read(buf+pnt, cnk); + if (read > 0) { + pnt += read; + Serial.println(" Read: " + String(read)); + } + //delay(10); + } + + //while (pnt < total) { + // if (total - pnt < cnk) + // cnk = total - pnt; + // s->read(buf+pnt, cnk); + // pnt += cnk; + // Serial.println(" Read: " + String(cnk)); + //} + //for (j = 0; j < h; j++) { + // size_t read = s->read(buf, 64); + // Serial.println(" Read: " + String(read)); + // delay(1); + //} + long t_stop = millis(); + + Serial.println("Time: " + String(t_stop - t_start)); + + int i, j, k = bmpHeader.startRAW - 34; + for (j = 0; j < h; j++) { + for (i = 0; i < w; i++) { + uint8_t r = buf[k++]; + uint8_t g = buf[k++]; + uint8_t b = buf[k++]; + //This is the proper way of converting True Color (24 Bit RGB) bitmap file into grayscale, but it takes waaay too much time (full size picture takes about 17s to decode!) + //float px = (0.2126 * (readByteFromSD(&file) / 255.0)) + (0.7152 * (readByteFromSD(&file) / 255.0)) + (0.0722 * (readByteFromSD(&file) / 255.0)); + //px = pow(px, 1.5); + //display.drawPixel(i + x, h - j + y, (uint8_t)(px*7)); + + //So then, we are convertng it to grayscale using good old average and gamma correction (from LUT). With this metod, it is still slow (full size image takes 4 seconds), but much beter than prev mentioned method. + uint8_t px = (r * 2126 / 10000) + (g * 7152 / 10000) + (b * 722 / 10000); + //drawPixel(i + x, h - j + y, gammaLUT[px]); + drawPixel(i + x, h - j + y, px>>5); + //drawPixel(i + x, h - j + y, px/32); + } + if (padding) { + for (int p = 0; p < padding; p++) { + k++; + } + } + } + + free(buf); + + return 1; +} + void Inkplate::precalculateGamma(uint8_t* c, float gamma) { for (int i = 0; i < 256; i++) { c[i] = int(round((pow(i / 255.0, gamma)) * 15)); diff --git a/Inkplate.h b/Inkplate.h index 90cc3eb..1a63316 100644 --- a/Inkplate.h +++ b/Inkplate.h @@ -15,6 +15,7 @@ #include "SPI.h" #include "Adafruit_MCP23017.h" #include "SdFat.h" +#include "WiFiClient.h" #define INKPLATE_GAMMA 1.45 #define E_INK_WIDTH 800 @@ -132,6 +133,8 @@ class Inkplate : public Adafruit_GFX { uint8_t getDisplayMode(); int drawBitmapFromSD(SdFile* p, int x, int y); int drawBitmapFromSD(char* fileName, int x, int y); + int drawBitmapFromWeb(WiFiClient* s, int x, int y, int len); + int drawBitmapFromWeb(char* url, int x, int y); int sdCardInit(); SdFat getSdFat(); SPIClass getSPI(); @@ -163,9 +166,12 @@ class Inkplate : public Adafruit_GFX { void display3b(); uint32_t read32(uint8_t* c); uint16_t read16(uint8_t* c); - void readBmpHeader(SdFile *_f, struct bitmapHeader *_h); - int drawMonochromeBitmap(SdFile *f, struct bitmapHeader bmpHeader, int x, int y); - int drawGrayscaleBitmap24(SdFile *f, struct bitmapHeader bmpHeader, int x, int y); + void readBmpHeaderSd(SdFile *_f, struct bitmapHeader *_h); + void readBmpHeaderWeb(WiFiClient *_s, struct bitmapHeader *_h); + int drawMonochromeBitmapSd(SdFile *f, struct bitmapHeader bmpHeader, int x, int y); + int drawGrayscaleBitmap24Sd(SdFile *f, struct bitmapHeader bmpHeader, int x, int y); + int drawMonochromeBitmapWeb(WiFiClient *s, struct bitmapHeader bmpHeader, int x, int y, int len); + int drawGrayscaleBitmap24Web(WiFiClient *s, struct bitmapHeader bmpHeader, int x, int y, int len); void precalculateGamma(uint8_t* c, float gamma); }; diff --git a/examples/2. Advanced Inkplate Features/10-Inkplate_Download_And_Show/10-Inkplate_Download_And_Show.ino b/examples/2. Advanced Inkplate Features/10-Inkplate_Download_And_Show/10-Inkplate_Download_And_Show.ino new file mode 100644 index 0000000..a0fac3c --- /dev/null +++ b/examples/2. Advanced Inkplate Features/10-Inkplate_Download_And_Show/10-Inkplate_Download_And_Show.ino @@ -0,0 +1,157 @@ +/* + 10_Inkplate_Download_And_Show example for e-radionica Inkplate6 + For this example you will need a micro USB cable, Inkplate6, an SD card and an + available WiFi connection. + Select "Inkplate 6(ESP32)" from Tools -> Board menu. + Don't have "Inkplate 6(ESP32)" option? Follow our tutorial and add it: + https://e-radionica.com/en/blog/add-inkplate-6-to-arduino-ide/ + + To work with SD card on Inkplate, you will need to add one extra library. + Download and install it from here: https://github.com/e-radionicacom/Inkplate-6-SDFat-Arduino-Library + + You can open .bmp files that have color depth of 1 bit (monochrome bitmap) and + 24 bit AND have resoluton smaller than 800x600 or otherwise it won't fit on screen. + + This example will show you how you can download a .bmp file (picture) from the web to the SD card and + display that image on e-paper display. + + Want to learn more about Inkplate? Visit www.inkplate.io + Looking to get support? Write on our forums: http://forum.e-radionica.com/en/ + 23 July 2020 by e-radionica.com +*/ + +#include "Inkplate.h" //Include Inkplate library to the sketch +#include "SdFat.h" //Include library for SD card +#include "WiFi.h" //Include library for WiFi +#include "HTTPClient.h" //Include library for HTTP downloading +Inkplate display(INKPLATE_1BIT); //Create an object on Inkplate library and also set library into 1 Bit mode (Monochrome) +SdFile file; //Create SdFile object used for accessing files on SD card + +const char* ssid = "Twilight Sparkle"; //Your WiFi SSID +const char* password = "thori4twily"; //Your WiFi password + +//Photo taken by: Paulian Prajitura +char* url = "https://dl.thorinair.net/neowise2.bmp"; //URL of image to download + +void setup() { + Serial.begin(115200); + + display.begin(); //Init Inkplate library (you should call this function ONLY ONCE) + display.clearDisplay(); //Clear frame buffer of display + display.display(); //Put clear image on display + + display.println("Connecting to WiFi..."); + display.partialUpdate(); + + //Connect to the WiFi network. + WiFi.begin(ssid, password); + WiFi.setSleep(false); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + display.println("WiFi OK! Downloading..."); + display.partialUpdate(); + + if(!display.drawBitmapFromWeb(url, 0, 0)) { + //If is something failed (wrong filename or wrong bitmap format), write error message on the screen. + //REMEMBER! You can only use Windows Bitmap file with color depth of 1 or 24 bits with no compression! + display.println("Image open error"); + display.display(); + } + display.display(); + + //HTTPClient http; +// + //http.begin(url); +// + ////Get the image, check if connection was successful. + //uint32_t httpCode = http.GET(); + //if (httpCode == 200) { + // + // display.println("HTTP opened! Size: " + String(len) + " - Saving to SD..."); + // display.partialUpdate(); +// +// +// + // //String str = http.getString(); + // //int str_len = http.getSize(); + // //char char_array[str_len]; + // //str.toCharArray(char_array, str_len); + // //http.end(); +// + // SdFat sd = display.getSdFat(); + // sd.remove("image.bmp"); + // File image = sd.open("image.bmp", O_CREAT | O_WRITE); + // if (image) { +// +// +// + // http.writeToStream(&stream); +//// + // //String body = http.getString(); +//// + // //for (int i = 0; i < http.getSize(); i++) { + // // //Serial.print(body[i]); + // // image.write(body[i]); + // //} +// + // //WiFiClient* stream = http.getStreamPtr(); + // //while (stream->available()) { + // // uint8_t c = stream->read(); + // // //Serial.print(c); + // // image.write(c); + // //} +//// + // ////image.write(char_array, str_len); //Write HTTP content to file +// + // //WiFiClient * stream = http.getStreamPtr(); + // //byte buffer[len]; + // //stream->readBytes(buffer, len); + // //for (int i = 0; i < len; i++) + // // image.write(buffer[i]); +// + // //uint32_t pos = 0; + // //char buff[2048]; + // //while (pos < len && http.available()) { + // // ESP.wdtFeed(); + // // yield(); + // // http.seek(pos, fs::SeekMode::SeekSet); + // // const auto read = http.readBytes(buff, sizeof(buff)); + // // pos += read; + // // image.write(buff, read); + // //} +// + // image.close(); //Close the file + // display.println("Image saved!"); + // display.partialUpdate(); +// + // delay(1000); +// + // //Load and display the image on e-paper at position X=0, Y=0 + // if(!display.drawBitmapFromSD("image.bmp", 0, 0)) { + // //If is something failed (wrong filename or wrong bitmap format), write error message on the screen. + // //REMEMBER! You can only use Windows Bitmap file with color depth of 1 or 24 bits with no compression! + // display.println("Image open error"); + // display.display(); + // } + // display.display(); + // } + // else { + // //If image file was not open successfully, display error on screen + // display.println("File error!"); + // display.partialUpdate(); + // return; + // } + //} + //else { + // //If HTTP connection was not successful, display error on screen + // display.println("HTTP connection error!"); + // display.partialUpdate(); + // return; + //} +} + +void loop() { + //Nothing... +}