1+ /*
2+ Inkplate Lan Gallery Example
3+ Compatible with Soldered Inkplate 10
4+
5+ Getting started:
6+ For this project you will need: Inkplate 10, microSD card (formatted to FAT32!)
7+ and WiFi connection.
8+
9+ Step 1: Place the formatted microSD card in onboard slot on Inkplate 10
10+
11+ Step 2: Install `AsyncTCP` and `ESPAsyncWebServer` libraries, they are available directly from
12+ Arduino Library Manager.
13+
14+ Step 3: Modify the ssid, password and IMAGE_CHANGE_INTERVAL variables. SSID and password
15+ need to be the same as the network you are trying to connect, and INTERVAL_CHANGE_TIMER
16+ changes the duration between image changes (in miliseconds, default is 30000ms or 30s)
17+
18+ Step 4: Upload the code as usual
19+
20+ Step 5: Using another device connected to the same network open a browser and
21+ go to langallery.local
22+
23+ Step 6: Press the 'Choose File' button and select a picture that you want to upload, and
24+ after that press the 'Upload' button
25+
26+ Overview:
27+ This example demonstartes how to run a webapp, handle multiple users at once, upload files
28+ to onboard SD card and display uploaded images in random order on the e-ink display. Images
29+ will be automatically resized.
30+
31+ */
32+
33+ // Ensure corect board is selected
134#if !defined(ARDUINO_INKPLATE10) && !defined(ARDUINO_INKPLATE10V2)
235#error "Select 'Soldered Inkplate10' in the boards menu."
336#endif
437
5- #include " Inkplate.h"
38+ #include " Inkplate.h" // Inkplate library
639
40+ // Initialize Inkplate (3-bit grayscale mode)
741Inkplate display (INKPLATE_3BIT);
842
43+
44+ // WiFi network credentials
945const char * ssid = " YOUR_SSID" ;
1046const char * password = " YOUR_PASSWORD" ;
1147
12- #define IMAGE_CHANGE_INTERVAL 30000UL
48+ // Image rotation/change inteerval (milliseconds)
49+ #define IMAGE_CHANGE_INTERVAL 30000UL
50+
51+ // Linked list structure for storing image filepaths
52+ struct Node {
53+ char * path;
54+ int id;
55+ Node* next;
56+ };
1357
14- struct Node { char * path; int id; Node* next; };
15- Node* head = nullptr ;
16- int nodeCount = 0 ;
58+ Node* head = nullptr ; // Pointer to the first node in circular list
59+ int nodeCount = 0 ; // Total number of images found
1760
18- SemaphoreHandle_t sdMutex;
19- SdFile* currentUploadFile = nullptr ;
20- volatile bool uploadComplete = false ;
61+ SemaphoreHandle_t sdMutex; // Mutex for SD card access synchronization
62+ SdFile* currentUploadFile = nullptr ; // File object for current upload
63+ volatile bool uploadComplete = false ; // Flag to indicate upload completion
2164
65+ // Forward declaration
2266void setupWebServer ();
2367
68+ // Add a new image node to the linked list
2469void addNode (const char * p, int id) {
2570 Node* n = (Node*)malloc (sizeof (Node));
2671 if (!n) return ;
@@ -38,10 +83,13 @@ void addNode(const char* p, int id) {
3883 nodeCount++;
3984}
4085
86+ // Build a list of all image files found on the SD card
4187bool buildImageList () {
4288 Serial.println (" Building image list..." );
4389 if (!display.sdCardInit ()) {
44- Serial.println (" SD Card init failed!" );
90+ display.clearDisplay ();
91+ display.print (" SD Card init failed!" );
92+ display.display ();
4593 return false ;
4694 }
4795 SdFile root;
@@ -50,6 +98,7 @@ bool buildImageList() {
5098 return false ;
5199 }
52100
101+ // Free any existing list before rebuilding
53102 if (head) {
54103 Node* current = head;
55104 Node* first = head;
@@ -63,6 +112,7 @@ bool buildImageList() {
63112 }
64113 nodeCount = 0 ;
65114
115+ // Iterate through SD card root directory and find image files
66116 SdFile e;
67117 while (e.openNext (&root, O_RDONLY)) {
68118 char name[64 ];
@@ -80,6 +130,7 @@ bool buildImageList() {
80130 return nodeCount > 0 ;
81131}
82132
133+ // Pick a random image node from the list
83134Node* pickRandomNode () {
84135 if (!head) return nullptr ;
85136 int id = random (nodeCount);
@@ -88,6 +139,7 @@ Node* pickRandomNode() {
88139 return t;
89140}
90141
142+ // read BMP image dimensions directly from SD card
91143static bool readBmpSize_SdFat (const char * path, int &w, int &h) {
92144 w = h = 0 ;
93145 SdFile f;
@@ -107,6 +159,7 @@ static bool readBmpSize_SdFat(const char* path, int &w, int &h) {
107159 return true ;
108160}
109161
162+ // Read JPEG image dimensions directly from SD card
110163static bool readJpegSize_SdFat (const char * path, int &w, int &h) {
111164 w = h = 0 ;
112165 SdFile f;
@@ -135,6 +188,7 @@ static bool readJpegSize_SdFat(const char* path, int &w, int &h) {
135188 return true ;
136189 };
137190
191+ // Parse JPEG markers until SOF segment found
138192 while (true ) {
139193 uint8_t markerPrefix;
140194 do {
@@ -176,6 +230,7 @@ static bool readJpegSize_SdFat(const char* path, int &w, int &h) {
176230 return false ;
177231}
178232
233+ // Detect image type and get its width and height
179234static bool getImageWH_SdFat (const char * path, int &w, int &h) {
180235 const char *ext = strrchr (path, ' .' );
181236 if (ext) {
@@ -189,14 +244,21 @@ static bool getImageWH_SdFat(const char* path, int &w, int &h) {
189244 if (readJpegSize_SdFat (path, w, h)) return true ;
190245 }
191246 }
192- w = 800 ; h = 600 ;
247+ // Fallback default size if detection fails
248+ w = 800 ;
249+ h = 600 ;
193250 return false ;
194251}
195252
253+ // Display selected image file on the e-ink screen
196254void showImage (const char * path) {
197255 Serial.printf (" Displaying: %s\n " , path);
198256 display.clearDisplay ();
199- display.sdCardInit ();
257+ if (!display.sdCardInit ()){
258+ display.clearDisplay ();
259+ display.print (" SD Card Init() failed!" );
260+ display.display ();
261+ }
200262
201263 int imgW = 0 , imgH = 0 ;
202264 bool okSize = getImageWH_SdFat (path, imgW, imgH);
@@ -213,13 +275,14 @@ void showImage(const char* path) {
213275
214276 Serial.printf (" Draw at x=%d, y=%d (disp=%dx%d)\n " , x, y, dispW, dispH);
215277
278+ // Draw image fron SD card
216279 if (!display.drawImage (path, x, y, 3 )) {
217280 display.setTextSize (2 );
218281 display.setCursor (100 , 300 );
219282 display.println (" Image load failed!" );
220283 }
221284
222- // Overlay text (white on black background)
285+ // Overlay footer text (white on black background)
223286 const char * overlayText = " Inkplate LAN Gallery on langallery.local" ;
224287 display.setTextSize (1 );
225288
@@ -243,6 +306,7 @@ void showImage(const char* path) {
243306 display.display ();
244307}
245308
309+ // Start receiving file data for upload
246310void startFileUpload (const char * filename) {
247311 if (xSemaphoreTake (sdMutex, portMAX_DELAY)) {
248312 display.sdCardInit ();
@@ -257,6 +321,7 @@ void startFileUpload(const char* filename) {
257321 }
258322}
259323
324+ // Write incoming file data chunks to SD card
260325void writeFileData (uint8_t * data, size_t len) {
261326 if (xSemaphoreTake (sdMutex, portMAX_DELAY)) {
262327 if (currentUploadFile && currentUploadFile->isOpen ()) {
@@ -269,6 +334,7 @@ void writeFileData(uint8_t* data, size_t len) {
269334 }
270335}
271336
337+ // Finish file upload and close the file
272338void finishFileUpload () {
273339 if (xSemaphoreTake (sdMutex, portMAX_DELAY)) {
274340 if (currentUploadFile) {
@@ -288,6 +354,7 @@ void finishFileUpload() {
288354void setup () {
289355 Serial.begin (115200 );
290356 display.begin ();
357+ display.setTextColor (BLACK);
291358 randomSeed (analogRead (0 ));
292359 sdMutex = xSemaphoreCreateMutex ();
293360
@@ -297,15 +364,18 @@ void setup() {
297364 display.println (" Connecting Wi-Fi..." );
298365 display.display ();
299366
367+ // Connect to existing WiFi network
300368 WiFi.begin (ssid, password);
301369 while (WiFi.status () != WL_CONNECTED) {
302370 delay (500 );
303371 Serial.print (" ." );
304372 }
305373 Serial.printf (" \n Connected! IP: %s\n " , WiFi.localIP ().toString ().c_str ());
306374
375+ // Start local web server fro uploads
307376 setupWebServer ();
308377
378+ // Build image list and show a random one at startup
309379 if (buildImageList ()) {
310380 Node* n = pickRandomNode ();
311381 if (n) showImage (n->path );
@@ -318,7 +388,7 @@ void setup() {
318388 }
319389}
320390
321- unsigned long lastImageChange = 0 ;
391+ unsigned long lastImageChange = 0 ; // track last displayed image time
322392
323393void loop () {
324394 if (uploadComplete) {
@@ -339,6 +409,7 @@ void loop() {
339409 lastImageChange = millis ();
340410 }
341411
412+ // Automatically change image after the set interval
342413 if (millis () - lastImageChange >= IMAGE_CHANGE_INTERVAL) {
343414 if (xSemaphoreTake (sdMutex, portMAX_DELAY)) {
344415 if (buildImageList ()) {
@@ -350,5 +421,5 @@ void loop() {
350421 lastImageChange = millis ();
351422 }
352423
353- delay (10 );
424+ delay (10 ); // small delay for task scheduling
354425}
0 commit comments