197 lines
7.7 KiB
Arduino
197 lines
7.7 KiB
Arduino
|
// 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);
|
||
|
|
||
|
// 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
|
||
|
// Updated by: Bodmer 23/11/18 to support SDA reads and the ESP32
|
||
|
// Version: 0.08
|
||
|
|
||
|
// MIT licence applies, all text above must be included in derivative works
|
||
|
|
||
|
//====================================================================================
|
||
|
// Definitions
|
||
|
//====================================================================================
|
||
|
#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
|
||
|
|
||
|
// File names must be alpha-numeric characters (0-9, a-z, A-Z) or "/" underscore "_"
|
||
|
// other ascii characters are stripped out by client, including / generates
|
||
|
// sub-directories
|
||
|
#define DEFAULT_FILENAME "tft_screenshots/screenshot" // In case none is specified
|
||
|
#define FILE_TYPE "png" // jpg, bmp, png, tif are valid
|
||
|
|
||
|
// Filename extension
|
||
|
// '#' = add incrementing number, '@' = add timestamp, '%' add millis() timestamp,
|
||
|
// '*' = add nothing
|
||
|
// '@' and '%' will generate new unique filenames, so beware of cluttering up your
|
||
|
// hard drive with lots of images! The PC client sketch is set to limit the number of
|
||
|
// saved images to 1000 and will then prompt for a restart.
|
||
|
#define FILE_EXT '@'
|
||
|
|
||
|
// Number of pixels to send in a burst (minimum of 1), no benefit above 8
|
||
|
// NPIXELS values and render times:
|
||
|
// NPIXELS 1 = use readPixel() = >5s and 16 bit pixels only
|
||
|
// NPIXELS >1 using rectRead() 2 = 1.75s, 4 = 1.68s, 8 = 1.67s
|
||
|
#define NPIXELS 8 // Must be integer division of both TFT width and TFT height
|
||
|
|
||
|
//====================================================================================
|
||
|
// Screen server call with no filename
|
||
|
//====================================================================================
|
||
|
// Start a screen dump server (serial or network) - no filename specified
|
||
|
bool screenServer(void)
|
||
|
{
|
||
|
// With no filename the screenshot will be saved with a default name e.g. tft_screen_#.xxx
|
||
|
// where # is a number 0-9 and xxx is a file type specified below
|
||
|
return screenServer(DEFAULT_FILENAME);
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
// Screen server call with filename
|
||
|
//====================================================================================
|
||
|
// Start a screen dump server (serial or network) - filename specified
|
||
|
bool screenServer(String filename)
|
||
|
{
|
||
|
delay(0); // Equivalent to yield() for ESP8266;
|
||
|
|
||
|
bool result = serialScreenServer(filename); // Screenshot serial port server
|
||
|
//bool result = wifiScreenServer(filename); // Screenshot WiFi UDP port server (WIP)
|
||
|
|
||
|
delay(0); // Equivalent to yield()
|
||
|
|
||
|
//Serial.println();
|
||
|
//if (result) Serial.println(F("Screen dump passed :-)"));
|
||
|
//else Serial.println(F("Screen dump failed :-("));
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
// Serial server function that sends the data to the client
|
||
|
//====================================================================================
|
||
|
bool serialScreenServer(String filename)
|
||
|
{
|
||
|
// Precautionary receive buffer garbage flush for 50ms
|
||
|
uint32_t clearTime = millis() + 50;
|
||
|
while ( millis() < clearTime && Serial.read() >= 0) delay(0); // Equivalent to yield() for ESP8266;
|
||
|
|
||
|
bool wait = true;
|
||
|
uint32_t lastCmdTime = millis(); // Initialise start of command time-out
|
||
|
|
||
|
// Wait for the starting flag with a start time-out
|
||
|
while (wait)
|
||
|
{
|
||
|
delay(0); // Equivalent to yield() for ESP8266;
|
||
|
// 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() >= 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 etc using a simple header with delimiters for client checks
|
||
|
sendParameters(filename);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Check for time-out
|
||
|
if ( millis() > lastCmdTime + START_TIMEOUT) return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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 NPIXELS as we send NPIXELS for every byte received
|
||
|
for ( uint32_t x = 0; x < tft.width(); x += NPIXELS)
|
||
|
{
|
||
|
delay(0); // Equivalent to yield() for ESP8266;
|
||
|
|
||
|
// Wait here for serial data to arrive or a time-out elapses
|
||
|
while ( Serial.available() == 0 )
|
||
|
{
|
||
|
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 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() >= 0) delay(0); // Equivalent to yield() for ESP8266;
|
||
|
return false;
|
||
|
}
|
||
|
// Save arrival time of the read command (for later time-out check)
|
||
|
lastCmdTime = millis();
|
||
|
|
||
|
#if defined BITS_PER_PIXEL && BITS_PER_PIXEL >= 24 && NPIXELS > 1
|
||
|
// 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
|
||
|
if (NPIXELS > 1) tft.readRect(x, y, NPIXELS, 1, (uint16_t *)color);
|
||
|
else
|
||
|
{
|
||
|
uint16_t c = tft.readPixel(x, y);
|
||
|
color[0] = c>>8;
|
||
|
color[1] = c & 0xFF; // Swap bytes
|
||
|
}
|
||
|
// Send buffer to client
|
||
|
Serial.write(color, 2 * NPIXELS); // Write all pixels in the buffer
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Serial.flush(); // Make sure all pixel bytes have been despatched
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//====================================================================================
|
||
|
// Send screen size etc using a simple header with delimiters for client checks
|
||
|
//====================================================================================
|
||
|
void sendParameters(String filename)
|
||
|
{
|
||
|
Serial.write('W'); // Width
|
||
|
Serial.write(tft.width() >> 8);
|
||
|
Serial.write(tft.width() & 0xFF);
|
||
|
|
||
|
Serial.write('H'); // Height
|
||
|
Serial.write(tft.height() >> 8);
|
||
|
Serial.write(tft.height() & 0xFF);
|
||
|
|
||
|
Serial.write('Y'); // Bits per pixel (16 or 24)
|
||
|
if (NPIXELS > 1) Serial.write(BITS_PER_PIXEL);
|
||
|
else Serial.write(16); // readPixel() only provides 16 bit values
|
||
|
|
||
|
Serial.write('?'); // Filename next
|
||
|
Serial.print(filename);
|
||
|
|
||
|
Serial.write('.'); // End of filename marker
|
||
|
|
||
|
Serial.write(FILE_EXT); // Filename extension identifier
|
||
|
|
||
|
Serial.write(*FILE_TYPE); // First character defines file type j,b,p,t
|
||
|
}
|