acidportal

- 😈 Worlds smallest Evil Portal on a LilyGo T-QT
git clone git://git.acid.vegas/acidportal.git
Log | Files | Refs | Archive | README | LICENSE

screenServer.ino (7869B)

      1 // Reads a screen image off the TFT and send it to a processing client sketch
      2 // over the serial port. Use a high baud rate, e.g. for an ESP8266:
      3 // Serial.begin(921600);
      4 
      5 // At 921600 baud a 320 x 240 image with 16 bit colour transfers can be sent to the
      6 // PC client in ~1.67s and 24 bit colour in ~2.5s which is close to the theoretical
      7 // minimum transfer time.
      8 
      9 // This sketch has been created to work with the TFT_eSPI library here:
     10 // https://github.com/Bodmer/TFT_eSPI
     11 
     12 // Created by: Bodmer 27/1/17
     13 // Updated by: Bodmer 10/3/17
     14 // Updated by: Bodmer 23/11/18 to support SDA reads and the ESP32
     15 // Version: 0.08
     16 
     17 // MIT licence applies, all text above must be included in derivative works
     18 
     19 //====================================================================================
     20 //                                  Definitions
     21 //====================================================================================
     22 #define PIXEL_TIMEOUT 100     // 100ms Time-out between pixel requests
     23 #define START_TIMEOUT 10000   // 10s Maximum time to wait at start transfer
     24 
     25 #define BITS_PER_PIXEL 16     // 24 for RGB colour format, 16 for 565 colour format
     26 
     27 // File names must be alpha-numeric characters (0-9, a-z, A-Z) or "/" underscore "_"
     28 // other ascii characters are stripped out by client, including / generates
     29 // sub-directories
     30 #define DEFAULT_FILENAME "tft_screenshots/screenshot" // In case none is specified
     31 #define FILE_TYPE "png"       // jpg, bmp, png, tif are valid
     32 
     33 // Filename extension
     34 // '#' = add incrementing number, '@' = add timestamp, '%' add millis() timestamp,
     35 // '*' = add nothing
     36 // '@' and '%' will generate new unique filenames, so beware of cluttering up your
     37 // hard drive with lots of images! The PC client sketch is set to limit the number of
     38 // saved images to 1000 and will then prompt for a restart.
     39 #define FILE_EXT  '@'         
     40 
     41 // Number of pixels to send in a burst (minimum of 1), no benefit above 8
     42 // NPIXELS values and render times:
     43 // NPIXELS 1 = use readPixel() = >5s and 16 bit pixels only
     44 // NPIXELS >1 using rectRead() 2 = 1.75s, 4 = 1.68s, 8 = 1.67s
     45 #define NPIXELS 8  // Must be integer division of both TFT width and TFT height
     46 
     47 //====================================================================================
     48 //                           Screen server call with no filename
     49 //====================================================================================
     50 // Start a screen dump server (serial or network) - no filename specified
     51 bool screenServer(void)
     52 {
     53   // With no filename the screenshot will be saved with a default name e.g. tft_screen_#.xxx
     54   // where # is a number 0-9 and xxx is a file type specified below
     55   return screenServer(DEFAULT_FILENAME);
     56 }
     57 
     58 //====================================================================================
     59 //                           Screen server call with filename
     60 //====================================================================================
     61 // Start a screen dump server (serial or network) - filename specified
     62 bool screenServer(String filename)
     63 {
     64   delay(0); // Equivalent to yield() for ESP8266;
     65 
     66   bool result = serialScreenServer(filename); // Screenshot serial port server
     67   //bool result = wifiScreenServer(filename);   // Screenshot WiFi UDP port server (WIP)
     68 
     69   delay(0); // Equivalent to yield()
     70 
     71   //Serial.println();
     72   //if (result) Serial.println(F("Screen dump passed :-)"));
     73   //else        Serial.println(F("Screen dump failed :-("));
     74 
     75   return result;
     76 }
     77 
     78 //====================================================================================
     79 //                Serial server function that sends the data to the client
     80 //====================================================================================
     81 bool serialScreenServer(String filename)
     82 {
     83   // Precautionary receive buffer garbage flush for 50ms
     84   uint32_t clearTime = millis() + 50;
     85   while ( millis() < clearTime && Serial.read() >= 0) delay(0); // Equivalent to yield() for ESP8266;
     86 
     87   bool wait = true;
     88   uint32_t lastCmdTime = millis();     // Initialise start of command time-out
     89 
     90   // Wait for the starting flag with a start time-out
     91   while (wait)
     92   {
     93     delay(0); // Equivalent to yield() for ESP8266;
     94     // Check serial buffer
     95     if (Serial.available() > 0) {
     96       // Read the command byte
     97       uint8_t cmd = Serial.read();
     98       // If it is 'S' (start command) then clear the serial buffer for 100ms and stop waiting
     99       if ( cmd == 'S' ) {
    100         // Precautionary receive buffer garbage flush for 50ms
    101         clearTime = millis() + 50;
    102         while ( millis() < clearTime && Serial.read() >= 0) delay(0); // Equivalent to yield() for ESP8266;
    103 
    104         wait = false;           // No need to wait anymore
    105         lastCmdTime = millis(); // Set last received command time
    106 
    107         // Send screen size etc using a simple header with delimiters for client checks
    108         sendParameters(filename);
    109       }
    110     }
    111     else
    112     {
    113       // Check for time-out
    114       if ( millis() > lastCmdTime + START_TIMEOUT) return false;
    115     }
    116   }
    117 
    118   uint8_t color[3 * NPIXELS]; // RGB and 565 format color buffer for N pixels
    119 
    120   // Send all the pixels on the whole screen
    121   for ( uint32_t y = 0; y < tft.height(); y++)
    122   {
    123     // Increment x by NPIXELS as we send NPIXELS for every byte received
    124     for ( uint32_t x = 0; x < tft.width(); x += NPIXELS)
    125     {
    126       delay(0); // Equivalent to yield() for ESP8266;
    127 
    128       // Wait here for serial data to arrive or a time-out elapses
    129       while ( Serial.available() == 0 )
    130       {
    131         if ( millis() > lastCmdTime + PIXEL_TIMEOUT) return false;
    132         delay(0); // Equivalent to yield() for ESP8266;
    133       }
    134 
    135       // Serial data must be available to get here, read 1 byte and
    136       // respond with N pixels, i.e. N x 3 RGB bytes or N x 2 565 format bytes
    137       if ( Serial.read() == 'X' ) {
    138         // X command byte means abort, so clear the buffer and return
    139         clearTime = millis() + 50;
    140         while ( millis() < clearTime && Serial.read() >= 0) delay(0); // Equivalent to yield() for ESP8266;
    141         return false;
    142       }
    143       // Save arrival time of the read command (for later time-out check)
    144       lastCmdTime = millis();
    145 
    146 #if defined BITS_PER_PIXEL && BITS_PER_PIXEL >= 24 && NPIXELS > 1
    147       // Fetch N RGB pixels from x,y and put in buffer
    148       tft.readRectRGB(x, y, NPIXELS, 1, color);
    149       // Send buffer to client
    150       Serial.write(color, 3 * NPIXELS); // Write all pixels in the buffer
    151 #else
    152       // Fetch N 565 format pixels from x,y and put in buffer
    153       if (NPIXELS > 1) tft.readRect(x, y, NPIXELS, 1, (uint16_t *)color);
    154       else
    155       {
    156         uint16_t c = tft.readPixel(x, y);
    157         color[0] = c>>8;
    158         color[1] = c & 0xFF;  // Swap bytes
    159       }
    160       // Send buffer to client
    161       Serial.write(color, 2 * NPIXELS); // Write all pixels in the buffer
    162 #endif
    163     }
    164   }
    165 
    166   Serial.flush(); // Make sure all pixel bytes have been despatched
    167 
    168   return true;
    169 }
    170 
    171 //====================================================================================
    172 //    Send screen size etc using a simple header with delimiters for client checks
    173 //====================================================================================
    174 void sendParameters(String filename)
    175 {
    176   Serial.write('W'); // Width
    177   Serial.write(tft.width()  >> 8);
    178   Serial.write(tft.width()  & 0xFF);
    179 
    180   Serial.write('H'); // Height
    181   Serial.write(tft.height() >> 8);
    182   Serial.write(tft.height() & 0xFF);
    183 
    184   Serial.write('Y'); // Bits per pixel (16 or 24)
    185   if (NPIXELS > 1) Serial.write(BITS_PER_PIXEL);
    186   else Serial.write(16); // readPixel() only provides 16 bit values
    187 
    188   Serial.write('?'); // Filename next
    189   Serial.print(filename);
    190 
    191   Serial.write('.'); // End of filename marker
    192 
    193   Serial.write(FILE_EXT); // Filename extension identifier
    194 
    195   Serial.write(*FILE_TYPE); // First character defines file type j,b,p,t
    196 }