From c62b8eff38f96650cd15fb13feedc2fe56fe1767 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Sun, 23 Nov 2025 15:16:25 +0100 Subject: [PATCH] debug scripts --- examples/test_hardware_display.py | 201 +++++++++++++++++++++++++++ examples/test_library_on_hardware.py | 175 +++++++++++++++++++++++ examples/test_library_render.py | 89 ++++++++++++ examples/test_simple_pattern.py | 126 +++++++++++++++++ hardware_config.json | 3 +- 5 files changed, 593 insertions(+), 1 deletion(-) create mode 100755 examples/test_hardware_display.py create mode 100755 examples/test_library_on_hardware.py create mode 100755 examples/test_library_render.py create mode 100755 examples/test_simple_pattern.py diff --git a/examples/test_hardware_display.py b/examples/test_hardware_display.py new file mode 100755 index 0000000..7839669 --- /dev/null +++ b/examples/test_hardware_display.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python3 +""" +Simple test to verify the hardware HAL can display images. +Creates a test pattern and displays it on the e-ink screen. +""" + +import sys +import asyncio +from pathlib import Path +from PIL import Image, ImageDraw, ImageFont + +# Add parent directory to path +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from dreader.hal_hardware import HardwareDisplayHAL + + +def create_test_image(width=1404, height=1872 ): + """Create a test pattern image.""" + print("Creating test image...") + + # Create white background + img = Image.new('RGBA', (width, height), color='white') + draw = ImageDraw.Draw(img) + + # Draw border + border_width = 10 + draw.rectangle( + [(border_width, border_width), + (width - border_width, height - border_width)], + outline='black', + width=border_width + ) + + # Draw title + try: + font_large = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 80) + font_medium = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 50) + except: + font_large = ImageFont.load_default() + font_medium = ImageFont.load_default() + + # Center text + title = "Hardware Display Test" + title_bbox = draw.textbbox((0, 0), title, font=font_large) + title_width = title_bbox[2] - title_bbox[0] + title_x = (width - title_width) // 2 + draw.text((title_x, 100), title, fill='black', font=font_large) + + # Instructions + instructions = [ + "If you can see this, the display is working!", + "", + "Test pattern includes:", + "• Border around the screen", + "• Centered text", + "• Diagonal lines", + "• Corner markers" + ] + + y = 250 + for line in instructions: + if line: + bbox = draw.textbbox((0, 0), line, font=font_medium) + line_width = bbox[2] - bbox[0] + x = (width - line_width) // 2 + draw.text((x, y), line, fill='black', font=font_medium) + y += 70 + + # Draw diagonal lines + draw.line([(50, 50), (width-50, height-50)], fill='black', width=3) + draw.line([(width-50, 50), (50, height-50)], fill='black', width=3) + + # Draw corner markers + marker_size = 100 + # Top-left + draw.rectangle([(20, 20), (20 + marker_size, 20 + marker_size)], + fill='black') + draw.text((30, 30), "TL", fill='white', font=font_medium) + + # Top-right + draw.rectangle([(width - 20 - marker_size, 20), + (width - 20, 20 + marker_size)], + fill='black') + draw.text((width - 100, 30), "TR", fill='white', font=font_medium) + + # Bottom-left + draw.rectangle([(20, height - 20 - marker_size), + (20 + marker_size, height - 20)], + fill='black') + draw.text((30, height - 100), "BL", fill='white', font=font_medium) + + # Bottom-right + draw.rectangle([(width - 20 - marker_size, height - 20 - marker_size), + (width - 20, height - 20)], + fill='black') + draw.text((width - 100, height - 100), "BR", fill='white', font=font_medium) + + print(f"Test image created: {width}x{height} RGBA") + return img + + +async def main(args): + """Test hardware display.""" + + print("=" * 60) + print("Hardware Display Test") + print("=" * 60) + print() + + # Get VCOM from args + vcom = args.vcom if hasattr(args, 'vcom') else -1.65 + print(f"Using VCOM: {vcom}V") + print() + + # Create test image + test_image = create_test_image(1872, 1404) + + # Save test image for reference + output_file = "test_pattern.png" + test_image.save(output_file) + print(f"Test pattern saved to: {output_file}") + print() + + # Initialize hardware HAL + print("Initializing hardware HAL...") + hal = HardwareDisplayHAL( + width=1872, + height=1404, + vcom=vcom, + config_file="hardware_config.json" + ) + + print("Initializing hardware components...") + await hal.initialize() + print("✓ Hardware initialized") + print() + + # Display the test image + print("Displaying test image on e-ink screen...") + print("(This may take a few seconds for the full refresh)") + print(f"Converting image from {test_image.mode} to grayscale for e-ink...") + + # Convert RGBA to grayscale (L mode) for e-ink display + if test_image.mode == 'RGBA': + # Convert RGBA to RGB first (flatten alpha) + rgb_image = Image.new('RGB', test_image.size, (255, 255, 255)) + rgb_image.paste(test_image, mask=test_image.split()[3]) # Use alpha as mask + test_image_gray = rgb_image.convert('L') + else: + test_image_gray = test_image.convert('L') + + print(f"Image converted to {test_image_gray.mode} mode") + await hal.show_image(test_image_gray) + print("✓ Image displayed!") + print() + + print("=" * 60) + print("SUCCESS!") + print("=" * 60) + print() + print("If you can see the test pattern on the screen, the") + print("hardware display is working correctly!") + print() + print("Check that you can see:") + print(" • Black border around the edges") + print(" • Title text centered at the top") + print(" • Diagonal lines crossing the screen") + print(" • Corner markers labeled TL, TR, BL, BR") + print() + print("Press Ctrl+C to exit") + print() + + # Keep running so user can see the image + try: + while True: + await asyncio.sleep(1) + except KeyboardInterrupt: + print("\nCleaning up...") + await hal.cleanup() + print("Done!") + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description='Test hardware display') + parser.add_argument('--vcom', type=float, default=-1.65, + help='VCOM voltage (check your display label, default: -1.65)') + + args = parser.parse_args() + + try: + asyncio.run(main(args)) + except KeyboardInterrupt: + print("\nTest interrupted") + except Exception as e: + print(f"\nERROR: {e}") + import traceback + traceback.print_exc() + sys.exit(1) diff --git a/examples/test_library_on_hardware.py b/examples/test_library_on_hardware.py new file mode 100755 index 0000000..222f16f --- /dev/null +++ b/examples/test_library_on_hardware.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python3 +""" +Test script to render the library view and display it on hardware. +Combines library rendering with hardware display testing. +""" + +import sys +import asyncio +from pathlib import Path + +# Add parent directory to path +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from dreader.library import LibraryManager +from dreader.hal_hardware import HardwareDisplayHAL + + +async def main(args): + """Test library rendering on hardware display.""" + + library_path = args.library_path + output_path = "library_render_test.png" + vcom = args.vcom + + print("=" * 60) + print("Library Rendering on Hardware Test") + print("=" * 60) + print(f"Library path: {library_path}") + print(f"VCOM: {vcom}V") + print(f"Output file: {output_path}") + print() + + # Step 1: Create library manager and scan + print("Step 1: Creating library manager...") + library = LibraryManager( + library_path=library_path, + page_size=(1872, 1404) + ) + + print("Scanning library...") + library.scan_library() + + print() + print(f"Found {len(library.books)} books:") + for i, book in enumerate(library.books[:10], 1): # Show first 10 + print(f" {i}. {book['title']} by {book['author']}") + if len(library.books) > 10: + print(f" ... and {len(library.books) - 10} more") + print() + + # Step 2: Create and render library table + print("Step 2: Creating library table (page 1)...") + table = library.create_library_table(page=0) + print(f"✓ Library table created: {table is not None}") + if table: + print(f" Table has {len(table.rows)} rows") + print() + + print("Rendering library view...") + image = library.render_library() + + if not image: + print("ERROR: No image rendered!") + print(f" library.library_table = {library.library_table}") + print(f" library.rendered_page = {library.rendered_page}") + return 1 + + print(f"✓ Library rendered: {image.size} {image.mode}") + + # Check if image is not blank + import numpy as np + img_array = np.array(image) + unique_colors = len(np.unique(img_array.reshape(-1, img_array.shape[-1]), axis=0)) + print(f" Image has {unique_colors} unique colors (blank would be ~1)") + + # Sample some pixels to see what we have + print(f" Sample pixels:") + print(f" Top-left corner: {image.getpixel((10, 10))}") + print(f" Center: {image.getpixel((936, 702))}") + print(f" Bottom-right: {image.getpixel((1860, 1390))}") + print() + + # Save for reference + print(f"Saving to {output_path}...") + image.save(output_path) + print(f"✓ Saved to {output_path}") + print() + + # Step 3: Initialize hardware HAL + print("Step 3: Initializing hardware HAL...") + hal = HardwareDisplayHAL( + width=1872, + height=1404, + vcom=vcom, + config_file="hardware_config.json" + ) + + print("Initializing hardware components...") + await hal.initialize() + print("✓ Hardware initialized") + print() + + # Step 4: Display on e-ink screen + print("Step 4: Displaying library on e-ink screen...") + print("(This may take a few seconds for the full refresh)") + print(f"Converting image from {image.mode} to grayscale for e-ink...") + + # Convert to grayscale (L mode) for e-ink display + if image.mode == 'RGBA': + # Convert RGBA to RGB first (flatten alpha) + rgb_image = Image.new('RGB', image.size, (255, 255, 255)) + rgb_image.paste(image, mask=image.split()[3]) # Use alpha as mask + image_gray = rgb_image.convert('L') + else: + image_gray = image.convert('L') + + print(f"Image converted to {image_gray.mode} mode") + await hal.show_image(image_gray) + print("✓ Image displayed!") + print() + + print("=" * 60) + print("SUCCESS!") + print("=" * 60) + print() + print("The library view should now be visible on your e-ink screen.") + print() + print(f"You can also view the saved PNG: {output_path}") + print() + print("Press Ctrl+C to exit") + print() + + # Keep running so user can see the image + try: + while True: + await asyncio.sleep(1) + except KeyboardInterrupt: + print("\nCleaning up...") + await hal.cleanup() + print("Done!") + return 0 + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser( + description='Test library rendering on hardware display' + ) + parser.add_argument( + 'library_path', + nargs='?', + default='tests/data/library-epub', + help='Path to library directory (default: tests/data/library-epub)' + ) + parser.add_argument( + '--vcom', + type=float, + default=-1.65, + help='VCOM voltage (check your display label, default: -1.65)' + ) + + args = parser.parse_args() + + try: + exit_code = asyncio.run(main(args)) + sys.exit(exit_code or 0) + except KeyboardInterrupt: + print("\nTest interrupted") + sys.exit(0) + except Exception as e: + print(f"\nERROR: {e}") + import traceback + traceback.print_exc() + sys.exit(1) diff --git a/examples/test_library_render.py b/examples/test_library_render.py new file mode 100755 index 0000000..7edb54c --- /dev/null +++ b/examples/test_library_render.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +""" +Test script to render the library view and save it as PNG. +This helps verify the rendering is working correctly. +""" + +import sys +from pathlib import Path + +# Add parent directory to path +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from dreader.library import LibraryManager + + +def main(): + """Test library rendering and save as PNG.""" + + library_path = "tests/data/library-epub" + output_path = "library_render_test.png" + + print("=" * 60) + print("Library Rendering Test") + print("=" * 60) + print(f"Library path: {library_path}") + print(f"Output file: {output_path}") + print() + + # Create library manager + print("Creating library manager...") + library = LibraryManager( + library_path=library_path, + page_size=(1872, 1404) + ) + + # Scan library + print("Scanning library...") + library.scan_library() + + print() + print(f"Found {len(library.books)} books:") + for book in library.books: + print(f" - {book['title']} by {book['author']}") + print() + + # Create library table (renders the first page) + print("Creating library table (page 1)...") + library.create_library_table(page=0) + + # Render the table + print("Rendering library view...") + image = library.render_library() + + if image: + print(f"Image size: {image.size}") + print(f"Image mode: {image.mode}") + + # Save as PNG + print(f"Saving to {output_path}...") + image.save(output_path) + + print() + print("=" * 60) + print("SUCCESS!") + print("=" * 60) + print(f"Library view saved to: {output_path}") + print() + print("You can view the image with:") + print(f" xdg-open {output_path} # Linux") + print(f" open {output_path} # macOS") + print() + + return 0 + else: + print() + print("=" * 60) + print("ERROR: No image rendered") + print("=" * 60) + return 1 + + +if __name__ == "__main__": + try: + sys.exit(main()) + except Exception as e: + print(f"\nERROR: {e}") + import traceback + traceback.print_exc() + sys.exit(1) diff --git a/examples/test_simple_pattern.py b/examples/test_simple_pattern.py new file mode 100755 index 0000000..fe1c836 --- /dev/null +++ b/examples/test_simple_pattern.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +""" +Simplest possible test - half black, half white screen. +This verifies the display can show black pixels. +""" + +import sys +import asyncio +from pathlib import Path +from PIL import Image, ImageDraw + +# Add parent directory to path +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from dreader.hal_hardware import HardwareDisplayHAL + + +def create_simple_test(): + """Create a simple half-black, half-white test image.""" + print("Creating simple test pattern...") + print("Left half: BLACK") + print("Right half: WHITE") + + width, height = 1872, 1404 + + # Create grayscale image (L mode = 8-bit grayscale) + img = Image.new('L', (width, height), color=255) # Start with white + draw = ImageDraw.Draw(img) + + # Draw left half black + draw.rectangle( + [(0, 0), (width // 2, height)], + fill=0 # 0 = black + ) + + print(f"Test image created: {width}x{height} grayscale") + print(f" Left half ({width//2}px): BLACK (0)") + print(f" Right half ({width//2}px): WHITE (255)") + + return img + + +async def main(args): + """Test hardware display with simple pattern.""" + + print("=" * 60) + print("Simple Display Test - Half Black, Half White") + print("=" * 60) + print() + + vcom = args.vcom + print(f"Using VCOM: {vcom}V") + print() + + # Create simple test image + test_image = create_simple_test() + + # Save test image for reference + output_file = "simple_test.png" + test_image.save(output_file) + print(f"Test pattern saved to: {output_file}") + print() + + # Initialize hardware HAL + print("Initializing hardware HAL...") + hal = HardwareDisplayHAL( + width=1872, + height=1404, + vcom=vcom, + config_file="hardware_config.json" + ) + + print("Initializing hardware components...") + await hal.initialize() + print("✓ Hardware initialized") + print() + + # Display the test image + print("Displaying test pattern on e-ink screen...") + print("(This may take a few seconds for the full refresh)") + await hal.show_image(test_image) + print("✓ Image displayed!") + print() + + print("=" * 60) + print("CHECK YOUR SCREEN:") + print("=" * 60) + print() + print("You should see:") + print(" • LEFT HALF: BLACK") + print(" • RIGHT HALF: WHITE") + print() + print("If you see this, the display is working!") + print("If the screen is all white, there may be a driver issue.") + print() + print("Press Ctrl+C to exit") + print() + + # Keep running so user can see the image + try: + while True: + await asyncio.sleep(1) + except KeyboardInterrupt: + print("\nCleaning up...") + await hal.cleanup() + print("Done!") + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description='Simple display test') + parser.add_argument('--vcom', type=float, default=-1.65, + help='VCOM voltage (check your display label, default: -1.65)') + + args = parser.parse_args() + + try: + asyncio.run(main(args)) + except KeyboardInterrupt: + print("\nTest interrupted") + except Exception as e: + print(f"\nERROR: {e}") + import traceback + traceback.print_exc() + sys.exit(1) diff --git a/hardware_config.json b/hardware_config.json index 224c20c..925f8ec 100644 --- a/hardware_config.json +++ b/hardware_config.json @@ -5,8 +5,9 @@ "display": { "width": 1872, "height": 1404, - "vcom": -2.0, + "vcom": -1.7, "spi_hz": 24000000, + "rotate": "CW", "auto_sleep": true },