From 3314530bbbeccb2a344ef75391d197228a75070e Mon Sep 17 00:00:00 2001 From: Bodmer Date: Tue, 28 Feb 2017 22:13:16 +0000 Subject: [PATCH] Add processing screenshot viewer --- ...LIScreenshotViewer licence information.txt | 23 -- Tools/ILIScreenshotViewer.exe | Bin 16896 -> 0 bytes Tools/Screenshot_client/Screenshot_client.pde | 285 ++++++++++++++++ .../TFT_Screen_Capture/TFT_Screen_Capture.ino | 165 ++++++++++ .../TFT_Screen_Capture/processing_sketch.ino | 304 ++++++++++++++++++ .../TFT_Screen_Capture/screenServer.ino | 152 +++++++++ 6 files changed, 906 insertions(+), 23 deletions(-) delete mode 100644 Tools/ILIScreenshotViewer licence information.txt delete mode 100644 Tools/ILIScreenshotViewer.exe create mode 100644 Tools/Screenshot_client/Screenshot_client.pde create mode 100644 examples/ILI9341/TFT_Screen_Capture/TFT_Screen_Capture.ino create mode 100644 examples/ILI9341/TFT_Screen_Capture/processing_sketch.ino create mode 100644 examples/ILI9341/TFT_Screen_Capture/screenServer.ino diff --git a/Tools/ILIScreenshotViewer licence information.txt b/Tools/ILIScreenshotViewer licence information.txt deleted file mode 100644 index 2c9f877..0000000 --- a/Tools/ILIScreenshotViewer licence information.txt +++ /dev/null @@ -1,23 +0,0 @@ - -This ILIScreenshotViewer.exe file is part of the Arduino ILI9341_due library. - -A set of compatible functions is included in the "ESP8266_uncannyEyes" example. - -The original library licence conditions that apply to the software in this folder are: - -Copyright (c) 2014 Marek Buriak - -Sources can be found at https://github.com/marekburiak/ILI9341_Due. - -ILI9341_due is free software you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 2.1 of the License, or -(at your option) any later version. - -ILI9341_due is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with ILI9341_due. If not, see . diff --git a/Tools/ILIScreenshotViewer.exe b/Tools/ILIScreenshotViewer.exe deleted file mode 100644 index d9f4ed92b984202825858c42de4efe19a035df36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16896 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 diff --git a/Tools/Screenshot_client/Screenshot_client.pde b/Tools/Screenshot_client/Screenshot_client.pde new file mode 100644 index 0000000..22f6afd --- /dev/null +++ b/Tools/Screenshot_client/Screenshot_client.pde @@ -0,0 +1,285 @@ +// 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. + +// 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 + +// Captured images are stored in the sketch folder, use "Sketch" menu option +// "Show Sketch Folder" or press Ctrl+K + +// Created by: Bodmer 27/1/17 + +// 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 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 # +// # +// Change the image file type saved here, comment out all but one # +//String image_type = ".jpg"; // # +String image_type = ".png"; // # +//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 = 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 + +int bgcolor = 255; // Background color + +PImage img, tft_img; + +color light_blue = color(50, 128, 255); + +int[] rgb = new int[6]; // Buffer for the RGB 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 x_offset = (500 - tft_width) /2; // Image offsets in the window +int y_offset = 20; // +int xpos, ypos; // Pixel position + +int beginTime = 0; +int pixelWaitTime = 100; // Wait a maximum of 100ms gap 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 drawLoopCount = 0; + +void setup() { + + size(500, 540); // Stage size, could handle 480 pixel scrren + noStroke(); // No border on the next thing drawn + + 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); + } + + 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; + + // Print a list of the available serial ports + println("-----------------------"); + println("Available Serial Ports:"); + println("-----------------------"); + printArray(Serial.list()); + println("-----------------------"); + + print("Port currently used: ["); + print(serial_port); + println("]"); + + 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); + + println(""); + //println("Clearing pipe..."); + beginTime = millis() + 200; + while ( millis() < beginTime ) + { + serial.read(); + } + println("Ready to receive image"); + serial.write("S"); + xpos = 0; + ypos = 0; + serialCount = 0; + progress_bar = 0; + pixel_count = 0; + percentage = 0; + drawLoopCount = 0; + lastPixelTime = millis() + 1000; + state = 1; + break; + + case 1: // Console message, give server some time + println("Requesting image"); + delay(10); + 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"); + } + } + break; + + case 4: // 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; + 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 + 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 + 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); + + state = 0; + break; + + default: + println(""); + System.err.println("Error state reached - check sketch!"); + break; + } +} \ No newline at end of file diff --git a/examples/ILI9341/TFT_Screen_Capture/TFT_Screen_Capture.ino b/examples/ILI9341/TFT_Screen_Capture/TFT_Screen_Capture.ino new file mode 100644 index 0000000..ceed295 --- /dev/null +++ b/examples/ILI9341/TFT_Screen_Capture/TFT_Screen_Capture.ino @@ -0,0 +1,165 @@ +/* + 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 uses the GLCD, 2, 4, 6 fonts. + + Make sure all the display driver and pin comnenctions are correct by + editting the User_Setup.h file in the TFT_eSPI library folder. + + ######################################################################### + ###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ###### + ######################################################################### +*/ + +#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 + +unsigned long targetTime = 0; +byte red = 31; +byte green = 0; +byte blue = 0; +byte state = 0; +unsigned int colour = red << 11; // Colour order is RGB 5+6+5 bits each + +void setup(void) { + Serial.begin(921600); + + tft.init(); + tft.setRotation(0); + tft.fillScreen(TFT_BLACK); + + targetTime = millis() + 1000; +} + +void loop() { + + if (targetTime < millis()) { + targetTime = millis() + 4000; + + 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.setTextFont(0); // Select font 0 which is the Adafruit font + tft.print("Original Adafruit font!"); + + //tft.drawString("Original Adafruit font!",60,5,1); + + // 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.drawCentreString("12.34 is in font size 6", 120, 92, 2); // Draw text centre at position 120, 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 + 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.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.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_GREEN, TFT_BLACK); // This time we will use green text on a black background + tft.setTextFont(2); // Select font 2 + + // 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(TL_DATUM); // Reset to top left for text datum + screenServer(); + } +} + +// Fill screen with a rainbow pattern +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--) { + // This is a "state machine" that ramps up/down the colour brightnesses in sequence + switch (state) { + case 0: + green ++; + if (green == 64) { + green = 63; + state = 1; + } + break; + case 1: + red--; + if (red == 255) { + red = 0; + state = 2; + } + break; + case 2: + blue ++; + if (blue == 32) { + blue = 31; + state = 3; + } + break; + case 3: + green --; + if (green == 255) { + green = 0; + state = 4; + } + break; + case 4: + red ++; + if (red == 32) { + red = 31; + state = 5; + } + break; + case 5: + blue --; + if (blue == 255) { + blue = 0; + state = 0; + } + break; + } + colour = red << 11 | green << 5 | blue; + // 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 + } +} + + + diff --git a/examples/ILI9341/TFT_Screen_Capture/processing_sketch.ino b/examples/ILI9341/TFT_Screen_Capture/processing_sketch.ino new file mode 100644 index 0000000..4fafcbd --- /dev/null +++ b/examples/ILI9341/TFT_Screen_Capture/processing_sketch.ino @@ -0,0 +1,304 @@ +// 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! + +// 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: +// 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. + +// 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 + +// Captured images are stored in the Processing sketch folder, use "Sketch" menu option +// "Show Sketch Folder" or press Ctrl+K in the Processing IDE. + +// Created by: Bodmer 27/1/17 + +// 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 # +// # +// Change the image file type saved here, comment out all but one # +//String image_type = ".jpg"; // # +String image_type = ".png"; // # +//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 = 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 + +int bgcolor = 255; // Background color + +PImage img, tft_img; + +color light_blue = color(50, 128, 255); + +int[] rgb = new int[6]; // Buffer for the RGB 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 x_offset = (500 - tft_width) /2; // Image offsets in the window +int y_offset = 20; // +int xpos, ypos; // Pixel position + +int beginTime = 0; +int pixelWaitTime = 100; // Wait a maximum of 100ms gap 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 drawLoopCount = 0; + +void setup() { + + size(500, 540); // Stage size, could handle 480 pixel scrren + noStroke(); // No border on the next thing drawn + + 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); + } + + 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; + + // Print a list of the available serial ports + println("-----------------------"); + println("Available Serial Ports:"); + println("-----------------------"); + printArray(Serial.list()); + println("-----------------------"); + + print("Port currently used: ["); + print(serial_port); + println("]"); + + 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); + + println(""); + //println("Clearing pipe..."); + beginTime = millis() + 200; + while ( millis() < beginTime ) + { + serial.read(); + } + println("Ready to receive image"); + serial.write("S"); + xpos = 0; + ypos = 0; + serialCount = 0; + progress_bar = 0; + pixel_count = 0; + percentage = 0; + drawLoopCount = 0; + lastPixelTime = millis() + 1000; + state = 1; + break; + + case 1: // Console message, give server some time + println("Requesting image"); + delay(10); + 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"); + } + } + break; + + case 4: // 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; + 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 + 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 + 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); + + state = 0; + break; + + default: + println(""); + System.err.println("Error state reached - check sketch!"); + break; + } +} + +*/ // <<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS LINE <<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/examples/ILI9341/TFT_Screen_Capture/screenServer.ino b/examples/ILI9341/TFT_Screen_Capture/screenServer.ino new file mode 100644 index 0000000..53a8dc0 --- /dev/null +++ b/examples/ILI9341/TFT_Screen_Capture/screenServer.ino @@ -0,0 +1,152 @@ +// TFT screenshot server + +// 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 capy 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) + +// 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 + +// The MIT permissive free software license applies, include all text above in +// derivatives. + +#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 + +// 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(); + + boolean result = serialScreenServer(); // Screenshot serial port server + //boolean result = wifiDump(); // 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(); + + //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(); + } + + boolean wait = true; + uint32_t lastCmdTime = millis(); // Initialise start of command time-out + + // Wait for the starting flag with a start time-out + while (wait) + { + yield(); + // Check serial buffer + if (Serial.available() > 0) { + // Read the command byte + uint8_t cmd = Serial.read(); + // If it is 'S' (start command) then clear the serial buffer for 100ms and stop waiting + if ( cmd == 'S' ) { + // Precautionary receive buffer garbage flush for 50ms + clearTime = millis() + 50; + while ( millis() < clearTime ) { + Serial.read(); + yield(); + } + 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'); + } + } + else + { + // Check for time-out + if ( millis() > lastCmdTime + START_TIMEOUT) return false; + } + } + + uint8_t color[3]; // RGB color buffer for 1 pixel + + // Send all the pixels on the whole screen (typically 5 seconds at 921600 baud) + 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) + { + yield(); + + // Wait here for serial data to arrive or a time-out elapses + while ( Serial.available() == 0 ) + { + yield; + if ( millis() > lastCmdTime + PIXEL_TIMEOUT) return false; + } + + // Serial data must be available to get here, read 1 byte and + // respond with N pixels, i.e. N x 3 RGB bytes + if ( Serial.read() == 'X' ) { + // X command byte means abort, so clear the buffer and return + clearTime = millis() + 50; + while ( millis() < clearTime ) Serial.read(); + 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 + } + } + + // Receive buffer excess command flush for 50ms + clearTime = millis() + 50; + while ( millis() < clearTime ) Serial.read(); + + return true; +} +