From 53dce06f3562cf9512483cea0c1470d308849c09 Mon Sep 17 00:00:00 2001 From: Liz Date: Tue, 5 Aug 2025 19:56:35 -0400 Subject: [PATCH] quad color eInk demos --- .../.uno.test.only | 0 .../Arduino_Quad_Color_ImageReader.ino | 117 ++++++++++++++++++ .../Arduino_Quad_Color_ImageReader/blinka.bmp | Bin 0 -> 91800 bytes .../Arduino_Quad_ThinkInk/.uno.test.only | 0 .../Arduino_Quad_ThinkInk.ino | 116 +++++++++++++++++ Quad_Color_eInk_Demos/Blinka_EPD_Demo/code.py | 79 ++++++++++++ .../Blinka_EPD_Pillow_Image_Demo/blinka.png | Bin 0 -> 8057 bytes .../Blinka_EPD_Pillow_Image_Demo/code.py | 92 ++++++++++++++ 8 files changed, 404 insertions(+) create mode 100644 Quad_Color_eInk_Demos/Arduino_Quad_Color_ImageReader/.uno.test.only create mode 100644 Quad_Color_eInk_Demos/Arduino_Quad_Color_ImageReader/Arduino_Quad_Color_ImageReader.ino create mode 100644 Quad_Color_eInk_Demos/Arduino_Quad_Color_ImageReader/blinka.bmp create mode 100644 Quad_Color_eInk_Demos/Arduino_Quad_ThinkInk/.uno.test.only create mode 100644 Quad_Color_eInk_Demos/Arduino_Quad_ThinkInk/Arduino_Quad_ThinkInk.ino create mode 100644 Quad_Color_eInk_Demos/Blinka_EPD_Demo/code.py create mode 100644 Quad_Color_eInk_Demos/Blinka_EPD_Pillow_Image_Demo/blinka.png create mode 100644 Quad_Color_eInk_Demos/Blinka_EPD_Pillow_Image_Demo/code.py diff --git a/Quad_Color_eInk_Demos/Arduino_Quad_Color_ImageReader/.uno.test.only b/Quad_Color_eInk_Demos/Arduino_Quad_Color_ImageReader/.uno.test.only new file mode 100644 index 000000000..e69de29bb diff --git a/Quad_Color_eInk_Demos/Arduino_Quad_Color_ImageReader/Arduino_Quad_Color_ImageReader.ino b/Quad_Color_eInk_Demos/Arduino_Quad_Color_ImageReader/Arduino_Quad_Color_ImageReader.ino new file mode 100644 index 000000000..dccba00ed --- /dev/null +++ b/Quad_Color_eInk_Demos/Arduino_Quad_Color_ImageReader/Arduino_Quad_Color_ImageReader.ino @@ -0,0 +1,117 @@ +// SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +// Adafruit_ImageReader test for Adafruit E-Ink Breakouts. +// Demonstrates loading images from SD card or flash memory to the screen, +// to RAM, and how to query image file dimensions. +// Requires BMP file in root directory of QSPI Flash: +// blinka.bmp. + +#include // Core graphics library +#include "Adafruit_ThinkInk.h" +#include // SD card & FAT filesystem library +#include // SPI / QSPI flash library +#include // Image-reading functions + +// Comment out the next line to load from SPI/QSPI flash instead of SD card: +#define USE_SD_CARD + +#define EPD_DC 10 +#define EPD_CS 9 +#define EPD_BUSY 7 // can set to -1 to not use a pin (will wait a fixed delay) +#define SRAM_CS 6 +#define EPD_RESET 8 // can set to -1 and share with microcontroller Reset! +#define EPD_SPI &SPI // primary SPI +#define SD_CS 5 // SD card chip select + +// 2.13" Quadcolor EPD with JD79661 chipset +ThinkInk_213_Quadcolor_AJHE5 display(EPD_DC, EPD_RESET, EPD_CS, SRAM_CS, EPD_BUSY, + EPD_SPI); + +#if defined(USE_SD_CARD) + SdFat SD; // SD card filesystem + Adafruit_ImageReader_EPD reader(SD); // Image-reader object, pass in SD filesys +#else + +// SPI or QSPI flash filesystem (i.e. CIRCUITPY drive) + #if defined(__SAMD51__) || defined(NRF52840_XXAA) + Adafruit_FlashTransport_QSPI flashTransport(PIN_QSPI_SCK, PIN_QSPI_CS, + PIN_QSPI_IO0, PIN_QSPI_IO1, PIN_QSPI_IO2, PIN_QSPI_IO3); + #else + #if (SPI_INTERFACES_COUNT == 1 || defined(ADAFRUIT_CIRCUITPLAYGROUND_M0)) + Adafruit_FlashTransport_SPI flashTransport(SS, &SPI); + #else + Adafruit_FlashTransport_SPI flashTransport(SS1, &SPI1); + #endif + #endif + Adafruit_SPIFlash flash(&flashTransport); + FatVolume filesys; + Adafruit_ImageReader_EPD reader(filesys); // Image-reader, pass in flash filesys +#endif + +Adafruit_Image_EPD img; // An image loaded into RAM +int32_t width = 0, // BMP image dimensions + height = 0; + +void setup(void) { + + ImageReturnCode stat; // Status from image-reading functions + + Serial.begin(115200); + while(!Serial); // Wait for Serial Monitor before continuing + + display.begin(); + display.setRotation(3); + + // The Adafruit_ImageReader constructor call (above, before setup()) + // accepts an uninitialized SdFat or FatVolume object. This MUST + // BE INITIALIZED before using any of the image reader functions! + Serial.print(F("Initializing filesystem...")); + // SPI or QSPI flash requires two steps, one to access the bare flash + // memory itself, then the second to access the filesystem within... +#if defined(USE_SD_CARD) + // SD card is pretty straightforward, a single call... + if(!SD.begin(SD_CS, SD_SCK_MHZ(10))) { // Breakouts require 10 MHz limit due to longer wires + Serial.println(F("SD begin() failed")); + for(;;); // Fatal error, do not continue + } +#else + // SPI or QSPI flash requires two steps, one to access the bare flash + // memory itself, then the second to access the filesystem within... + if(!flash.begin()) { + Serial.println(F("flash begin() failed")); + for(;;); + } + if(!filesys.begin(&flash)) { + Serial.println(F("filesys begin() failed")); + for(;;); + } +#endif + Serial.println(F("OK!")); + + // Load full-screen BMP file 'blinka.bmp' at position (0,0) (top left). + // Notice the 'reader' object performs this, with 'epd' as an argument. + Serial.print(F("Loading blinka.bmp to canvas...")); + stat = reader.drawBMP((char *)"/blinka.bmp", display, 0, 0); + reader.printStatus(stat); // How'd we do? + display.display(); + + // Query the dimensions of image 'blinka.bmp' WITHOUT loading to screen: + Serial.print(F("Querying blinka.bmp image size...")); + stat = reader.bmpDimensions("blinka.bmp", &width, &height); + reader.printStatus(stat); // How'd we do? + if(stat == IMAGE_SUCCESS) { // If it worked, print image size... + Serial.print(F("Image dimensions: ")); + Serial.print(width); + Serial.write('x'); + Serial.println(height); + } + + delay(30 * 1000); // Pause 30 seconds before continuing because it's eInk + +} + +void loop() { + +} diff --git a/Quad_Color_eInk_Demos/Arduino_Quad_Color_ImageReader/blinka.bmp b/Quad_Color_eInk_Demos/Arduino_Quad_Color_ImageReader/blinka.bmp new file mode 100644 index 0000000000000000000000000000000000000000..0cb13dc89ae45a8a8382afd5718881c0c8b35b0e GIT binary patch literal 91800 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 z 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 literal 0 HcmV?d00001 diff --git a/Quad_Color_eInk_Demos/Blinka_EPD_Pillow_Image_Demo/code.py b/Quad_Color_eInk_Demos/Blinka_EPD_Pillow_Image_Demo/code.py new file mode 100644 index 000000000..10e6f74cb --- /dev/null +++ b/Quad_Color_eInk_Demos/Blinka_EPD_Pillow_Image_Demo/code.py @@ -0,0 +1,92 @@ +# SPDX-FileCopyrightText: 2019 Melissa LeBlanc-Williams for Adafruit Industries +# SPDX-License-Identifier: MIT +""" +Image resizing and drawing using the Pillow Library for Quad Color eInk +""" +import board +import digitalio +from PIL import Image, ImageEnhance +from adafruit_epd.jd79661 import Adafruit_JD79661 + +# create the spi device and pins we will need +spi = board.SPI() +ecs = digitalio.DigitalInOut(board.CE0) +dc = digitalio.DigitalInOut(board.D25) +srcs = None +rst = digitalio.DigitalInOut(board.D27) # can be None to not use this pin +busy = digitalio.DigitalInOut(board.D17) # can be None to not use this pin + +# give them all to our driver +display = Adafruit_JD79661(122, 250, # 2.13" Quad-color display + spi, + cs_pin=ecs, + dc_pin=dc, + sramcs_pin=srcs, + rst_pin=rst, + busy_pin=busy, +) +display.rotation = 3 + +image = Image.open("blinka.png") + +# Scale the image to the smaller screen dimension +image_ratio = image.width / image.height +screen_ratio = display.width / display.height +if screen_ratio < image_ratio: + scaled_width = image.width * display.height // image.height + scaled_height = display.height +else: + scaled_width = display.width + scaled_height = image.height * display.width // image.width +image = image.resize((scaled_width, scaled_height), Image.BICUBIC) + +# Crop and center the image +x = scaled_width // 2 - display.width // 2 +y = scaled_height // 2 - display.height // 2 +image = image.crop((x, y, x + display.width, y + display.height)).convert("RGB") + +quad_colors = [ + (0, 0, 0), # Black + (255, 255, 255), # White + (255, 0, 0), # Red + (255, 255, 0), # Yellow +] +palette_image = Image.new('P', (1, 1)) + +# Create palette data - PIL expects 768 values (256 colors * 3 channels) +palette_data = [] +for color in quad_colors: + palette_data.extend(color) +# Fill remaining palette entries with black +for i in range(4, 256): + palette_data.extend([0, 0, 0]) + +palette_image.putpalette(palette_data) + +enhancer = ImageEnhance.Color(image) +image = enhancer.enhance(1.5) + +temp_image = image.quantize(palette=palette_image, dither=Image.Dither.FLOYDSTEINBERG) + +pixels = temp_image.load() +width, height = temp_image.size + +final_palette = Image.new('P', (1, 1)) +final_palette.putpalette(palette_data) +final_image = Image.new('P', (width, height)) +final_pixels = final_image.load() + +# Copy pixels, ensuring they use indices 0-3 +for y in range(height): + for x in range(width): + # Clamp pixel values to 0-3 range + final_pixels[x, y] = min(pixels[x, y], 3) + +final_image.putpalette(palette_data) + +# Convert back to RGB for display +image = final_image.convert('RGB') + +# Display image. +display.image(image) +display.display()