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 000000000..0cb13dc89 Binary files /dev/null and b/Quad_Color_eInk_Demos/Arduino_Quad_Color_ImageReader/blinka.bmp differ diff --git a/Quad_Color_eInk_Demos/Arduino_Quad_ThinkInk/.uno.test.only b/Quad_Color_eInk_Demos/Arduino_Quad_ThinkInk/.uno.test.only new file mode 100644 index 000000000..e69de29bb diff --git a/Quad_Color_eInk_Demos/Arduino_Quad_ThinkInk/Arduino_Quad_ThinkInk.ino b/Quad_Color_eInk_Demos/Arduino_Quad_ThinkInk/Arduino_Quad_ThinkInk.ino new file mode 100644 index 000000000..13a0417d5 --- /dev/null +++ b/Quad_Color_eInk_Demos/Arduino_Quad_ThinkInk/Arduino_Quad_ThinkInk.ino @@ -0,0 +1,116 @@ +// SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +/*************************************************** + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries. + MIT license, all text above must be included in any redistribution + ****************************************************/ + +#include "Adafruit_ThinkInk.h" + +#ifdef ARDUINO_ADAFRUIT_FEATHER_RP2040_THINKINK // detects if compiling for + // Feather RP2040 ThinkInk +#define EPD_DC PIN_EPD_DC // ThinkInk 24-pin connector DC +#define EPD_CS PIN_EPD_CS // ThinkInk 24-pin connector CS +#define EPD_BUSY PIN_EPD_BUSY // ThinkInk 24-pin connector Busy +#define SRAM_CS -1 // use onboard RAM +#define EPD_RESET PIN_EPD_RESET // ThinkInk 24-pin connector Reset +#define EPD_SPI &SPI1 // secondary SPI for ThinkInk +#else +#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 +#endif + +// 2.13" Quadcolor EPD with JD79661 chipset +ThinkInk_213_Quadcolor_AJHE5 display(EPD_DC, EPD_RESET, EPD_CS, SRAM_CS, EPD_BUSY, + EPD_SPI); + +void setup() { + Serial.begin(115200); + while (!Serial) { + delay(10); + } + Serial.println("Adafruit EPD full update test in red/yellow/black/white"); + display.begin(THINKINK_QUADCOLOR); +} + +void loop() { + Serial.println("Banner demo"); + display.clearBuffer(); + display.setTextSize(3); + display.setCursor((display.width() - 144) / 2, (display.height() - 24) / 2); + String text = "QuadColor"; + uint16_t colors[] = {EPD_BLACK, EPD_RED, EPD_YELLOW}; + + for (int i = 0; i < text.length(); i++) { + // Change color for every character (0: BLACK, 1: RED, 2: YELLOW, 3: BLACK, etc.) + display.setTextColor(colors[i % 3]); + display.print(text.charAt(i)); + } + display.display(); + + delay(15000); + + Serial.println("Color quadrant demo"); + display.clearBuffer(); + // Top-left quadrant - EPD_BLACK + display.fillRect(0, 0, display.width() / 2, display.height() / 2, EPD_BLACK); + // Top-right quadrant - EPD_RED + display.fillRect(display.width() / 2, 0, display.width() / 2, display.height() / 2, EPD_RED); + // Bottom-left quadrant - EPD_YELLOW + display.fillRect(0, display.height() / 2, display.width() / 2, display.height() / 2, EPD_YELLOW); + // Bottom-right quadrant - assume you have a 4th color like EPD_WHITE or another color + display.fillRect(display.width() / 2, display.height() / 2, display.width() / 2, display.height() / 2, EPD_WHITE); + + display.display(); + + delay(15000); + + Serial.println("Text demo"); + // large block of text + display.clearBuffer(); + display.setTextSize(1); + testdrawtext( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur " + "adipiscing ante sed nibh tincidunt feugiat. Maecenas enim massa, " + "fringilla sed malesuada et, malesuada sit amet turpis. Sed porttitor " + "neque ut ante pretium vitae malesuada nunc bibendum. Nullam aliquet " + "ultrices massa eu hendrerit. Ut sed nisi lorem. In vestibulum purus a " + "tortor imperdiet posuere. ", + EPD_BLACK); + display.display(); + + delay(15000); + + display.clearBuffer(); + for (int16_t i = 0; i < display.width(); i += 4) { + display.drawLine(0, 0, i, display.height() - 1, EPD_BLACK); + } + for (int16_t i = 0; i < display.height(); i += 4) { + display.drawLine(display.width() - 1, 0, 0, i, EPD_RED); + } + for (int16_t i = 0; i < display.width(); i += 4) { + display.drawLine(display.width()/2, display.height()-1, i, 0, + EPD_YELLOW); + } + + display.display(); + + delay(15000); +} + +void testdrawtext(const char *text, uint16_t color) { + display.setCursor(0, 0); + display.setTextColor(color); + display.setTextWrap(true); + display.print(text); +} diff --git a/Quad_Color_eInk_Demos/Blinka_EPD_Demo/code.py b/Quad_Color_eInk_Demos/Blinka_EPD_Demo/code.py new file mode 100644 index 000000000..458fb4c2c --- /dev/null +++ b/Quad_Color_eInk_Demos/Blinka_EPD_Demo/code.py @@ -0,0 +1,79 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT +"""Blinka EPD Demo for the Quad Color eInk""" +import board +import digitalio +from PIL import Image, ImageDraw, ImageFont + +from adafruit_epd.epd import Adafruit_EPD +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 + +display = Adafruit_JD79661(122, 250, spi, + cs_pin=ecs, dc_pin=dc, sramcs_pin=srcs, + rst_pin=rst, busy_pin=busy) + +display.rotation = 3 +width = display.width +height = display.height +image = Image.new("RGB", (width, height)) + +WHITE = (0xFF, 0xFF, 0xFF) +YELLOW = (0xFF, 0xFF, 0x00) +RED = (0xFF, 0x00, 0x00) +BLACK = (0x00, 0x00, 0x00) + +# clear the buffer +display.fill(Adafruit_EPD.WHITE) + +# Get drawing object to draw on image. +draw = ImageDraw.Draw(image) +# empty it +draw.rectangle((0, 0, width, height), fill=WHITE) + +# Draw an outline box +draw.rectangle((1, 1, width - 2, height - 2), outline=BLACK, fill=WHITE) + +# Draw some shapes. +# First define some constants to allow easy resizing of shapes. +padding = 5 +shape_width = 30 +top = padding +bottom = height - padding +# Move left to right keeping track of the current x position for drawing shapes. +x = padding +# Draw an ellipse. +draw.ellipse((x, top, x + shape_width, bottom), outline=YELLOW, fill=WHITE) +x += shape_width + padding +# Draw a rectangle. +draw.rectangle((x, top, x + shape_width, bottom), outline=RED, fill=BLACK) +x += shape_width + padding +# Draw a triangle. +draw.polygon( + [(x, bottom), (x + shape_width / 2, top), (x + shape_width, bottom)], + outline=BLACK, + fill=RED, +) +x += shape_width + padding +# Draw an X. +draw.line((x, bottom, x + shape_width, top), fill=YELLOW) +draw.line((x, top, x + shape_width, bottom), fill=YELLOW) +x += shape_width + padding + +# Load default font. +font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 20) + +draw.text((x, top), "Hello", font=font, fill=YELLOW) +draw.text((x, top + 20), "World!", font=font, fill=YELLOW) + +# Display image. +display.image(image) + +display.display() diff --git a/Quad_Color_eInk_Demos/Blinka_EPD_Pillow_Image_Demo/blinka.png b/Quad_Color_eInk_Demos/Blinka_EPD_Pillow_Image_Demo/blinka.png new file mode 100644 index 000000000..1482c9dd8 Binary files /dev/null and b/Quad_Color_eInk_Demos/Blinka_EPD_Pillow_Image_Demo/blinka.png differ 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()