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