Skip to content

Commit 54ba882

Browse files
Merge pull request #2419 from arduino/pedromsousalima/giga/display-usb
[PXCT-323] Display Shield- Image from USB/SD card on GIGA Display Shield
2 parents 8525dd0 + f29cdfd commit 54ba882

File tree

5 files changed

+278
-0
lines changed

5 files changed

+278
-0
lines changed
Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
---
2+
title: GIGA Display Shield Display Image From USB
3+
description: 'Learn to display RAW Images on the GIGA Display from USB Storage.'
4+
author: Pedro Sousa Lima
5+
tags: [Display, USB]
6+
---
7+
8+
## Introduction
9+
10+
This guide explores how to read and display raw RGB565 images from a USB drive onto an Arduino GIGA Display Shield. Making it possible for you to list files from a USB drive, select an image, and render it.
11+
12+
![Display with image from USB](assets/image-example.jpg)
13+
14+
## Hardware Requirements
15+
16+
- [Arduino GIGA R1 WiFi](https://store.arduino.cc/products/giga-r1-wifi)
17+
- [Arduino GIGA Display Shield](https://store.arduino.cc/products/giga-display-shield)
18+
- USB mass storage device (formatted with FAT32)
19+
20+
## Software Requirements
21+
22+
- **USBHostMbed5** (for USB host functionality)
23+
- **FATFileSystem** (to read files from USB)
24+
- **ArduinoH7Video**(Included in the core) and [**ArduinoGraphics**](https://docs.arduino.cc/libraries/arduinographics/) libraries (for handling display rendering)
25+
26+
## Features
27+
28+
- Lists all files in the `/usb/` directory.
29+
- Allows users to select an image via the Serial Monitor.
30+
- Automatically reads and displays an **800x480 RGB565** image.
31+
- Clears the display upon request.
32+
33+
## Preparing Your Image
34+
35+
To ensure your image is in the correct format (800x480, 16-bit RGB565), you can use an online converter such as the [**LVGL Image Converter**](https://lvgl.io/tools/imageconverter). Select **RGB565** as the output format and set the resolution to 800x480 before saving the file to your USB drive. Make sure the USB drive is formatted for FAT32.
36+
For testing we used the image:
37+
![Unprepared test image](assets/lupe800x480.jpg)
38+
39+
For image conversion we selected the following settings:
40+
![Image settings](assets/lvgl-converter-settings.png)
41+
42+
## Code Breakdown
43+
44+
### Setting Up USB Host and Display
45+
46+
With the following code the USB host is initialized, enabling support for mass storage devices. The display is also set up using the `Arduino_H7_Video` class.
47+
48+
```arduino
49+
USBHostMSD msd;
50+
mbed::FATFileSystem usb("usb");
51+
Arduino_H7_Video Display(800, 480, GigaDisplayShield);
52+
```
53+
54+
### Listing Files on USB Storage
55+
56+
Once mounted, the USB drive is scanned for available files. Again, make sure the USB is in FAT32 format:
57+
58+
```arduino
59+
void listRootDirectory() {
60+
fileCount = 0;
61+
DIR* dir = opendir("/usb/");
62+
if (!dir) return;
63+
while (true) {
64+
struct dirent* entry = readdir(dir);
65+
if (!entry || fileCount >= MAX_FILES) break;
66+
strncpy(fileNames[fileCount], entry->d_name, MAX_NAME_LEN - 1);
67+
fileCount++;
68+
}
69+
closedir(dir);
70+
}
71+
```
72+
73+
### Selecting an Image via Serial Input
74+
75+
Now by entering a file number in the Serial Monitor an image is selected and will be displayed on the GIGA Display Shield.
76+
77+
```arduino
78+
void handleUserInput() {
79+
if (Serial.available() > 0) {
80+
int sel = Serial.parseInt();
81+
if (sel >= 1 && sel <= fileCount) {
82+
char path[256];
83+
snprintf(path, sizeof(path), "/usb/%s", fileNames[sel - 1]);
84+
displayRawRowByRow(path);
85+
}
86+
}
87+
}
88+
```
89+
![File list](assets/file-list.png)
90+
91+
### Reading and Displaying RAW Images
92+
93+
The file is read row-by-row (800 pixels per row, 2 bytes per pixel). If the entire image (800x480 pixels) were loaded into memory at once, it would require approximately 768 KB of RAM (800 \* 480 \* 2 bytes). However, by processing only one row at a time (800 * 2 bytes), the memory usage is reduced to just `1.6 KB`, making this approach much more efficient and feasible for devices with restricted memory (like the GIGA). This approach minimizes memory usage by processing smaller chunks of the image at one time, avoiding large allocations that may exceed available RAM (512 KB for the GIGA).
94+
We use, `fopen()` and `fclose()` are used for file management. `fopen()` allows us to open the image file and read data as needed, rather than loading everything at once. `fclose()` ensures that the file is properly closed after reading, freeing up system resources and preventing potential memory leaks.
95+
96+
97+
```arduino
98+
bool displayRawRowByRow(const char* path) {
99+
FILE* f = fopen(path, "rb");
100+
if (!f) return false;
101+
102+
uint8_t* rowBuffer = (uint8_t*) malloc(IMG_WIDTH * 2);
103+
if (!rowBuffer) {
104+
fclose(f);
105+
return false;
106+
}
107+
108+
Display.beginDraw();
109+
for (int y = 0; y < IMG_HEIGHT; y++) {
110+
fread(rowBuffer, 1, IMG_WIDTH * 2, f);
111+
Image rowImage(ENCODING_RGB16, rowBuffer, IMG_WIDTH, 1);
112+
Display.image(rowImage, 0, y);
113+
}
114+
Display.endDraw();
115+
116+
free(rowBuffer);
117+
fclose(f);
118+
return true;
119+
}
120+
```
121+
122+
### Clearing the Display
123+
124+
Users can reset the display by entering `clear` in the Serial Monitor.
125+
This simply fills all pixels with black.
126+
127+
```arduino
128+
void forceScreenClear() {
129+
Display.beginDraw();
130+
Display.fill(0x0000); // Fill with black
131+
Display.endDraw();
132+
}
133+
```
134+
135+
### Full Code
136+
137+
```arduino
138+
#include <Arduino_USBHostMbed5.h>
139+
#include <DigitalOut.h>
140+
#include <FATFileSystem.h>
141+
#include <Arduino_H7_Video.h>
142+
#include <ArduinoGraphics.h>
143+
144+
USBHostMSD msd;
145+
mbed::FATFileSystem usb("usb");
146+
147+
const int USB_HOST_ENABLE_PIN = PA_15;
148+
const int MAX_FILES = 50;
149+
const int MAX_NAME_LEN = 128;
150+
char fileNames[MAX_FILES][MAX_NAME_LEN];
151+
int fileCount = 0;
152+
const int MAX_CONNECTION_ATTEMPTS = 10;
153+
154+
Arduino_H7_Video Display(800, 480, GigaDisplayShield);
155+
const int IMG_WIDTH = 800;
156+
const int IMG_HEIGHT = 480;
157+
const uint32_t EXPECTED_FILE_SIZE = IMG_WIDTH * IMG_HEIGHT * 2;
158+
159+
uint8_t rowBuffer[IMG_WIDTH * 2]; // Static buffer
160+
161+
void setup() {
162+
Serial.begin(115200);
163+
pinMode(USB_HOST_ENABLE_PIN, OUTPUT);
164+
digitalWrite(USB_HOST_ENABLE_PIN, HIGH);
165+
while (!Serial) {}
166+
delay(1500);
167+
168+
Serial.println("=== USB File List ===");
169+
Display.begin();
170+
forceScreenClear();
171+
172+
if (!initUSBHost() || !mountUSB()) return;
173+
listRootDirectory();
174+
if (fileCount == 0) return;
175+
176+
printFileList();
177+
Serial.println("Select a file # to open as 800x480 .bin, or type 'clear' to reset the screen:");
178+
}
179+
180+
void loop() {
181+
if (fileCount > 0) handleUserInput();
182+
}
183+
184+
bool initUSBHost() {
185+
for (int i = 0; i < MAX_CONNECTION_ATTEMPTS; i++) {
186+
if (msd.connect()) {
187+
Serial.println("USB mass storage device connected!");
188+
return true;
189+
}
190+
Serial.println("USB device not detected, retrying...");
191+
delay(1000);
192+
}
193+
return false;
194+
}
195+
196+
bool mountUSB() {
197+
Serial.print("Mounting USB device... ");
198+
if (usb.mount(&msd)) {
199+
Serial.println("Failed to mount USB.");
200+
return false;
201+
}
202+
Serial.println("done.");
203+
return true;
204+
}
205+
206+
void listRootDirectory() {
207+
fileCount = 0;
208+
DIR* dir = opendir("/usb/");
209+
if (!dir) return;
210+
while (fileCount < MAX_FILES) {
211+
struct dirent* entry = readdir(dir);
212+
if (!entry) break;
213+
strncpy(fileNames[fileCount], entry->d_name, MAX_NAME_LEN - 1);
214+
fileNames[fileCount][MAX_NAME_LEN - 1] = '\0';
215+
fileCount++;
216+
}
217+
closedir(dir);
218+
}
219+
220+
void printFileList() {
221+
Serial.print("Found "); Serial.print(fileCount); Serial.println(" file(s) in /usb/:");
222+
for (int i = 0; i < fileCount; i++) {
223+
Serial.print(i + 1); Serial.print(") "); Serial.println(fileNames[i]);
224+
}
225+
}
226+
227+
void handleUserInput() {
228+
if (Serial.available() > 0) {
229+
String input = Serial.readStringUntil('\n');
230+
input.trim();
231+
232+
if (input.equalsIgnoreCase("clear")) {
233+
forceScreenClear();
234+
return;
235+
}
236+
237+
int sel = input.toInt();
238+
if (sel < 1 || sel > fileCount) return;
239+
240+
Serial.print("Displaying: /usb/");
241+
Serial.println(fileNames[sel - 1]);
242+
243+
displayRawRowByRow(fileNames[sel - 1]);
244+
}
245+
}
246+
247+
bool displayRawRowByRow(const char* fileName) {
248+
String path = "/usb/" + String(fileName);
249+
FILE* f = fopen(path.c_str(), "rb");
250+
if (!f) return false;
251+
252+
forceScreenClear();
253+
Display.beginDraw();
254+
255+
for (int y = 0; y < IMG_HEIGHT; y++) {
256+
if (fread(rowBuffer, 1, IMG_WIDTH * 2, f) != IMG_WIDTH * 2) break;
257+
Image rowImage(ENCODING_RGB16, rowBuffer, IMG_WIDTH, 1);
258+
Display.image(rowImage, 0, y);
259+
}
260+
261+
Display.endDraw();
262+
fclose(f);
263+
Serial.println("Image displayed successfully!");
264+
return true;
265+
}
266+
267+
void forceScreenClear() {
268+
Display.beginDraw();
269+
Display.fill(0x0000);
270+
Display.endDraw();
271+
}
272+
```
273+
274+
### Conclusion
275+
276+
This project demonstrates how to read and display **16-bit RGB565 images** from a USB drive onto the Arduino GIGA Display. The row-by-row approach optimizes memory usage while ensuring smooth rendering.
277+
278+

0 commit comments

Comments
 (0)