From 406aac051e222df46d6a506125ac9656675ad282 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 10 Mar 2017 19:16:31 +0000 Subject: [PATCH] Update to screenshot sketches + others Minor tweaks and re-arrangement of User and Custom font references between header files --- TFT_eSPI.h | 32 +- Tools/Screenshot_client/ILI9341_example.png | Bin 0 -> 5170 bytes Tools/Screenshot_client/Screenshot_client.pde | 441 +++++++++------- Tools/Screenshot_client/example_1.png | Bin 13401 -> 0 bytes Tools/Screenshot_client/example_2.png | Bin 9997 -> 0 bytes User_Setup_Select.h | 22 + .../TFT_Screen_Capture/TFT_Screen_Capture.ino | 117 +++-- .../TFT_Screen_Capture/processing_sketch.ino | 470 +++++++++++------- .../TFT_Screen_Capture/screenServer.ino | 132 +++-- 9 files changed, 728 insertions(+), 486 deletions(-) create mode 100644 Tools/Screenshot_client/ILI9341_example.png delete mode 100644 Tools/Screenshot_client/example_1.png delete mode 100644 Tools/Screenshot_client/example_2.png diff --git a/TFT_eSPI.h b/TFT_eSPI.h index d24e292..3e23ea7 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -19,20 +19,10 @@ #ifndef _TFT_eSPIH_ #define _TFT_eSPIH_ -// Include header file that defines the fonts loaded and the pins to be used +// Include header file that defines the fonts loaded, the TFT drivers +// available and the pins to be used #include -// Load the right driver definitions <<<<<<<<<<<<<<<<<<<<< ADD NEW DRIVERS TO THE LIST HERE <<<<<<<<<<<<<<<<<<<<<<< -#if defined (ILI9341_DRIVER) - #include -#elif defined (ST7735_DRIVER) - #include -#elif defined (ILI9163_DRIVER) - #include -#elif defined (S6D02A1_DRIVER) - #include -#endif - // If the frequency is not defined, set a default #ifndef SPI_FREQUENCY #define SPI_FREQUENCY 20000000 @@ -107,14 +97,10 @@ #include -// New custom fonts -#include // CF_OL24 -#include // CF_OL32 -#include // CF_RT24 -#include // CF_S24 -#include // CF_Y32 +// Call up any user custom fonts +#include -// Free fonts +// Original Adafruit_GFX "Free Fonts" #include // TT1 #include // FF1 or FM9 @@ -179,9 +165,6 @@ #include // FF47 or FSBI18 #include // FF48 or FSBI24 -// Swap any type -template static inline void -swap(T& a, T& b) { T t = a; a = b; b = t; } //These enumerate the text plotting alignment (reference datum point) #define TL_DATUM 0 // Top left (default) @@ -222,6 +205,11 @@ swap(T& a, T& b) { T t = a; a = b; b = t; } #define TFT_GREENYELLOW 0xAFE5 /* 173, 255, 47 */ #define TFT_PINK 0xF81F + +// Swap any type +template static inline void +swap(T& a, T& b) { T t = a; a = b; b = t; } + // This is a structure to conveniently hold infomation on the default fonts // Stores pointer to font character image address table, width table and height diff --git a/Tools/Screenshot_client/ILI9341_example.png b/Tools/Screenshot_client/ILI9341_example.png new file mode 100644 index 0000000000000000000000000000000000000000..25369e68396663915b3a300d61956023b1fcc4f3 GIT binary patch literal 5170 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 literal 0 HcmV?d00001 diff --git a/Tools/Screenshot_client/Screenshot_client.pde b/Tools/Screenshot_client/Screenshot_client.pde index 5ff0acd..f39e00f 100644 --- a/Tools/Screenshot_client/Screenshot_client.pde +++ b/Tools/Screenshot_client/Screenshot_client.pde @@ -1,76 +1,85 @@ -// Latest at 03/03/17 - // This is a Processing sketch, see https://processing.org/ to download the IDE // The sketch is a client that requests TFT screenshots from an Arduino board. -// The arduino must call a screenshot server function to respond with pixels. +// The Arduino must call a screenshot server function to respond with pixels. // It has been created to work with the TFT_eSPI library here: // https://github.com/Bodmer/TFT_eSPI -// The library provides a member function that reads the RGB values of screen pixels -// and an example TFT_Screen_Capture +// The sketch must only be run when the designated serial port is available and enumerated +// otherwise the screenshot window may freeze and that process will need to be terminated +// This is a limitation of the Processing environment and not the sketch. +// If anyone knows how to determine if a serial port is available at start up the PM me +// on (Bodmer) the Arduino forum. -// Captured images are stored in the sketch folder, use "Sketch" menu option -// "Show Sketch Folder" or press Ctrl+K +// The block below contains variables that the user may need to change for a particular setup +// As a minimum set the serial port and baud rate must be defined. The capture window is +// automatically resized for landscape, portrait and different TFT resolutions. -// Created by: Bodmer 27/1/17 +// Captured images are stored in the sketch folder, use the Processing IDE "Sketch" menu +// option "Show Sketch Folder" or press Ctrl+K + +// Created by: Bodmer 5/3/17 +// Updated by: Bodmer 10/3/17 +// Version: 0.06 // MIT licence applies, all text above must be included in derivative works -import processing.serial.*; - -Serial serial; // Create an instance called serial // ########################################################################################### // # These are the values to change for a particular setup # // # -int serial_port = 1; // Use enumerated value from list provided when sketch is run # -int serial_baud_rate = 921600; // Maximum tested is 921600 # +int serial_port = 0; // Use enumerated value from list provided when sketch is run # // # -int tft_width = 240; // TFT width in portrait orientation # -int tft_height = 320; // TFT height # -//int tft_width = 320; // TFT width in landscape orientation # -//int tft_height = 240; // TFT height # +// On an Arduino Due Programming Port use a baud rate of:115200) # +// On an Arduino Due Native USB Port use a baud rate of any value # +int serial_baud_rate = 921600; // # // # // Change the image file type saved here, comment out all but one # //String image_type = ".jpg"; // # -String image_type = ".png"; // # +String image_type = ".png"; // Lossless compression # //String image_type = ".bmp"; // # //String image_type = ".tif"; // # // # boolean save_border = true; // Save the image with a border # int border = 5; // Border pixel width # -boolean fade = true ; // Fade out image after saving # +boolean fade = false; // Fade out image after saving # // # int max_images = 10; // Maximum of numbered saved images before over-writing files # // # // # End of the values to change for a particular setup # // ########################################################################################### -int serialCount = 0; // Count of colour bytes arriving +// These are default values, this sketch obtains the actual values from the Arduino board +int tft_width = 480; // default TFT width (automatic - sent by Arduino) +int tft_height = 480; // default TFT height (automatic - sent by Arduino) +int color_bytes = 2; // 2 for 16 bit, 3 for three RGB bytes (automatic - sent by Arduino) -int bgcolor = 255; // Background color +import processing.serial.*; -PImage img, tft_img; +Serial serial; // Create an instance called serial -color light_blue = color(50, 128, 255); +int serialCount = 0; // Count of colour bytes arriving -int[] rgb = new int[6]; // Buffer for the RGB colour bytes +// Stage window graded background colours +color bgcolor1 = color(0, 100, 104); // Arduino IDE style background color 1 +color bgcolor2 = color(77, 183, 187); // Arduino IDE style background color 2 +//color bgcolor2 = color(255, 255, 255); // White +// TFT image frame greyscale value (dark grey) +color frameColor = 42; + +int[] rgb = new int[3]; // Buffer for the colour bytes int indexRed = 0; // Colour byte index in the array int indexGreen = 1; int indexBlue = 2; -long end = 10; - int n = 0; -boolean got_image = false; - int x_offset = (500 - tft_width) /2; // Image offsets in the window -int y_offset = 20; // -int xpos, ypos; // Pixel position +int y_offset = 20; + +int xpos = 0, ypos = 0; // Current pixel position int beginTime = 0; int pixelWaitTime = 1000; // Maximum 1000ms wait for image pixels to arrive @@ -78,32 +87,25 @@ int lastPixelTime = 0; // Time that "image send" command was sent int state = 0; // State machine current state -int progress_bar = 0; -int pixel_count = 0; -float percentage = 0; +int progress_bar = 0; // Console progress bar dot count +int pixel_count = 0; // Number of pixels read for 1 screen +float percentage = 0; // Percentage of pixels received -int drawLoopCount = 0; +int saved_image_count = 0; // Stats - number of images processed +int bad_image_count = 0; // Stats - number of images that had lost pixels + +int drawLoopCount = 0; // Used for the fade out void setup() { - size(500, 540); // Stage size, could handle 480 pixel scrren + size(500, 540); // Stage size, can handle 480 pixels wide screen noStroke(); // No border on the next thing drawn + noSmooth(); // No anti-aliasing to avoid adjacent pixel colour merging - img = createImage(500, 540, ARGB); - for (int i = 0; i < img.pixels.length; i++) { - float a = map(i, 0, img.pixels.length, 255, 0); - img.pixels[i] = color(0, 153, 204, a); - } + // Graded background and title + drawWindow(); - tft_img = createImage(tft_width, tft_height, ARGB); - for (int i = 0; i < tft_img.pixels.length; i++) { - tft_img.pixels[i] = color(0, 0, 0, 255); - } - - frameRate(5000); // High frame rate so draw() loops fast - - xpos = 0; - ypos = 0; + frameRate(2000); // High frame rate so draw() loops fast // Print a list of the available serial ports println("-----------------------"); @@ -118,27 +120,20 @@ void setup() { String portName = Serial.list()[serial_port]; - delay(1000); - serial = new Serial(this, portName, serial_baud_rate); state = 99; } void draw() { - drawLoopCount++; switch(state) { case 0: // Init varaibles, send start request tint(0, 0, 0, 255); + println(); + flushBuffer(); println(""); - //println("Clearing pipe..."); - beginTime = millis() + 200; - while ( millis() < beginTime ) - { - serial.read(); - } - println("Ready to receive image"); + print("Ready: "); xpos = 0; ypos = 0; @@ -146,139 +141,72 @@ void draw() { progress_bar = 0; pixel_count = 0; percentage = 0; - drawLoopCount = 0; + drawLoopCount = frameCount; lastPixelTime = millis() + 1000; + state = 1; break; case 1: // Console message, give server some time - println("Requesting image"); + print("requesting image "); serial.write("S"); - delay(10); + delay(10); + beginTime = millis() + 1000; state = 2; break; - case 2: // Get size and set start time for render time report - // To do: Read image size info, currently hard coded - beginTime = millis(); - noTint(); - state = 3; + case 2: // Get size and set start time for rendering duration report + if (millis() > beginTime) { + System.err.println(" - no response!"); + state = 0; + } + if ( getSize() == true ) { // Go to next state when we have the size and bits per pixel + flushBuffer(); // Precaution in case image header size increases in later versions + beginTime = millis(); + state = 3; + } break; case 3: // Request pixels and render returned RGB values + state = renderPixels(); // State will change when all pixels are rendered + + // Request more pixels, changing the number requested allows the average transfer rate to be controlled + // The pixel transfer rate is dependant on four things: + // 1. The frame rate defined in this Processing sketch in setup() + // 2. The baud rate of the serial link (~10 bit periods per byte) + // 3. The number of request bytes 'R' sent in the lines below + // 4. The number of pixels sent in a burst by the server sketch (defined via NPIXELS) - if ( serial.available() > 0 ) { - - // Add the latest byte from the serial port to array: - while (serial.available()>0) - { - rgb[serialCount++] = serial.read(); - - // If we have 3 colour bytes: - if (serialCount >= 3 ) { - serialCount = 0; - pixel_count++; - stroke(rgb[indexRed], rgb[indexGreen], rgb[indexBlue],1000); - // We seem to get some pixel transparency so draw twice - point(xpos + x_offset, ypos + y_offset); - point(xpos + x_offset, ypos + y_offset); - lastPixelTime = millis(); - xpos++; - if (xpos >= tft_width) { - xpos = 0; - print("."); - progress_bar++; - if (progress_bar >31) - { - progress_bar = 0; - percentage = 0.5 + 100 * pixel_count/(0.001 + tft_width * tft_height); - if (percentage > 100) percentage = 100; - println(" [ " + (int)percentage + "% ]"); - } - ypos++; - if (ypos>=tft_height) { - ypos = 0; - println("Image fetch time = " + (millis()-beginTime)/1000.0 + " s"); - state = 5; - } - } - } - } - } else - { - if (millis() > (lastPixelTime + pixelWaitTime)) - { - println(""); - System.err.println("No response, trying again..."); - state = 4; - } - } - // Request 32pixels - serial.write("RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"); + //serial.write("RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"); // 32 x NPIXELS more + serial.write("RRRRRRRRRRRRRRRR"); // 16 x NPIXELS more + //serial.write("RRRRRRRR"); // 8 x NPIXELS more + //serial.write("RRRR"); // 4 x NPIXELS more + //serial.write("RR"); // 2 x NPIXELS more + //serial.write("R"); // 1 x NPIXELS more break; - case 4: // Time-out, flush serial buffer + case 4: // Pixel receive time-out, flush serial buffer println(); - //println("Clearing serial pipe after a time-out"); - int clearTime = millis() + 50; - while ( millis() < clearTime ) - { - serial.read(); - } + flushBuffer(); state = 6; break; - case 5: // Save the image tot he sketch folder - println(); - String filename = "tft_screen_" + n + image_type; - println("Saving image as \"" + filename); - if (save_border) - { - PImage partialSave = get(x_offset - border, y_offset - border, tft_width + 2*border, tft_height + 2*border); - partialSave.save(filename); - } else { - PImage partialSave = get(x_offset, y_offset, tft_width, tft_height); - partialSave.save(filename); - } - - n = n + 1; - if (n>=max_images) n = 0; - drawLoopCount = 0; // Reset value ready for counting in step 6 + case 5: // Save the image to the sketch folder (Ctrl+K to access) + saveScreenshot(); + saved_image_count++; + println("Saved image count = " + saved_image_count); + if (bad_image_count > 0) System.err.println(" Bad image count = " + bad_image_count); + drawLoopCount = frameCount; // Reset value ready for counting in step 6 state = 6; break; case 6: // Fade the old image if enabled - int opacity = drawLoopCount; // So we get increasing fade - if (drawLoopCount > 50) // End fade after 50 cycles - { - opacity = 255; - state = 0; - } - delay(10); - if (fade) - { - tint(255, opacity); - image(tft_img, x_offset, y_offset); - } - + if ( fadedImage() == true ) state = 0; // Go to next state when image has faded break; case 99: // Draw image viewer window - textAlign(CENTER); - textSize(20); - background(bgcolor); - image(img, 0, 0); - - fill(0); - text("Bodmer's TFT image viewer", width/2, height-10); - - stroke(0, 0, 0); - - rect(x_offset - border, y_offset - border, tft_width - 1 + 2*border, tft_height - 1 + 2*border); - - fill(255); - rect(x_offset, y_offset, tft_width-1, tft_height-1); - + drawWindow(); + delay(50); // Delay here seems to be required for the IDE console to get ready state = 0; break; @@ -287,4 +215,185 @@ void draw() { System.err.println("Error state reached - check sketch!"); break; } +} + +void drawWindow() +{ + // Graded background in Arduino colours + for (int i = 0; i < height - 25; i++) { + float inter = map(i, 0, height - 25, 0, 1); + color c = lerpColor(bgcolor1, bgcolor2, inter); + stroke(c); + line(0, i, 500, i); + } + fill(bgcolor2); + rect( 0, height-25, width-1, 24); + textAlign(CENTER); + textSize(20); + fill(0); + text("Bodmer's TFT image viewer", width/2, height-6); +} + +void flushBuffer() +{ + //println("Clearing serial pipe after a time-out"); + int clearTime = millis() + 50; + while ( millis() < clearTime ) serial.read(); +} + +boolean getSize() +{ + if ( serial.available() > 6 ) { + println(); + char code = (char)serial.read(); + if (code == 'W') { + tft_width = serial.read()<<8 | serial.read(); + } + code = (char)serial.read(); + if (code == 'H') { + tft_height = serial.read()<<8 | serial.read(); + } + code = (char)serial.read(); + if (code == 'Y') { + int bits_per_pixel = (char)serial.read(); + if (bits_per_pixel == 24) color_bytes = 3; + else color_bytes = 2; + } + code = (char)serial.read(); + if (code == '?') { + drawWindow(); + + x_offset = (500 - tft_width) /2; + tint(0, 0, 0, 255); + noStroke(); + fill(frameColor); + rect((width - tft_width)/2 - border, y_offset - border, tft_width + 2 * border, tft_height + 2 * border); + return true; + } + } + return false; +} + +int renderPixels() +{ + if ( serial.available() > 0 ) { + + // Add the latest byte from the serial port to array: + while (serial.available()>0) + { + rgb[serialCount++] = serial.read(); + + // If we have 3 colour bytes: + if ( serialCount >= color_bytes ) { + serialCount = 0; + pixel_count++; + if (color_bytes == 3) + { + stroke(rgb[indexRed], rgb[indexGreen], rgb[indexBlue], 1000); + } else + { + stroke( (rgb[1] & 0x1F)<<3, (rgb[1] & 0xE0)>>3 | (rgb[0] & 0x07)<<5, (rgb[0] & 0xF8)); + //stroke( (rgb[1] & 0xF8), (rgb[0] & 0xE0)>>3 | (rgb[1] & 0x07)<<5, (rgb[0] & 0x1F)<<3); + } + // We get some pixel merge aliasing if smooth() is defined, so draw pixel twice + point(xpos + x_offset, ypos + y_offset); + //point(xpos + x_offset, ypos + y_offset); + + lastPixelTime = millis(); + xpos++; + if (xpos >= tft_width) { + xpos = 0; + progressBar(); + ypos++; + if (ypos>=tft_height) { + ypos = 0; + if ((int)percentage <100) { + while(progress_bar++ < 64) print(" "); + percent(100); + } + println("Image fetch time = " + (millis()-beginTime)/1000.0 + " s"); + return 5; + } + } + } + } + } else + { + if (millis() > (lastPixelTime + pixelWaitTime)) + { + println(""); + System.err.println(pixelWaitTime + "ms time-out for pixels exceeded..."); + if (pixel_count > 0) { + bad_image_count++; + System.err.print("Pixels missing = " + (tft_width * tft_height - pixel_count)); + System.err.println(", corrupted image not saved"); + System.err.println("Good image count = " + saved_image_count); + System.err.println(" Bad image count = " + bad_image_count); + } + return 4; + } + } + return 3; +} + +void progressBar() +{ + progress_bar++; + print("."); + if (progress_bar >63) + { + progress_bar = 0; + percentage = 0.5 + 100 * pixel_count/(0.001 + tft_width * tft_height); + percent(percentage); + } +} + +void percent(float percentage) +{ + if (percentage > 100) percentage = 100; + println(" [ " + (int)percentage + "% ]"); + textAlign(LEFT); + textSize(16); + noStroke(); + fill(bgcolor2); + rect(10, height - 25, 70, 20); + fill(0); + text(" [ " + (int)percentage + "% ]", 10, height-8); +} + +void saveScreenshot() +{ + println(); + String filename = "tft_screen_" + n + image_type; + println("Saving image as \"" + filename); + if (save_border) + { + PImage partialSave = get(x_offset - border, y_offset - border, tft_width + 2*border, tft_height + 2*border); + partialSave.save(filename); + } else { + PImage partialSave = get(x_offset, y_offset, tft_width, tft_height); + partialSave.save(filename); + } + + n = n + 1; + if (n>=max_images) n = 0; +} + +boolean fadedImage() +{ + int opacity = frameCount - drawLoopCount; // So we get increasing fade + if (fade) + { + tint(255, opacity); + //image(tft_img, x_offset, y_offset); + noStroke(); + fill(50, 50, 50, opacity); + rect( (width - tft_width)/2, y_offset, tft_width, tft_height); + delay(10); + } + if (opacity > 50) // End fade after 50 cycles + { + return true; + } + return false; } \ No newline at end of file diff --git a/Tools/Screenshot_client/example_1.png b/Tools/Screenshot_client/example_1.png deleted file mode 100644 index 3a29e3c16c7cd98b6e8185c2da1b041e6c763bda..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13401 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 diff --git a/Tools/Screenshot_client/example_2.png b/Tools/Screenshot_client/example_2.png deleted file mode 100644 index 42ae983d58ecd098599501d1fec4431a34b87732..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9997 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 DBL3Qy diff --git a/User_Setup_Select.h b/User_Setup_Select.h index 7161f24..b0b2d7c 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -24,3 +24,25 @@ //#include // Setup file configured for my S6D02A1 //#include // Setup file template for copying/editting + + + + + +///////////////////////////////////////////////////////////////////////////////////// +// // +// DON'T TINKER WITH ANY OF THE FOLLOWING LINES, THESE ADD THE TFT DRIVERS // +// THEY ARE HERE FOR BODMER'S CONVENIENCE! // +// // +///////////////////////////////////////////////////////////////////////////////////// + +// Load the right driver definition - do not tinker here ! +#if defined (ILI9341_DRIVER) + #include +#elif defined (ST7735_DRIVER) + #include +#elif defined (ILI9163_DRIVER) + #include +#elif defined (S6D02A1_DRIVER) + #include +#endif diff --git a/examples/ILI9341/TFT_Screen_Capture/TFT_Screen_Capture.ino b/examples/ILI9341/TFT_Screen_Capture/TFT_Screen_Capture.ino index ceed295..d791b0f 100644 --- a/examples/ILI9341/TFT_Screen_Capture/TFT_Screen_Capture.ino +++ b/examples/ILI9341/TFT_Screen_Capture/TFT_Screen_Capture.ino @@ -1,24 +1,41 @@ /* - This example draws rainbow colours on the screen, adds text in various - fast rendering fonts and then sends the TFT screen to a PC over the serial - port to a processing sketch. + This sketch has been written to test the Processing screenshot client. - This sketch uses the GLCD, 2, 4, 6 fonts. + It has been created to work with the TFT_eSPI library here: + https://github.com/Bodmer/TFT_eSPI + + It sends screenshots to a PC running a Processing client sketch. + + The Processing IDE that will run the client sketch can be downloaded + here: https://processing.org/ + + The Processing sketch needed is contained within a tab attached to this + Arduino sketch. Cut and copy that tab into the Processing IDE and run. + + This sketch uses the GLCD, 2, 4, 6 fonts only. Make sure all the display driver and pin comnenctions are correct by editting the User_Setup.h file in the TFT_eSPI library folder. + Maximum recommended SPI clock rate is 27MHz when reading pixels, 40MHz + seems to be OK with ILI9341 displays but this is above the manufacturers + specifed maximum clock rate. + ######################################################################### ###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ###### ######################################################################### */ +// Created by: Bodmer 5/3/17 +// Updated by: Bodmer 10/3/17 +// Version: 0.06 + +// MIT licence applies, all text above must be included in derivative works + #include // Hardware-specific library #include -TFT_eSPI tft = TFT_eSPI(); // Invoke custom library with default width and height - -//TFT_eSPI tft = TFT_eSPI(240, 320); // Could invoke custom library declaring width and height +TFT_eSPI tft = TFT_eSPI(); // Invoke custom library with default width and height unsigned long targetTime = 0; byte red = 31; @@ -34,72 +51,76 @@ void setup(void) { tft.setRotation(0); tft.fillScreen(TFT_BLACK); + randomSeed(analogRead(A0)); + targetTime = millis() + 1000; } void loop() { if (targetTime < millis()) { - targetTime = millis() + 4000; - + targetTime = millis() + 1500; // Wait a minimum of 1.5s + + tft.setRotation(random(4)); rainbow_fill(); // Fill the screen with rainbow colours - // The standard AdaFruit font still works as before - tft.setTextColor(TFT_BLACK); // Background is not defined so it is transparent - - tft.setCursor (60, 5); + tft.setTextColor(TFT_BLACK); // Text background is not defined so it is transparent + tft.setTextDatum(TC_DATUM); // Top Centre datum + int xpos = tft.width()/2; // Centre of screen + tft.setTextFont(0); // Select font 0 which is the Adafruit font - tft.print("Original Adafruit font!"); - - //tft.drawString("Original Adafruit font!",60,5,1); + tft.drawString("Original Adafruit font!", xpos, 5); // The new larger fonts do not need to use the .setCursor call, coords are embedded tft.setTextColor(TFT_BLACK); // Do not plot the background colour + // Overlay the black text on top of the rainbow plot (the advantage of not drawing the backgorund colour!) - tft.drawCentreString("Font size 2", 120, 14, 2); // Draw text centre at position 120, 14 using font 2 - tft.drawCentreString("Font size 4", 120, 30, 4); // Draw text centre at position 120, 30 using font 4 - tft.drawCentreString("12.34", 120, 54, 6); // Draw text centre at position 120, 54 using font 6 + tft.drawString("Font size 2", xpos, 14, 2); // Draw text centre at position xpos, 14 using font 2 + tft.drawString("Font size 4", xpos, 30, 4); // Draw text centre at position xpos, 30 using font 4 + tft.drawString("12.34", xpos, 54, 6); // Draw text centre at position xpos, 54 using font 6 - tft.drawCentreString("12.34 is in font size 6", 120, 92, 2); // Draw text centre at position 120, 92 using font 2 + tft.drawString("12.34 is in font size 6", xpos, 92, 2); // Draw text centre at position xpos, 92 using font 2 // Note the x position is the top of the font! // draw a floating point number - float pi = 3.14159; // Value to print - int precision = 3; // Number of digits after decimal point - int xpos = 90; // x position + float pi = 3.1415926; // Value to print + int precision = 3; // Number of digits after decimal point + int ypos = 110; // y position - int font = 2; // font number 2 - xpos += tft.drawFloat(pi, precision, xpos, ypos, font); // Draw rounded number and return new xpos delta for next print position - tft.drawString(" is pi", xpos, ypos, font); // Continue printing from new x position - tft.setTextSize(1); // We are using a size multiplier of 1 + tft.setTextDatum(TR_DATUM); // Top Right datum so text butts neatly to xpos (right justified) + tft.drawFloat(pi, precision, xpos, ypos, 2); // Draw rounded number and return new xpos delta for next print position + + tft.setTextDatum(TL_DATUM); // Top Left datum so text butts neatly to xpos (left justified) + + tft.drawString(" is pi", xpos, ypos, 2); + + tft.setTextSize(1); // We are using a font size multiplier of 1 + tft.setTextDatum(TC_DATUM); // Top Centre datum tft.setTextColor(TFT_BLACK); // Set text colour to black, no background (so transparent) - tft.setCursor(36, 150, 4); // Set cursor to x = 36, y = 150 and use font 4 - tft.println("Transparent..."); // As we use println, the cursor moves to the next line + tft.drawString("Transparent...", xpos, 125, 4); // Font 4 - tft.setCursor(30, 175); // Set cursor to x = 30, y = 175 - tft.setTextColor(TFT_WHITE, TFT_BLACK); // Set text colour to white and background to black - tft.println(" White on black "); - - tft.setTextFont(4); // Select font 4 without moving cursor - tft.setCursor(50, 210); // Set cursor to x = 50, y = 210 without changing the font - tft.setTextColor(TFT_WHITE); - // By using #TFT print we can use all the formatting features like printing HEX - tft.print(57005, HEX); // Cursor does no move to next line - tft.println(48879, HEX); // print and move cursor to next line + tft.setTextColor(TFT_WHITE, TFT_BLACK); // Set text colour to white and background to black + tft.drawString("White on black", xpos, 150, 4); // Font 4 tft.setTextColor(TFT_GREEN, TFT_BLACK); // This time we will use green text on a black background - tft.setTextFont(2); // Select font 2 + + tft.setTextFont(2); // Select font 2, now we do not need to specify the font in drawString() // An easier way to position text and blank old text is to set the datum and use width padding - tft.setTextDatum(BC_DATUM); // Bottom centre for text datum - tft.setTextPadding(241); // Pad text to full screen wdth (240 pixels + 1 spare for +/-1 position rounding) - tft.drawString("Ode to a Small Lump of Green Putty", 120, 300 - 32); - tft.drawString("I Found in My Armpit One Midsummer", 120, 300 - 16); - tft.drawString("Morning", 120, 300); + tft.setTextDatum(BC_DATUM); // Bottom centre for text datum + tft.setTextPadding(tft.width() + 1); // Pad text to full screen width + 1 spare for +/-1 position rounding + + tft.drawString("Ode to a Small Lump of Green Putty", xpos, 230 - 32); + tft.drawString("I Found in My Armpit One Midsummer", xpos, 230 - 16); + tft.drawString("Morning", xpos, 230); + tft.setTextDatum(TL_DATUM); // Reset to top left for text datum + tft.setTextPadding(0); // Reset text padding to 0 pixels + + // Now call the screen server to send a copy of the TFT screen to the PC running the Processing client sketch screenServer(); } } @@ -108,8 +129,9 @@ void loop() { void rainbow_fill() { // The colours and state are not initialised so the start colour changes each time the funtion is called - - for (int i = 319; i >= 0; i--) { + int rotation = tft.getRotation(); + tft.setRotation(random(4)); + for (int i = tft.height() - 1; i >= 0; i--) { // This is a "state machine" that ramps up/down the colour brightnesses in sequence switch (state) { case 0: @@ -159,6 +181,7 @@ void rainbow_fill() // Draw a line 1 pixel wide in the selected colour tft.drawFastHLine(0, i, tft.width(), colour); // in this example tft.width() returns the pixel width of the display } + tft.setRotation(rotation); } diff --git a/examples/ILI9341/TFT_Screen_Capture/processing_sketch.ino b/examples/ILI9341/TFT_Screen_Capture/processing_sketch.ino index 4fafcbd..f904c12 100644 --- a/examples/ILI9341/TFT_Screen_Capture/processing_sketch.ino +++ b/examples/ILI9341/TFT_Screen_Capture/processing_sketch.ino @@ -1,56 +1,51 @@ -// Below is a copy of the processing sketch that can be used to capture the images -// The sketch beow is NOT and Arduino IDE sketch! +// This is a copy of the processing sketch that can be used to capture the images +// Copy the sketch below and remove the /* and */ at the beginning and end. -// Copy the sketch content below and remove the /* and */ at the beginning and end. -// The sketch runs in Processing version 3.3, it can be downloaded here: +// The sketch runs in Processing version 3.3 on a PC, it can be downloaded here: // https://processing.org/download/ -// When the sketch is loaded in Processing, save it as "Screenshot_Client", and click -// the run triangle. Then check the serial port list in the console report, edit the -// Processing sketch to use the right port by changing the port number allocated in -// "int serial_port = X;" at line 26 (see line 43 below) - -// The Arduino IDE and Processing will share a serial port, make sure only one -// program tries to use the port at any time. Processing may "freeze" otherwise. - -/* <<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS LINE <<<<<<<<<<<<<<<<<<<<<<<<< +/* // This is a Processing sketch, see https://processing.org/ to download the IDE // The sketch is a client that requests TFT screenshots from an Arduino board. -// The arduino must call a screenshot server function to respond with pixels. +// The Arduino must call a screenshot server function to respond with pixels. // It has been created to work with the TFT_eSPI library here: // https://github.com/Bodmer/TFT_eSPI -// The library provides a member function that reads the RGB values of screen pixels -// and an example TFT_Screen_Capture +// The sketch must only be run when the designated serial port is available and enumerated +// otherwise the screenshot window may freeze and that process will need to be terminated +// This is a limitation of the Processing environment and not the sketch. +// If anyone knows how to determine if a serial port is available at start up the PM me +// on (Bodmer) the Arduino forum. -// Captured images are stored in the Processing sketch folder, use "Sketch" menu option -// "Show Sketch Folder" or press Ctrl+K in the Processing IDE. +// The block below contains variables that the user may need to change for a particular setup +// As a minimum set the serial port and baud rate must be defined. The capture window is +// automatically resized for landscape, portrait and different TFT resolutions. -// Created by: Bodmer 27/1/17 +// Captured images are stored in the sketch folder, use the Processing IDE "Sketch" menu +// option "Show Sketch Folder" or press Ctrl+K + +// Created by: Bodmer 5/3/17 +// Updated by: Bodmer 10/3/17 +// Version: 0.06 // MIT licence applies, all text above must be included in derivative works -import processing.serial.*; - -Serial serial; // Create an instance called serial // ########################################################################################### // # These are the values to change for a particular setup # // # int serial_port = 0; // Use enumerated value from list provided when sketch is run # -int serial_baud_rate = 921600; // Maximum tested is 921600 # // # -int tft_width = 240; // TFT width in portrait orientation # -int tft_height = 320; // TFT height # -//int tft_width = 320; // TFT width in landscape orientation # -//int tft_height = 240; // TFT height # +// On an Arduino Due Programming Port use a baud rate of:115200) # +// On an Arduino Due Native USB Port use a baud rate of any value # +int serial_baud_rate = 921600; // # // # // Change the image file type saved here, comment out all but one # //String image_type = ".jpg"; // # -String image_type = ".png"; // # +String image_type = ".png"; // Lossless compression # //String image_type = ".bmp"; // # //String image_type = ".tif"; // # // # @@ -63,62 +58,62 @@ int max_images = 10; // Maximum of numbered saved images before over-writing fil // # End of the values to change for a particular setup # // ########################################################################################### -int serialCount = 0; // Count of colour bytes arriving +// These are default values, this sketch obtains the actual values from the Arduino board +int tft_width = 480; // default TFT width (automatic - sent by Arduino) +int tft_height = 480; // default TFT height (automatic - sent by Arduino) +int color_bytes = 2; // 2 for 16 bit, 3 for three RGB bytes (automatic - sent by Arduino) -int bgcolor = 255; // Background color +import processing.serial.*; -PImage img, tft_img; +Serial serial; // Create an instance called serial -color light_blue = color(50, 128, 255); +int serialCount = 0; // Count of colour bytes arriving -int[] rgb = new int[6]; // Buffer for the RGB colour bytes +// Stage window graded background colours +color bgcolor1 = color(0, 100, 104); // Arduino IDE style background color 1 +color bgcolor2 = color(77, 183, 187); // Arduino IDE style background color 2 +//color bgcolor2 = color(255, 255, 255); // White +// TFT image frame greyscale value (dark grey) +color frameColor = 42; + +int[] rgb = new int[3]; // Buffer for the colour bytes int indexRed = 0; // Colour byte index in the array int indexGreen = 1; int indexBlue = 2; -long end = 10; // Whether we've heard from the microcontroller - -int n = 0; // Whether we've heard from the microcontroller - -boolean got_image = false; +int n = 0; int x_offset = (500 - tft_width) /2; // Image offsets in the window -int y_offset = 20; // -int xpos, ypos; // Pixel position +int y_offset = 20; + +int xpos = 0, ypos = 0; // Current pixel position int beginTime = 0; -int pixelWaitTime = 100; // Wait a maximum of 100ms gap for image pixels to arrive +int pixelWaitTime = 1000; // Maximum 1000ms wait for image pixels to arrive int lastPixelTime = 0; // Time that "image send" command was sent int state = 0; // State machine current state -int progress_bar = 0; -int pixel_count = 0; -float percentage = 0; +int progress_bar = 0; // Console progress bar dot count +int pixel_count = 0; // Number of pixels read for 1 screen +float percentage = 0; // Percentage of pixels received -int drawLoopCount = 0; +int saved_image_count = 0; // Stats - number of images processed +int bad_image_count = 0; // Stats - number of images that had lost pixels + +int drawLoopCount = 0; // Used for the fade out void setup() { - size(500, 540); // Stage size, could handle 480 pixel scrren + size(500, 540); // Stage size, can handle 480 pixels wide screen noStroke(); // No border on the next thing drawn + noSmooth(); // No anti-aliasing to avoid adjacent pixel colour merging - img = createImage(500, 540, ARGB); - for (int i = 0; i < img.pixels.length; i++) { - float a = map(i, 0, img.pixels.length, 255, 0); - img.pixels[i] = color(0, 153, 204, a); - } + // Graded background and title + drawWindow(); - tft_img = createImage(tft_width, tft_height, ARGB); - for (int i = 0; i < tft_img.pixels.length; i++) { - tft_img.pixels[i] = color(0, 0, 0, 255); - } - - frameRate(5000); // High frame rate so draw() loops fast - - xpos = 0; - ypos = 0; + frameRate(2000); // High frame rate so draw() loops fast // Print a list of the available serial ports println("-----------------------"); @@ -133,164 +128,93 @@ void setup() { String portName = Serial.list()[serial_port]; - delay(1000); - serial = new Serial(this, portName, serial_baud_rate); state = 99; } void draw() { - drawLoopCount++; switch(state) { case 0: // Init varaibles, send start request - tint(255, 255); - textAlign(CENTER); - textSize(20); - + tint(0, 0, 0, 255); + println(); + flushBuffer(); println(""); - //println("Clearing pipe..."); - beginTime = millis() + 200; - while ( millis() < beginTime ) - { - serial.read(); - } - println("Ready to receive image"); - serial.write("S"); + print("Ready: "); + xpos = 0; ypos = 0; serialCount = 0; progress_bar = 0; pixel_count = 0; percentage = 0; - drawLoopCount = 0; + drawLoopCount = frameCount; lastPixelTime = millis() + 1000; + state = 1; break; case 1: // Console message, give server some time - println("Requesting image"); - delay(10); + print("requesting image "); + serial.write("S"); + delay(10); + beginTime = millis() + 1000; state = 2; break; - case 2: // Get size and set start time for render time report - // To do: Read image size info, currently hard coded - beginTime = millis(); - state = 3; - break; - - case 3: // Request pixels and reder them - if ( serial.available() > 0 ) { - - // Add the latest byte from the serial port to array: - while (serial.available()>0) - { - rgb[serialCount] = serial.read(); - serialCount++; - - // If we have 3 colour bytes: - if (serialCount >= 3 ) { - serialCount = 0; - pixel_count++; - stroke(rgb[indexRed], rgb[indexGreen], rgb[indexBlue]); - point(xpos + x_offset, ypos + y_offset); - lastPixelTime = millis(); - xpos++; - if (xpos >= tft_width) { - xpos = 0; - print("."); - progress_bar++; - if (progress_bar >31) - { - progress_bar = 0; - percentage = 0.5 + 100 * pixel_count/(0.001 + tft_width * tft_height); - if (percentage > 100) percentage = 100; - println(" [ " + (int)percentage + "% ]"); - } - ypos++; - if (ypos>=tft_height) { - ypos = 0; - println("Image fetch time = " + (millis()-beginTime)/1000.0 + " s"); - state = 5; - } - } - } - } - } else - { - - if (millis() > (lastPixelTime + pixelWaitTime)) - { - println(""); - System.err.println("No response, trying again..."); - state = 4; - } else - { - // Request 64 more pixels (ESP8266 buffer size) - serial.write("RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"); - serial.write("RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"); - } + case 2: // Get size and set start time for rendering duration report + if (millis() > beginTime) { + System.err.println(" - no response!"); + state = 0; + } + if ( getSize() == true ) { // Go to next state when we have the size and bits per pixel + flushBuffer(); // Precaution in case image header size increases in later versions + beginTime = millis(); + state = 3; } break; - case 4: // Time-out, flush serial buffer + case 3: // Request pixels and render returned RGB values + state = renderPixels(); // State will change when all pixels are rendered + + // Request more pixels, changing the number requested allows the average transfer rate to be controlled + // The pixel transfer rate is dependant on four things: + // 1. The frame rate defined in this Processing sketch in setup() + // 2. The baud rate of the serial link (~10 bit periods per byte) + // 3. The number of request bytes 'R' sent in the lines below + // 4. The number of pixels sent in a burst by the server sketch (defined via NPIXELS) + + //serial.write("RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"); // 32 x NPIXELS more + serial.write("RRRRRRRRRRRRRRRR"); // 16 x NPIXELS more + //serial.write("RRRRRRRR"); // 8 x NPIXELS more + //serial.write("RRRR"); // 4 x NPIXELS more + //serial.write("RR"); // 2 x NPIXELS more + //serial.write("R"); // 1 x NPIXELS more + break; + + case 4: // Pixel receive time-out, flush serial buffer println(); - //println("Clearing serial pipe after a time-out"); - int clearTime = millis() + 50; - while ( millis() < clearTime ) - { - serial.read(); - } - state = 0; + flushBuffer(); + state = 6; break; - case 5: // Save the image tot he sketch folder - println(); - String filename = "tft_screen_" + n + image_type; - println("Saving image as \"" + filename); // Does not execute - if (save_border) - { - PImage partialSave = get(x_offset - border, y_offset - border, tft_width + 2*border, tft_height + 2*border); - partialSave.save(filename); - } else { - PImage partialSave = get(x_offset, y_offset, tft_width, tft_height); - partialSave.save(filename); - } - - n = n + 1; - if (n>9) n = 0; - drawLoopCount = 0; // Reset value ready for counting in step 6 + case 5: // Save the image to the sketch folder (Ctrl+K to access) + saveScreenshot(); + saved_image_count++; + println("Saved image count = " + saved_image_count); + if (bad_image_count > 0) System.err.println(" Bad image count = " + bad_image_count); + drawLoopCount = frameCount; // Reset value ready for counting in step 6 state = 6; break; case 6: // Fade the old image if enabled - delay(10); - if (fade) - { - tint(255, drawLoopCount); - image(tft_img, x_offset, y_offset); - } - if (drawLoopCount > 50) state = 0; // Wait for fade to end + if ( fadedImage() == true ) state = 0; // Go to next state when image has faded break; case 99: // Draw image viewer window - textAlign(CENTER); - textSize(20); - background(bgcolor); - image(img, 0, 0); - - fill(0); - text("Bodmer's TFT image viewer", width/2, height-10); - - stroke(0, 0, 0); - - rect(x_offset - border, y_offset - border, tft_width - 1 + 2*border, tft_height - 1 + 2*border); - - fill(100); - rect(x_offset, y_offset, tft_width-1, tft_height-1); - + drawWindow(); + delay(50); // Delay here seems to be required for the IDE console to get ready state = 0; break; @@ -301,4 +225,186 @@ void draw() { } } -*/ // <<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS LINE <<<<<<<<<<<<<<<<<<<<<<<<< +void drawWindow() +{ + // Graded background in Arduino colours + for (int i = 0; i < height - 25; i++) { + float inter = map(i, 0, height - 25, 0, 1); + color c = lerpColor(bgcolor1, bgcolor2, inter); + stroke(c); + line(0, i, 500, i); + } + fill(bgcolor2); + rect( 0, height-25, width-1, 24); + textAlign(CENTER); + textSize(20); + fill(0); + text("Bodmer's TFT image viewer", width/2, height-6); +} + +void flushBuffer() +{ + //println("Clearing serial pipe after a time-out"); + int clearTime = millis() + 50; + while ( millis() < clearTime ) serial.read(); +} + +boolean getSize() +{ + if ( serial.available() > 6 ) { + println(); + char code = (char)serial.read(); + if (code == 'W') { + tft_width = serial.read()<<8 | serial.read(); + } + code = (char)serial.read(); + if (code == 'H') { + tft_height = serial.read()<<8 | serial.read(); + } + code = (char)serial.read(); + if (code == 'Y') { + int bits_per_pixel = (char)serial.read(); + if (bits_per_pixel == 24) color_bytes = 3; + else color_bytes = 2; + } + code = (char)serial.read(); + if (code == '?') { + drawWindow(); + + x_offset = (500 - tft_width) /2; + tint(0, 0, 0, 255); + noStroke(); + fill(frameColor); + rect((width - tft_width)/2 - border, y_offset - border, tft_width + 2 * border, tft_height + 2 * border); + return true; + } + } + return false; +} + +int renderPixels() +{ + if ( serial.available() > 0 ) { + + // Add the latest byte from the serial port to array: + while (serial.available()>0) + { + rgb[serialCount++] = serial.read(); + + // If we have 3 colour bytes: + if ( serialCount >= color_bytes ) { + serialCount = 0; + pixel_count++; + if (color_bytes == 3) + { + stroke(rgb[indexRed], rgb[indexGreen], rgb[indexBlue], 1000); + } else + { + stroke( (rgb[1] & 0x1F)<<3, (rgb[1] & 0xE0)>>3 | (rgb[0] & 0x07)<<5, (rgb[0] & 0xF8)); + //stroke( (rgb[1] & 0xF8), (rgb[0] & 0xE0)>>3 | (rgb[1] & 0x07)<<5, (rgb[0] & 0x1F)<<3); + } + // We get some pixel merge aliasing if smooth() is defined, so draw pixel twice + point(xpos + x_offset, ypos + y_offset); + //point(xpos + x_offset, ypos + y_offset); + + lastPixelTime = millis(); + xpos++; + if (xpos >= tft_width) { + xpos = 0; + progressBar(); + ypos++; + if (ypos>=tft_height) { + ypos = 0; + if ((int)percentage <100) { + while(progress_bar++ < 64) print(" "); + percent(100); + } + println("Image fetch time = " + (millis()-beginTime)/1000.0 + " s"); + return 5; + } + } + } + } + } else + { + if (millis() > (lastPixelTime + pixelWaitTime)) + { + println(""); + System.err.println(pixelWaitTime + "ms time-out for pixels exceeded..."); + if (pixel_count > 0) { + bad_image_count++; + System.err.print("Pixels missing = " + (tft_width * tft_height - pixel_count)); + System.err.println(", corrupted image not saved"); + System.err.println("Good image count = " + saved_image_count); + System.err.println(" Bad image count = " + bad_image_count); + } + return 4; + } + } + return 3; +} + +void progressBar() +{ + progress_bar++; + print("."); + if (progress_bar >63) + { + progress_bar = 0; + percentage = 0.5 + 100 * pixel_count/(0.001 + tft_width * tft_height); + percent(percentage); + } +} + +void percent(float percentage) +{ + if (percentage > 100) percentage = 100; + println(" [ " + (int)percentage + "% ]"); + textAlign(LEFT); + textSize(16); + noStroke(); + fill(bgcolor2); + rect(10, height - 25, 70, 20); + fill(0); + text(" [ " + (int)percentage + "% ]", 10, height-8); +} + +void saveScreenshot() +{ + println(); + String filename = "tft_screen_" + n + image_type; + println("Saving image as \"" + filename); + if (save_border) + { + PImage partialSave = get(x_offset - border, y_offset - border, tft_width + 2*border, tft_height + 2*border); + partialSave.save(filename); + } else { + PImage partialSave = get(x_offset, y_offset, tft_width, tft_height); + partialSave.save(filename); + } + + n = n + 1; + if (n>=max_images) n = 0; +} + +boolean fadedImage() +{ + int opacity = frameCount - drawLoopCount; // So we get increasing fade + if (fade) + { + tint(255, opacity); + //image(tft_img, x_offset, y_offset); + noStroke(); + fill(50, 50, 50, opacity); + rect( (width - tft_width)/2, y_offset, tft_width, tft_height); + delay(10); + } + if (opacity > 50) // End fade after 50 cycles + { + return true; + } + return false; +} + + +*/ diff --git a/examples/ILI9341/TFT_Screen_Capture/screenServer.ino b/examples/ILI9341/TFT_Screen_Capture/screenServer.ino index 911750b..4c9ecbf 100644 --- a/examples/ILI9341/TFT_Screen_Capture/screenServer.ino +++ b/examples/ILI9341/TFT_Screen_Capture/screenServer.ino @@ -1,68 +1,62 @@ -// TFT screenshot server +// Reads a screen image off the TFT and send it to a processing client sketch +// over the serial port. Use a high baud rate, e.g. for an ESP8266: +// Serial.begin(921600); -// This is a sketch support tab containing function calls to read a screen image -// off a TFT and send it to a processing client sketch over the serial port. - -// See the processing_sketch tab, it contains a copy of the processing sketch. - -// Use a high baud rate, for an ESP8266: -/* - Serial.begin(921600); -*/ -// 240 x 320 images take about 3.5s to transfer at 921600 baud(minimum is ~2.5s) +// At 921600 baud a 320 x 240 image with 16 bit colour transfers can be sent to the +// PC client in ~1.67s and 24 bit colour in ~2.5s which is close to the theoretical +// minimum transfer time. // This sketch has been created to work with the TFT_eSPI library here: // https://github.com/Bodmer/TFT_eSPI // Created by: Bodmer 27/1/17 +// Updated by: Bodmer 10/3/17 +// Version: 0.06 -// The MIT permissive free software license applies, include all text above in -// derivatives. +// MIT licence applies, all text above must be included in derivative works -#define BAUD_RATE 250000 // Maximum Arduino IDE Serial Monitor rate -#define DUMP_BAUD_RATE 921600 // Rate used for screen dumps by ESP8266 -#define PIXEL_TIMEOUT 100 // 100ms Time-out between pixel requests -#define START_TIMEOUT 10000 // 10s Maximum time to wait at start transfer +#define BAUD_RATE 250000 // Maximum Serial Monitor rate for other messages +#define DUMP_BAUD_RATE 921600 // Rate used for screen dumps + +#define PIXEL_TIMEOUT 100 // 100ms Time-out between pixel requests +#define START_TIMEOUT 10000 // 10s Maximum time to wait at start transfer + +#define BITS_PER_PIXEL 16 // 24 for RGB colour format, 16 for 565 colour format + +// Number of pixels to send in a burst (minimum of 1), no benefit above 8 +// NPIXELS values and render times: 1 = 5.0s, 2 = 1.75s, 4 = 1.68s, 8 = 1.67s +#define NPIXELS 8 // Must be integer division of both TFT width and TFT height + // Start a screen dump server (serial or network) boolean screenServer(void) { Serial.end(); // Stop the serial port (clears buffers too) Serial.begin(DUMP_BAUD_RATE); // Force baud rate to be high - yield(); - + delay(0); // Equivalent to yield() for ESP8266; + boolean result = serialScreenServer(); // Screenshot serial port server - //boolean result = wifiDump(); // Screenshot WiFi UDP port server (WIP) - + //boolean result = wifiScreenServer(); // Screenshot WiFi UDP port server (WIP) + Serial.end(); // Stop the serial port (clears buffers too) Serial.begin(BAUD_RATE); // Return baud rate to normal - yield(); + delay(0); // Equivalent to yield() for ESP8266; //Serial.println(); //if (result) Serial.println(F("Screen dump passed :-)")); //else Serial.println(F("Screen dump failed :-(")); - + return result; } // Screenshot serial port server (Processing sketch acts as client) boolean serialScreenServer(void) { - // Serial commands from client: - // 'S' to start the transfer process (To do: reply with width + height) - // 'R' or any character except 'X' to request pixel - // 'X' to abort and return immediately to caller - // Returned boolean values: - // true = image despatched OK - // false = time-out or abort command received // Precautionary receive buffer garbage flush for 50ms uint32_t clearTime = millis() + 50; - while ( millis() < clearTime ) { - Serial.read(); - yield(); - } + while ( millis() < clearTime && Serial.read() >= 0) delay(0); // Equivalent to yield() for ESP8266; boolean wait = true; uint32_t lastCmdTime = millis(); // Initialise start of command time-out @@ -70,7 +64,7 @@ boolean serialScreenServer(void) // Wait for the starting flag with a start time-out while (wait) { - yield(); + delay(0); // Equivalent to yield() for ESP8266; // Check serial buffer if (Serial.available() > 0) { // Read the command byte @@ -79,21 +73,21 @@ boolean serialScreenServer(void) if ( cmd == 'S' ) { // Precautionary receive buffer garbage flush for 50ms clearTime = millis() + 50; - while ( millis() < clearTime ) { - Serial.read(); - yield(); - } + while ( millis() < clearTime && Serial.read() >= 0) delay(0); // Equivalent to yield() for ESP8266; + wait = false; // No need to wait anymore lastCmdTime = millis(); // Set last received command time - // Send screen size, not supported by processing sketch yet - //Serial.write('W'); - //Serial.write(tft.width() >> 8); - //Serial.write(tft.width() & 0xFF); - //Serial.write('H'); - //Serial.write(tft.height() >> 8); - //Serial.write(tft.height() & 0xFF); - //Serial.write('Y'); + // Send screen size using a simple header with delimiters for client checks + Serial.write('W'); + Serial.write(tft.width() >> 8); + Serial.write(tft.width() & 0xFF); + Serial.write('H'); + Serial.write(tft.height() >> 8); + Serial.write(tft.height() & 0xFF); + Serial.write('Y'); + Serial.write(BITS_PER_PIXEL); + Serial.write('?'); } } else @@ -103,49 +97,49 @@ boolean serialScreenServer(void) } } - uint8_t color[3]; // RGB color buffer for 1 pixel - - // Send all the pixels on the whole screen (typically 5 seconds at 921600 baud) + uint8_t color[3 * NPIXELS]; // RGB and 565 format color buffer for N pixels + + // Send all the pixels on the whole screen for ( uint32_t y = 0; y < tft.height(); y++) { - // Increment x by 2 as we send 2 pixels for every byte received - for ( uint32_t x = 0; x < tft.width(); x += 1) + // Increment x by NPIXELS as we send NPIXELS for every byte received + for ( uint32_t x = 0; x < tft.width(); x += NPIXELS) { - yield(); + delay(0); // Equivalent to yield() for ESP8266; // Wait here for serial data to arrive or a time-out elapses while ( Serial.available() == 0 ) { - yield; if ( millis() > lastCmdTime + PIXEL_TIMEOUT) return false; + delay(0); // Equivalent to yield() for ESP8266; } // Serial data must be available to get here, read 1 byte and - // respond with N pixels, i.e. N x 3 RGB bytes + // respond with N pixels, i.e. N x 3 RGB bytes or N x 2 565 format bytes if ( Serial.read() == 'X' ) { // X command byte means abort, so clear the buffer and return clearTime = millis() + 50; - while ( millis() < clearTime ) Serial.read(); + while ( millis() < clearTime && Serial.read() >= 0) delay(0); // Equivalent to yield() for ESP8266; return false; } // Save arrival time of the read command (for later time-out check) lastCmdTime = millis(); - // Fetch data for N pixels starting at x,y - tft.readRectRGB(x, y, 1, 1, color); - // Send values to client - Serial.write(color[0]); // Pixel 1 red - Serial.write(color[1]); // Pixel 1 green - Serial.write(color[2]); // Pixel 1 blue - //Serial.write(color[3]); // Pixel 2 red - //Serial.write(color[4]); // Pixel 2 green - //Serial.write(color[5]); // Pixel 2 blue +#if defined BITS_PER_PIXEL && BITS_PER_PIXEL >= 24 + // Fetch N RGB pixels from x,y and put in buffer + tft.readRectRGB(x, y, NPIXELS, 1, color); + // Send buffer to client + Serial.write(color, 3 * NPIXELS); // Write all pixels in the buffer +#else + // Fetch N 565 format pixels from x,y and put in buffer + tft.readRect(x, y, NPIXELS, 1, (uint16_t *)color); + // Send buffer to client + Serial.write(color, 2 * NPIXELS); // Write all pixels in the buffer +#endif } } - - // Receive buffer excess command flush for 50ms - clearTime = millis() + 50; - while ( millis() < clearTime ) Serial.read(); + + Serial.flush(); // Make sure all pixel bytes have been despatched return true; }