acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

processing_sketch.ino (17336B)

      1 // This is a copy of the processing sketch that can be used to capture the images
      2 // Copy the sketch below and remove the /* and */ at the beginning and end.
      3 
      4 // The sketch runs in Processing version 3.3 on a PC, it can be downloaded here:
      5 // https://processing.org/download/
      6 
      7 /*
      8 
      9 // This is a Processing sketch, see https://processing.org/ to download the IDE
     10 
     11 // The sketch is a client that requests TFT screenshots from an Arduino board.
     12 // The Arduino must call a screenshot server function to respond with pixels.
     13 
     14 // It has been created to work with the TFT_eSPI library here:
     15 // https://github.com/Bodmer/TFT_eSPI
     16 
     17 // The sketch must only be run when the designated serial port is available and enumerated
     18 // otherwise the screenshot window may freeze and that process will need to be terminated
     19 // This is a limitation of the Processing environment and not the sketch.
     20 // If anyone knows how to determine if a serial port is available at start up the PM me
     21 // on (Bodmer) the Arduino forum.
     22 
     23 // The block below contains variables that the user may need to change for a particular setup
     24 // As a minimum set the serial port and baud rate must be defined. The capture window is
     25 // automatically resized for landscape, portrait and different TFT resolutions.
     26 
     27 // Captured images are stored in the sketch folder, use the Processing IDE "Sketch" menu
     28 // option "Show Sketch Folder" or press Ctrl+K
     29 
     30 // Created by: Bodmer  5/3/17
     31 // Updated by: Bodmer 12/3/17
     32 // Version: 0.07
     33 
     34 // MIT licence applies, all text above must be included in derivative works
     35 
     36 
     37 // ###########################################################################################
     38 // #                  These are the values to change for a particular setup                  #
     39 //                                                                                           #
     40 int serial_port = 0;     // Use enumerated value from list provided when sketch is run       #
     41 //                                                                                           #
     42 // On an Arduino Due Programming Port use a baud rate of:115200)                             #
     43 // On an Arduino Due Native USB Port use a baud rate of any value                            #
     44 int serial_baud_rate = 921600; //                                                            #
     45 //                                                                                           #
     46 // Change the image file type saved here, comment out all but one                            #
     47 //String image_type = ".jpg"; //                                                             #
     48 String image_type = ".png";   // Lossless compression                                        #
     49 //String image_type = ".bmp"; //                                                             #
     50 //String image_type = ".tif"; //                                                             #
     51 //                                                                                           #
     52 boolean save_border = true;   // Save the image with a border                                #
     53 int border = 5;               // Border pixel width                                          #
     54 boolean fade = false;         // Fade out image after saving                                 #
     55 //                                                                                           #
     56 int max_images = 100; // Maximum of numbered file images before over-writing files           #
     57 //                                                                                           #
     58 int max_allowed  = 1000; // Maximum number of save images allowed before a restart           #
     59 //                                                                                           #
     60 // #                   End of the values to change for a particular setup                    #
     61 // ###########################################################################################
     62 
     63 // These are default values, this sketch obtains the actual values from the Arduino board
     64 int tft_width  = 480;    // default TFT width  (automatic - sent by Arduino)
     65 int tft_height = 480;    // default TFT height (automatic - sent by Arduino)
     66 int color_bytes = 2;     // 2 for 16 bit, 3 for three RGB bytes (automatic - sent by Arduino)
     67 
     68 import processing.serial.*;
     69 
     70 Serial serial;           // Create an instance called serial
     71 
     72 int serialCount = 0;     // Count of colour bytes arriving
     73 
     74 // Stage window graded background colours
     75 color bgcolor1 = color(0, 100, 104);      // Arduino IDE style background color 1
     76 color bgcolor2 = color(77, 183, 187);     // Arduino IDE style background color 2
     77 //color bgcolor2 = color(255, 255, 255);  // White
     78 
     79 // TFT image frame greyscale value (dark grey)
     80 color frameColor = 42;
     81 
     82 color buttonStopped = color(255, 0, 0);
     83 color buttonRunning = color(128, 204, 206);
     84 color buttonDimmed  = color(180, 0, 0);
     85 boolean dimmed   = false;
     86 boolean running  = true;
     87 boolean mouseClick = false;
     88 
     89 int[] rgb = new int[3]; // Buffer for the colour bytes
     90 int indexRed   = 0;     // Colour byte index in the array
     91 int indexGreen = 1;
     92 int indexBlue  = 2;
     93 
     94 int n = 0;
     95 
     96 int x_offset = (500 - tft_width) /2; // Image offsets in the window
     97 int y_offset = 20;
     98 
     99 int xpos = 0, ypos = 0; // Current pixel position
    100 
    101 int beginTime     = 0;
    102 int pixelWaitTime = 1000;  // Maximum 1000ms wait for image pixels to arrive
    103 int lastPixelTime = 0;     // Time that "image send" command was sent
    104 
    105 int requestTime = 0;
    106 int requestCount = 0;
    107 
    108 int state = 0;  // State machine current state
    109 
    110 int   progress_bar = 0; // Console progress bar dot count
    111 int   pixel_count  = 0; // Number of pixels read for 1 screen
    112 float percentage   = 0; // Percentage of pixels received
    113 
    114 int  saved_image_count = 0; // Stats - number of images processed
    115 int  bad_image_count  = 0;  // Stats - number of images that had lost pixels
    116 String filename = "";
    117 
    118 int drawLoopCount = 0;      // Used for the fade out
    119 
    120 void setup() {
    121 
    122   size(500, 540);  // Stage size, can handle 480 pixels wide screen
    123   noStroke();      // No border on the next thing drawn
    124   noSmooth();      // No anti-aliasing to avoid adjacent pixel colour merging
    125 
    126   // Graded background and title
    127   drawWindow();
    128 
    129   frameRate(2000); // High frame rate so draw() loops fast
    130 
    131   // Print a list of the available serial ports
    132   println("-----------------------");
    133   println("Available Serial Ports:");
    134   println("-----------------------");
    135   printArray(Serial.list());
    136   println("-----------------------");
    137 
    138   print("Port currently used: [");
    139   print(serial_port);
    140   println("]");
    141 
    142   String portName = Serial.list()[serial_port];
    143 
    144   serial = new Serial(this, portName, serial_baud_rate);
    145 
    146   state = 99;
    147 }
    148 
    149 void draw() {
    150 
    151   if (mouseClick) buttonClicked();
    152 
    153   switch(state) {
    154 
    155   case 0: // Init varaibles, send start request
    156     if (running) {
    157       tint(0, 0, 0, 255);
    158       flushBuffer();
    159       println("");
    160       print("Ready: ");
    161 
    162       xpos = 0;
    163       ypos = 0;
    164       serialCount = 0;
    165       progress_bar = 0;
    166       pixel_count = 0;
    167       percentage   = 0;
    168       drawLoopCount = frameCount;
    169       lastPixelTime = millis() + 1000;
    170 
    171       state = 1;
    172     } else {
    173       if (millis() > beginTime) {
    174         beginTime = millis() + 500;
    175         dimmed = !dimmed;
    176         if (dimmed) drawButton(buttonDimmed);
    177         else drawButton(buttonStopped);
    178       }
    179     }
    180     break;
    181 
    182   case 1: // Console message, give server some time
    183     print("requesting image ");
    184     serial.write("S");
    185     delay(10);
    186     beginTime = millis();
    187     requestTime = millis() + 1000;
    188     requestCount = 1;
    189     state = 2;
    190     break;
    191 
    192   case 2: // Get size and set start time for rendering duration report
    193     if (millis() > requestTime) {
    194       requestCount++;
    195       print("*");
    196       serial.clear();
    197       serial.write("S");
    198       if (requestCount > 32) {
    199         requestCount = 0;
    200         System.err.println(" - no response!");
    201         state = 0;
    202       }
    203       requestTime = millis() + 1000;
    204     }
    205     if ( getSize() == true ) { // Go to next state when we have the size and bits per pixel
    206       getFilename();
    207       flushBuffer(); // Precaution in case image header size increases in later versions
    208       lastPixelTime = millis() + 1000;
    209       beginTime = millis();
    210       state = 3;
    211     }
    212     break;
    213 
    214   case 3: // Request pixels and render returned RGB values
    215     state = renderPixels(); // State will change when all pixels are rendered
    216 
    217     // Request more pixels, changing the number requested allows the average transfer rate to be controlled
    218     // The pixel transfer rate is dependant on four things:
    219     //    1. The frame rate defined in this Processing sketch in setup()
    220     //    2. The baud rate of the serial link (~10 bit periods per byte)
    221     //    3. The number of request bytes 'R' sent in the lines below
    222     //    4. The number of pixels sent in a burst by the server sketch (defined via NPIXELS)
    223 
    224     //serial.write("RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"); // 32 x NPIXELS more
    225     serial.write("RRRRRRRRRRRRRRRR"); // 16 x NPIXELS more
    226     //serial.write("RRRRRRRR"); // 8 x NPIXELS more
    227     //serial.write("RRRR"); // 4 x NPIXELS more
    228     //serial.write("RR"); // 2 x NPIXELS more
    229     //serial.write("R"); // 1 x NPIXELS more
    230     if (!running) state = 4;
    231     break;
    232 
    233   case 4: // Pixel receive time-out, flush serial buffer
    234     flushBuffer();
    235     state = 6;
    236     break;
    237 
    238   case 5: // Save the image to the sketch folder (Ctrl+K to access)
    239     saveScreenshot();
    240     saved_image_count++;
    241     println("Saved image count = " + saved_image_count);
    242     if (bad_image_count > 0) System.err.println(" Bad image count = " + bad_image_count);
    243     drawLoopCount = frameCount; // Reset value ready for counting in step 6
    244     state = 6;
    245     break;
    246 
    247   case 6: // Fade the old image if enabled
    248     if ( fadedImage() == true ) state = 0; // Go to next state when image has faded
    249     break;
    250 
    251   case 99: // Draw image viewer window
    252     drawWindow();
    253     delay(50); // Delay here seems to be required for the IDE console to get ready
    254     state = 0;
    255     break;
    256 
    257   default:
    258     println("");
    259     System.err.println("Error state reached - check sketch!");
    260     break;
    261   }
    262 }
    263 
    264 void drawWindow()
    265 {
    266   // Graded background in Arduino colours
    267   for (int i = 0; i < height - 25; i++) {
    268     float inter = map(i, 0, height - 25, 0, 1);
    269     color c = lerpColor(bgcolor1, bgcolor2, inter);
    270     stroke(c);
    271     line(0, i, 500, i);
    272   }
    273   fill(bgcolor2);
    274   rect( 0, height-25, width-1, 24);
    275   textAlign(CENTER);
    276   textSize(20);
    277   fill(0);
    278   text("Bodmer's TFT image viewer", width/2, height-6);
    279 
    280   if (running) drawButton(buttonRunning);
    281   else drawButton(buttonStopped);
    282 }
    283 
    284 void flushBuffer()
    285 {
    286   //println("Clearing serial pipe after a time-out");
    287   int clearTime = millis() + 50;
    288   while ( millis() < clearTime ) serial.clear();
    289 }
    290 
    291 boolean getSize()
    292 {
    293   if ( serial.available() > 6 ) {
    294     println();
    295     char code = (char)serial.read();
    296     if (code == 'W') {
    297       tft_width = serial.read()<<8 | serial.read();
    298     }
    299     code = (char)serial.read();
    300     if (code == 'H') {
    301       tft_height = serial.read()<<8 | serial.read();
    302     }
    303     code = (char)serial.read();
    304     if (code == 'Y') {
    305       int bits_per_pixel = (char)serial.read();
    306       if (bits_per_pixel == 24) color_bytes = 3;
    307       else color_bytes = 2;
    308     }
    309     code = (char)serial.read();
    310     if (code == '?') {
    311       drawWindow();
    312 
    313       x_offset = (500 - tft_width) /2;
    314       tint(0, 0, 0, 255);
    315       noStroke();
    316       fill(frameColor);
    317       rect((width - tft_width)/2 - border, y_offset - border, tft_width + 2 * border, tft_height + 2 * border);
    318       return true;
    319     }
    320   }
    321   return false;
    322 }
    323 
    324 void saveScreenshot()
    325 {
    326   println();
    327   if (saved_image_count < max_allowed)
    328   {
    329   if (filename == "") filename = "tft_screen_" + (n++);
    330   filename = filename  + image_type;
    331   println("Saving image as \"" + filename + "\"");
    332   if (save_border)
    333   {
    334     PImage partialSave = get(x_offset - border, y_offset - border, tft_width + 2*border, tft_height + 2*border);
    335     partialSave.save(filename);
    336   } else {
    337     PImage partialSave = get(x_offset, y_offset, tft_width, tft_height);
    338     partialSave.save(filename);
    339   }
    340 
    341   if (n>=max_images) n = 0;
    342   }
    343   else
    344   {
    345     System.err.println(max_allowed + " saved image count exceeded, restart the sketch");
    346   }
    347 }
    348 
    349 void getFilename()
    350 {
    351   int readTime = millis() + 20;
    352   int inByte = 0;
    353   filename = "";
    354   while ( serial.available() > 0 && millis() < readTime && inByte != '.')
    355   {
    356     inByte = serial.read();
    357     if (inByte == ' ') inByte = '_';
    358     if ( unicodeCheck(inByte) ) filename += (char)inByte;
    359   }
    360 
    361   inByte = serial.read();
    362        if (inByte == '@') filename += "_" + timeCode();
    363   else if (inByte == '#') filename += "_" + saved_image_count%100;
    364   else if (inByte == '%') filename += "_" + millis();
    365   else if (inByte != '*') filename  = "";
    366 
    367   inByte = serial.read();
    368        if (inByte == 'j') image_type =".jpg";
    369   else if (inByte == 'b') image_type =".bmp";
    370   else if (inByte == 'p') image_type =".png";
    371   else if (inByte == 't') image_type =".tif";
    372 }
    373 
    374 boolean unicodeCheck(int unicode)
    375 {
    376   if (  unicode >= '0' && unicode <= '9' ) return true;
    377   if ( (unicode >= 'A' && unicode <= 'Z' ) || (unicode >= 'a' && unicode <= 'z')) return true;
    378   if (  unicode == '_' || unicode == '/' ) return true;
    379   return false;
    380 }
    381 
    382 String timeCode()
    383 {
    384  String timeCode  = (int)year() + "_" + (int)month()  + "_" + (int)day() + "_";
    385         timeCode += (int)hour() + "_" + (int)minute() + "_" + (int)second(); 
    386  return timeCode;
    387 }
    388 
    389 int renderPixels()
    390 {
    391   if ( serial.available() > 0 ) {
    392 
    393     // Add the latest byte from the serial port to array:
    394     while (serial.available()>0)
    395     {
    396       rgb[serialCount++] = serial.read();
    397 
    398       // If we have 3 colour bytes:
    399       if ( serialCount >= color_bytes ) {
    400         serialCount = 0;
    401         pixel_count++;
    402         if (color_bytes == 3)
    403         {
    404           stroke(rgb[indexRed], rgb[indexGreen], rgb[indexBlue], 1000);
    405         } else
    406         { // Can cater for various byte orders
    407           //stroke( (rgb[0] & 0x1F)<<3, (rgb[0] & 0xE0)>>3 | (rgb[1] & 0x07)<<5, (rgb[1] & 0xF8));
    408           //stroke( (rgb[1] & 0x1F)<<3, (rgb[1] & 0xE0)>>3 | (rgb[0] & 0x07)<<5, (rgb[0] & 0xF8));
    409           stroke( (rgb[0] & 0xF8), (rgb[1] & 0xE0)>>3 | (rgb[0] & 0x07)<<5, (rgb[1] & 0x1F)<<3);
    410           //stroke( (rgb[1] & 0xF8), (rgb[0] & 0xE0)>>3 | (rgb[1] & 0x07)<<5, (rgb[0] & 0x1F)<<3);
    411         }
    412         // We get some pixel merge aliasing if smooth() is defined, so draw pixel twice
    413         point(xpos + x_offset, ypos + y_offset);
    414         //point(xpos + x_offset, ypos + y_offset);
    415 
    416         lastPixelTime = millis();
    417         xpos++;
    418         if (xpos >= tft_width) {
    419           xpos = 0; 
    420           progressBar();
    421           ypos++;
    422           if (ypos>=tft_height) {
    423             ypos = 0;
    424             if ((int)percentage <100) {
    425               while (progress_bar++ < 64) print(" ");
    426               percent(100);
    427             }
    428             println("Image fetch time = " + (millis()-beginTime)/1000.0 + " s");
    429             return 5;
    430           }
    431         }
    432       }
    433     }
    434   } else
    435   {
    436     if (millis() > (lastPixelTime + pixelWaitTime))
    437     {
    438       println("");
    439       System.err.println(pixelWaitTime + "ms time-out for pixels exceeded...");
    440       if (pixel_count > 0) {
    441         bad_image_count++;
    442         System.err.print("Pixels missing = " + (tft_width * tft_height - pixel_count));
    443         System.err.println(", corrupted image not saved");
    444         System.err.println("Good image count = " + saved_image_count);
    445         System.err.println(" Bad image count = " + bad_image_count);
    446       }
    447       return 4;
    448     }
    449   }
    450   return 3;
    451 }
    452 
    453 void progressBar()
    454 {
    455   progress_bar++;
    456   print(".");
    457   if (progress_bar >63)
    458   {
    459     progress_bar = 0;
    460     percentage = 0.5 + 100 * pixel_count/(0.001 + tft_width * tft_height);
    461     percent(percentage);
    462   }
    463 }
    464 
    465 void percent(float percentage)
    466 {
    467   if (percentage > 100) percentage = 100;
    468   println(" [ " + (int)percentage + "% ]");
    469   textAlign(LEFT);
    470   textSize(16);
    471   noStroke();
    472   fill(bgcolor2);
    473   rect(10, height - 25, 70, 20);
    474   fill(0);
    475   text(" [ " + (int)percentage + "% ]", 10, height-8);
    476 }
    477 
    478 boolean fadedImage()
    479 {
    480   int opacity = frameCount - drawLoopCount;  // So we get increasing fade
    481   if (fade)
    482   {
    483     tint(255, opacity);
    484     //image(tft_img, x_offset, y_offset);
    485     noStroke();
    486     fill(50, 50, 50, opacity);
    487     rect( (width - tft_width)/2, y_offset, tft_width, tft_height);
    488     delay(10);
    489   }
    490   if (opacity > 50)       // End fade after 50 cycles
    491   {
    492     return true;
    493   }
    494   return false;
    495 }
    496 
    497 void drawButton(color buttonColor)
    498 {
    499   stroke(0);
    500   fill(buttonColor);
    501   rect(500 - 100, 540 - 26, 80, 24);
    502   textAlign(CENTER);
    503   textSize(20);
    504   fill(0);
    505   if (running) text(" Pause ", 500 - 60, height-7);
    506   else text(" Run ", 500 - 60, height-7);
    507 }
    508 
    509 void buttonClicked()
    510 {
    511   mouseClick = false;
    512   if (running) {
    513     running = false;
    514     drawButton(buttonStopped);
    515     System.err.println("");
    516     System.err.println("Stopped - click 'Run' button: ");
    517     //noStroke();
    518     //fill(50);
    519     //rect( (width - tft_width)/2, y_offset, tft_width, tft_height);
    520     beginTime = millis() + 500;
    521     dimmed = false;
    522     state = 4;
    523   } else {
    524     running = true;
    525     drawButton(buttonRunning);
    526   }
    527 }
    528 
    529 void mousePressed() {
    530   if (mouseX > (500 - 100) && mouseX < (500 - 20) && mouseY > (540 - 26) && mouseY < (540 - 2)) {
    531     mouseClick = true;
    532   }
    533 }
    534 
    535 */