|
| 1 | +/* |
| 2 | + Inkplate10 Image Uploader Example |
| 3 | + Compatible with Soldered Inkplate 10 |
| 4 | +
|
| 5 | + Getting Started: |
| 6 | + For setup and documentation, visit: https://inkplate.readthedocs.io/en/latest/ |
| 7 | +
|
| 8 | + Overview: |
| 9 | + This example demonstrates how to upload an image to a webapp hosted by Inkplate 10 |
| 10 | + and display it on the e‐ink display. Image will be automatically resized. |
| 11 | +*/ |
| 12 | + |
| 13 | +// Ensure correct board is selected |
| 14 | +#if !defined(ARDUINO_INKPLATE10) && !defined(ARDUINO_INKPLATE10V2) |
| 15 | +#error "Wrong board selection for this example, please select e-radionica Inkplate10 or Soldered Inkplate10 in the boards menu." |
| 16 | +#endif |
| 17 | + |
| 18 | +#include <WiFi.h> // WiFi support |
| 19 | +#include <WebServer.h> // Built-in web server |
| 20 | +#include <HTTPClient.h> // HTTP client for image upload |
| 21 | +#include <Inkplate.h> // Inkplate display library |
| 22 | +#include "src/html.h" // HTML page definitions (INDEX_HTML) |
| 23 | + |
| 24 | +// Wi-Fi Access Point credentials |
| 25 | +const char* ap_ssid = "InkplateImage"; // SSID for AP mode |
| 26 | +const char* ap_password = "inkplate"; // Password (min 8 chars) |
| 27 | + |
| 28 | +// Initialize Inkplate (3-bit grayscale mode by default) |
| 29 | +Inkplate display(INKPLATE_3BIT); |
| 30 | + |
| 31 | +// Create a web server object on port 80 |
| 32 | +WebServer server(80); |
| 33 | + |
| 34 | +// Buffer to hold the latest uploaded image in RAM |
| 35 | +uint8_t* imageBuf = nullptr; // pointer to JPEG data |
| 36 | +size_t imageLen = 0; // actual bytes received |
| 37 | +size_t imageBufCapacity = 0; // allocated buffer size |
| 38 | + |
| 39 | +// Flag to indicate a completed upload |
| 40 | +bool imageUploaded = false; |
| 41 | + |
| 42 | +// Serve the main HTML page when root is accessed |
| 43 | +void handleIndex() { |
| 44 | + server.send_P(200, "text/html", INDEX_HTML); |
| 45 | +} |
| 46 | + |
| 47 | +// Handle file upload in three stages: START, WRITE, END |
| 48 | +void handleUpload() { |
| 49 | + HTTPUpload& u = server.upload(); |
| 50 | + |
| 51 | + if (u.status == UPLOAD_FILE_START) { |
| 52 | + // Beginning of upload: allocate buffer based on Content-Length header |
| 53 | + imageLen = 0; |
| 54 | + if (imageBuf) { |
| 55 | + free(imageBuf); |
| 56 | + imageBuf = nullptr; |
| 57 | + } |
| 58 | + imageBufCapacity = server.header("Content-Length").toInt(); |
| 59 | + if (imageBufCapacity <= 0) { |
| 60 | + // Fallback capacity if header is missing or invalid (~1200×825 pixels) |
| 61 | + imageBufCapacity = 1200 * 825; |
| 62 | + } |
| 63 | + imageBuf = (uint8_t*) malloc(imageBufCapacity); |
| 64 | + Serial.printf("Upload start, buffer capacity = %u bytes\n", |
| 65 | + (unsigned)imageBufCapacity); |
| 66 | + } |
| 67 | + else if (u.status == UPLOAD_FILE_WRITE) { |
| 68 | + // Middle of upload: append received chunk into buffer |
| 69 | + if (imageBuf && (imageLen + u.currentSize <= imageBufCapacity)) { |
| 70 | + memcpy(imageBuf + imageLen, u.buf, u.currentSize); |
| 71 | + imageLen += u.currentSize; |
| 72 | + Serial.printf(" received %u bytes (total %u)\n", |
| 73 | + (unsigned)u.currentSize, (unsigned)imageLen); |
| 74 | + } |
| 75 | + } |
| 76 | + else if (u.status == UPLOAD_FILE_END) { |
| 77 | + // End of upload: mark flag to display the image |
| 78 | + Serial.printf("Upload complete, final size = %u bytes\n", |
| 79 | + (unsigned)imageLen); |
| 80 | + imageUploaded = true; |
| 81 | + } |
| 82 | +} |
| 83 | + |
| 84 | +// After upload ends, reload the page so the user sees the result |
| 85 | +void handleUploadComplete() { |
| 86 | + server.send(200, "text/html"); |
| 87 | +} |
| 88 | + |
| 89 | +// Show a preview page with the last uploaded image |
| 90 | +void handlePreview() { |
| 91 | + String html = "<html><body><h3>Preview:</h3>" |
| 92 | + "<img src=\"/image.jpg?ts=" + String(millis()) + |
| 93 | + "\" style=\"max-width:100%;\">" |
| 94 | + "</body></html>"; |
| 95 | + server.send(200, "text/html", html); |
| 96 | +} |
| 97 | + |
| 98 | +// Serve the JPEG image bytes directly from RAM |
| 99 | +void handleImage() { |
| 100 | + if (imageBuf && imageLen > 0) { |
| 101 | + // Send appropriate headers for JPEG |
| 102 | + server.sendHeader("Content-Type", "image/jpeg"); |
| 103 | + server.sendHeader("Content-Length", String(imageLen)); |
| 104 | + server.send(200, "image/jpeg", ""); // end headers |
| 105 | + |
| 106 | + // Write image data to client |
| 107 | + WiFiClient client = server.client(); |
| 108 | + size_t sent = client.write(imageBuf, imageLen); |
| 109 | + Serial.printf("Sent %u of %u bytes\n", sent, (unsigned)imageLen); |
| 110 | + } |
| 111 | + else { |
| 112 | + // No image available yet |
| 113 | + server.send(404, "text/plain", "No image uploaded"); |
| 114 | + } |
| 115 | +} |
| 116 | + |
| 117 | +// Display the buffered image on the e-ink display |
| 118 | +void showImageBuffer() { |
| 119 | + if (!imageBuf || imageLen == 0) return; |
| 120 | + |
| 121 | + display.clearDisplay(); // clear existing content |
| 122 | + display.setDisplayMode(INKPLATE_3BIT); // ensure correct mode |
| 123 | + // Draw JPEG from RAM: full-screen, no dithering |
| 124 | + display.drawJpegFromBuffer(imageBuf, imageLen, 0, 0, true, false); |
| 125 | + display.display(); // push to panel |
| 126 | +} |
| 127 | + |
| 128 | +// Helper: calculate scaled dimensions by the larger original side |
| 129 | +void getScaledByLargerSide(int origW, int origH, int maxW, int maxH, int &outW, int &outH) { |
| 130 | + float ratio = (origW >= origH) ? (float(maxW) / origW) : (float(maxH) / origH); |
| 131 | + outW = round(origW * ratio); |
| 132 | + outH = round(origH * ratio); |
| 133 | +} |
| 134 | + |
| 135 | +void setup() { |
| 136 | + Serial.begin(115200); // initialize serial for debug |
| 137 | + |
| 138 | + // Initialize the display |
| 139 | + display.begin(); |
| 140 | + display.clearDisplay(); |
| 141 | + |
| 142 | + // Start Wi-Fi in AP+STA mode |
| 143 | + WiFi.mode(WIFI_AP_STA); |
| 144 | + WiFi.softAP(ap_ssid, ap_password); |
| 145 | + IPAddress apIP = WiFi.softAPIP(); |
| 146 | + Serial.print("AP IP: "); Serial.println(apIP); |
| 147 | + |
| 148 | + // Show instructions on the e-ink screen |
| 149 | + display.setTextSize(4); |
| 150 | + display.setTextColor(BLACK); |
| 151 | + display.clearDisplay(); |
| 152 | + display.setCursor(10, 10); |
| 153 | + display.print("Welcome to the Inkplate Image Uploader example."); |
| 154 | + display.setTextSize(2); |
| 155 | + display.setCursor(10, 50); |
| 156 | + display.print("Connect to WiFi:"); |
| 157 | + display.setCursor(30, 70); |
| 158 | + display.print("SSID: " + String(ap_ssid)); |
| 159 | + display.setCursor(30, 90); |
| 160 | + display.print("Password: " + String(ap_password)); |
| 161 | + display.setCursor(10, 110); |
| 162 | + display.print("Then visit:"); |
| 163 | + display.setCursor(10, 130); |
| 164 | + display.print(apIP.toString()); |
| 165 | + display.display(); |
| 166 | + |
| 167 | + // Define web routes and handlers |
| 168 | + server.on("/", HTTP_GET, handleIndex); |
| 169 | + server.on("/upload", HTTP_POST, handleIndex, handleUpload); |
| 170 | + server.on("/preview", HTTP_GET, handlePreview); |
| 171 | + server.on("/image.jpg", HTTP_GET, handleImage); |
| 172 | + |
| 173 | + server.begin(); // start the HTTP server |
| 174 | + Serial.println("HTTP server started"); |
| 175 | +} |
| 176 | + |
| 177 | +void loop() { |
| 178 | + server.handleClient(); // handle incoming HTTP requests |
| 179 | + |
| 180 | + // If an image was uploaded, display it once |
| 181 | + if (imageUploaded) { |
| 182 | + imageUploaded = false; // reset the flag |
| 183 | + delay(100); // small delay to finish POST |
| 184 | + showImageBuffer(); // render image on e-ink |
| 185 | + } |
| 186 | +} |
0 commit comments