I'm trying to create a virtual display, in which data is sent to a webserver as a 8bit gray scale .bmp (printable) image, rather than the display itself
Has anyone attempted such an implementation before?
I'm trying to create a virtual display, in which data is sent to a webserver as a 8bit gray scale .bmp (printable) image, rather than the display itself
Has anyone attempted such an implementation before?
Not that i know, but i'd use AdafruitGFX and go for 16-bit RGB for a format, or use modified parts of that library for 8-bit GS. Shouldn't be to hard to make it write to a memory location and then send the whole thing.
Thanks.
Yes, I'm planning on modifying one of their libraries, that way is super easy to just swap code, if using a real display, in particular the ILI9341. With a bit of luck I could fit it all in the RAM of an ESP8266. Wondering how much is free after the stack and the arduino stuff?
Depends a little on which ESP you are using.
Deva_Rishi:
Depends a little on which ESP you are using.
I striped down the ILI9341 library to x/y and color coordinates. Text is printed in a pixel by pixel basis. I am still to check with lines and circles, as the library uses some built in features on the display controller itself.
Serial print of my 16*77 pixels display memory using a UNO.
There are several ESP8266's available the smallest being the ESP-01 in which you would have about 50k's left of RAM, you could attempt to write your file to SPIFFS though that would complicate your program quite a lot. An ESP-07 already has quite a bit more memory and an ESP-12 as is on the nodeMCU boards even more
I striped down the ILI9341 library to x/y and color coordinates. Text is printed in a pixel by pixel basis. I am still to check with lines and circles, as the library uses some built in features on the display controller itself.
You should be using the Adafruit_GFX library, (IL9341 library does as well)
From Adafruit_GFX.h
class Adafruit_GFX : public Print {
public:
Adafruit_GFX(int16_t w, int16_t h); // Constructor
// This MUST be defined by the subclass:
virtual void drawPixel(int16_t x, int16_t y, uint16_t color) = 0;
// These MAY be overridden by the subclass to provide device-specific
// optimized code. Otherwise 'generic' versions are used.
virtual void
drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color),
drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color),
drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color),
drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color),
fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color),
fillScreen(uint16_t color),
invertDisplay(boolean i);
// These exist only with Adafruit_GFX (no subclass overrides)
void
drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color),
drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername,
uint16_t color),
fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color),
fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername,
int16_t delta, uint16_t color),
drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
int16_t x2, int16_t y2, uint16_t color),
fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
int16_t x2, int16_t y2, uint16_t color),
drawRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h,
int16_t radius, uint16_t color),
fillRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h,
int16_t radius, uint16_t color),
drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap,
int16_t w, int16_t h, uint16_t color),
drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap,
int16_t w, int16_t h, uint16_t color, uint16_t bg),
drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap,
int16_t w, int16_t h, uint16_t color),
drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color,
uint16_t bg, uint8_t size),
setCursor(int16_t x, int16_t y),
setTextColor(uint16_t c),
setTextColor(uint16_t c, uint16_t bg),
setTextSize(uint8_t s),
setTextWrap(boolean w),
setRotation(uint8_t r);
All you need to do is
create a 'void drawPixel(int16_t x, int16_t y, uint16_t color) = 0;'
i would event just keep the 16-bit color as argument and convert to 8 bit grayscale within that (5bit-R)*2+(6bit-G)+(5bit-B)*2 should nearly to the trick, maybe check for maximum range. Or go for a 16-bit color image.
In fact you could just enter the foreground (and background color) as 8-bit grayscale and it will come out the same the other end. Adafruit_GFX merely plots it all in the right spot but does nothing to the color value itself (and that it si stored as a 16-bit value within the function is not really relevant and doesn't waste more than a few bytes as long as your drawPixel() stores an 8-bit value again.
I'm using the Adafruit GFX, but through calls - my modified ILI9341 essentially is an interface that converts standard display commands into something useful for my output and adds an abstraction layer to the user.
So I think I will always need that, there is a lot of under the hood code? Or can I be missing something?
In the code I use things such as:
tft.print("...");
tft.fillRect(...);
tft.drawLine(...);
I also set the screen resolutions, output type (grayscale, 256 colors, 1 bit). etc...
This is the core part of the code. It prints pixels and deals with compression (1 bit, grayscale, 256 colors...))
Then I have other functions that use that to print text, squares, etc.
volatile uint8_t biDimArray2 [ILI9341_TFTWIDTH/8] [ILI9341_TFTHEIGHT] = {0}; // Compressed
//volatile uint8_t biDimArray2 [ILI9341_TFTWIDTH] [ILI9341_TFTHEIGHT] = {0}; // Uncompressed
void Adafruit_ILI9341::refresh(int8_t c) {
//Serial.println(c);
for(int16_t y=0; y<ILI9341_TFTHEIGHT; y++)
{
// This routine prints the memory contents
for(int16_t x=0; x<(ILI9341_TFTWIDTH/8); x++)
{
int8_t ljy = (biDimArray2 [x][y]); //Commented above, outside the loop
// Expand to cover 8 pixels
for(int8_t j=0; j<8; j++) // This is only needed if using compresion
{
// This one prints every bit on the byte using only 1/8 of the memory for B/W
// for (int8_t i=7; i>=0; i--)
{
if(bitRead(ljy, j)) // with compression
//if (ljy) // without compression
{
Serial.print("*");
}
else
{
Serial.print(" ");
}
}
}
}
Serial.println();
}
}
An image stored in flash memory can be read and sent with very little RAM used. Uno has 32K flash for program (a few K), labels and tables.
8-bit grayscale pixels, 16x77 needs 1232 bytes. Uno could store 20 such images and still have room for a lot of code.
Very big images can be stored on SD, an Uno can stream data from file to print way faster than most bauds print.