- Fixed drawing of round objects (Circles, Rounded Rects) using a lookup table

- Added function to read out the display_buffer via USB-Serial
 - Added basic structure and files for later complete firmware (still in progress)
 - Added Doc folder with schematic in it
 - Added Python script and batch file to read out the display buffer and open the image in gimp
This commit is contained in:
2025-09-07 08:55:39 +02:00
parent 714b5be13c
commit 128d42c586
66 changed files with 29943 additions and 225 deletions

250
Python/Buffer_Reader.cmd Normal file
View File

@@ -0,0 +1,250 @@
@echo off
setlocal enabledelayedexpansion
:: Buffer Reader Batch Script
:: Calls the RP2350 Buffer Reader Python script with various options
echo ===============================================
echo RP2350 Display Buffer Reader
echo ===============================================
echo.
:: Check if Python is available
python --version >nul 2>&1
if errorlevel 1 (
echo ERROR: Python is not installed or not in PATH
echo Please install Python and try again.
pause
exit /b 1
)
:: Check if Buffer_Reader.py exists
if not exist "Buffer_Reader.py" (
echo ERROR: Buffer_Reader.py not found in current directory
echo Please make sure the script is in the same folder as this batch file.
pause
exit /b 1
)
:: Check command line arguments
if "%~1"=="" goto :interactive_mode
if "%~1"=="/?" goto :show_help
if "%~1"=="--help" goto :show_help
:: Direct mode - use command line arguments
set "port_input=%~1"
set "image_file=%~2"
set "csv_file=%~3"
:: Process port input - add COM prefix if not present
if "!port_input:~0,3!"=="COM" (
set "serial_port=!port_input!"
) else (
set "serial_port=COM!port_input!"
)
goto :set_default_files
:interactive_mode
echo Interactive Mode
echo ================
echo.
:: Get serial port number
set "port_input="
set /p "port_input=Enter COM port number (e.g., 3 for COM3): "
if "!port_input!"=="" (
echo ERROR: Port number is required
pause
exit /b 1
)
:: Add COM prefix if not already present
if "!port_input:~0,3!"=="COM" (
set "serial_port=!port_input!"
) else (
set "serial_port=COM!port_input!"
)
:: Get image filename (optional)
set "image_file="
set /p "image_file=Enter image filename (press Enter for default): "
:: Get CSV filename (optional)
set "csv_file="
set /p "csv_file=Enter CSV filename (press Enter for default): "
:set_default_files
:: Get current timestamp for default filenames
for /f "tokens=2 delims==" %%a in ('wmic OS Get localdatetime /value') do set "dt=%%a"
set "timestamp=%dt:~0,4%%dt:~4,2%%dt:~6,2%_%dt:~8,2%%dt:~10,2%%dt:~12,2%"
if "!image_file!"=="" set "image_file=capture_1.png"
if "!csv_file!"=="" set "csv_file=pixels_1.csv"
:main_loop
echo.
echo Configuration:
echo Serial Port: !serial_port!
echo Image File: !image_file!
echo CSV File: !csv_file!
echo.
:run_script
echo.
echo Starting buffer read...
echo Command: python Buffer_Reader.py "!serial_port!" "!image_file!" "!csv_file!"
echo.
:: Run the Python script
python Buffer_Reader.py "!serial_port!" "!image_file!" "!csv_file!"
:: Check if the script ran successfully
if errorlevel 1 (
echo.
echo ERROR: Script execution failed
goto :ask_repeat
) else (
echo.
echo SUCCESS: Buffer read completed successfully!
echo Image saved to: !image_file!
echo CSV saved to: !csv_file!
:: Automatically open image with GIMP
if exist "!image_file!" (
echo.
echo Opening image with GIMP...
start "" "gimp" "!image_file!" 2>nul
if errorlevel 1 (
echo Warning: Could not open GIMP. Please check if GIMP is installed and in PATH.
echo You can manually open the file: !image_file!
)
)
)
:ask_repeat
echo.
echo Press ENTER to repeat readout with same settings
echo Press ESC or N + ENTER to exit
set /p "repeat_choice="
:: Check if user pressed just Enter (empty input)
if "!repeat_choice!"=="" (
:: Increment counter for repeat runs
if not defined repeat_counter set repeat_counter=0
set /a repeat_counter+=1
:: Create new filenames with counter suffix
:: Remove existing counter suffix if present, then add new one
set "base_image=!image_file!"
set "base_csv=!csv_file!"
:: Remove previous counter from image filename
for /f "tokens=1,2 delims=_" %%a in ("!base_image!") do (
if "%%b" neq "" (
echo %%b | findstr "^[0-9][0-9]*\.png$" >nul
if !errorlevel! equ 0 (
set "base_image=%%a.png"
)
)
)
:: Remove previous counter from csv filename
for /f "tokens=1,2 delims=_" %%a in ("!base_csv!") do (
if "%%b" neq "" (
echo %%b | findstr "^[0-9][0-9]*\.csv$" >nul
if !errorlevel! equ 0 (
set "base_csv=%%a.csv"
)
)
)
:: Add counter to filenames
for %%f in ("!base_image!") do (
set "image_file=%%~nf_!repeat_counter!%%~xf"
)
for %%f in ("!base_csv!") do (
set "csv_file=%%~nf_!repeat_counter!%%~xf"
)
goto :main_loop
)
:: Check for exit conditions
if /i "!repeat_choice!"=="N" goto :exit_script
if "!repeat_choice!"=="" goto :exit_script
:: If user entered something else, treat as exit
goto :exit_script
:exit_script
echo.
echo Exiting...
pause
exit /b 0
:increment_filename
:: Function to increment filename counter intelligently
:: %1 = input filename, %2 = variable name to store result
setlocal enabledelayedexpansion
set "filename=%~1"
set "return_var=%~2"
:: Extract name and extension
for %%f in ("!filename!") do (
set "name=%%~nf"
set "ext=%%~xf"
)
:: Check if filename ends with _number pattern
set "counter=1"
set "base_name=!name!"
:: Look for _number at the end of the filename
for /f "tokens=1,2 delims=_" %%a in ("!name!") do (
set "potential_base=%%a"
set "potential_counter=%%b"
if "!potential_counter!" neq "" (
:: Check if the part after _ is a number
set "is_number=1"
for /f "delims=0123456789" %%x in ("!potential_counter!") do set "is_number=0"
if !is_number! equ 1 (
:: It's a number, so increment it
set /a counter=!potential_counter!+1
set "base_name=!potential_base!"
)
)
)
:: Build new filename
set "new_filename=!base_name!_!counter!!ext!"
:: Return the result
endlocal & set "%return_var%=%new_filename%"
goto :eof
:show_help
echo.
echo USAGE:
echo %~nx0 - Interactive mode
echo %~nx0 [port_number] - Use default filenames (e.g., %~nx0 3)
echo %~nx0 [port_number] [image] - Specify image filename (e.g., %~nx0 3 my_screen.png)
echo %~nx0 [port_number] [image] [csv] - Specify both filenames
echo.
echo EXAMPLES:
echo %~nx0
echo %~nx0 3 (will use COM3)
echo %~nx0 4 my_screen.png (will use COM4)
echo %~nx0 3 my_screen.png data.csv (will use COM3)
echo.
echo Note: You can enter just the port number (e.g., 3) and COM will be added automatically
echo Generated images will automatically open in GIMP
echo.
echo OPTIONS:
echo /? Show this help
echo --help Show this help
echo.
pause
exit /b 0

167
Python/Buffer_Reader.py Normal file
View File

@@ -0,0 +1,167 @@
#!/usr/bin/env python3
import serial
import struct
from PIL import Image
import numpy as np
import time
import sys
import csv
class RP2350BufferReader:
def __init__(self, port, baudrate=115200):
self.Ser = serial.Serial(port, baudrate, timeout=5)
self.Width = 0
self.Height = 0
self.Bits_Per_Pixel = 0
def Read_Display_Buffer(self, image_filename=None, csv_filename=None):
"""Request and read display buffer from RP2350"""
# Send buffer read command
self.Ser.write(b'b')
# Read header
Header = self.Ser.read_until(b'\r').decode('ascii').strip()
if not Header.startswith('IMGBUF'):
raise ValueError(f"Invalid header: {Header}")
# Parse dimensions
Parts = Header[6:].split(',')
self.Width = int(Parts[0])
self.Height = int(Parts[1])
self.Bits_Per_Pixel = int(Parts[2])
print(f"Reading {self.Width}x{self.Height} buffer ({self.Bits_Per_Pixel}bpp)")
# Read binary chunked data with progress bar
Pixel_Data_ASCII : str = ""
Expected_Pixels = self.Width * self.Height
Expected_Byes = Expected_Pixels * (self.Bits_Per_Pixel // 8) * 2
Start_Time = time.time()
def Update_Progress(current, total, width=50):
Percent = (current / total) * 100
Filled = int(width * current / total)
Bar = '' * Filled + '' * (width - Filled)
Elapsed = time.time() - Start_Time
Rate = current / Elapsed if Elapsed > 0 else 0
Eta = (total - current) / Rate if Rate > 0 else 0
sys.stdout.write(f'\r[{Bar}] {Percent:.1f}% ({current}/{total}) 'f'Rate: {Rate:.0f} px/s ETA: {Eta:.1f}s ')
sys.stdout.flush()
print("Progress:")
while len(Pixel_Data_ASCII) < Expected_Byes:
Byte = self.Ser.read_all()
if Byte is not None:
Pixel_Data_ASCII = Pixel_Data_ASCII + Byte.decode("utf-8")
Update_Progress(len(Pixel_Data_ASCII), Expected_Byes)
Update_Progress(len(Pixel_Data_ASCII), Expected_Byes) # Final progress update
print()
Byte = self.Ser.read(1)
Byte_Value = ord(Byte.decode('utf-8'))
if Byte_Value != 0x0D:
raise ValueError(f"Invalid byte after pixel data: {str(int(Byte))}")
# Save ASCII data to CSV if requested
if csv_filename:
self._Save_ASCII_To_CSV(Pixel_Data_ASCII, csv_filename)
Pixel_Data_Hex = []
for i in range(0, len(Pixel_Data_ASCII), 4):
Int_Value = int(Pixel_Data_ASCII[i:i+4], 16)
# if Int_Value > 0:
# print("0x{:04x}".format(Int_Value))
Pixel_Data_Hex.append(Int_Value)
# Convert to numpy array
RGB565_Data = np.array(Pixel_Data_Hex, dtype=np.uint16)
RGB888_Data = self._RGB565_to_RGB888(RGB565_Data)
# Create PIL Image
img_array = RGB888_Data.reshape((self.Height, self.Width, 3))
image = Image.fromarray(img_array, 'RGB')
# image = Image.new('RGB', (self.Width, self.Height))
# Save if filename provided
if image_filename:
if not image_filename.lower().endswith(('.png', '.bmp', '.tiff')):
image_filename += '.png'
image.save(image_filename)
print(f"Image saved as {image_filename}")
return image
def _RGB565_to_RGB888(self, rgb565_array):
"""Convert RGB565 to RGB888"""
RGB888_Data = np.zeros((len(rgb565_array), 3), dtype=np.uint8)
# Extract RGB components from RGB565
r5 = ((rgb565_array & 0x1F00) >> 8)
g6 = ((rgb565_array & 0xE000) >> 11) | ((rgb565_array & 0x0007) << 3)
b5 = ((rgb565_array & 0x00F8) >> 3)
# Scale to 8-bit
RGB888_Data[:, 0] = (r5 * 255) // 31 # Red
RGB888_Data[:, 1] = (g6 * 255) // 63 # Green
RGB888_Data[:, 2] = (b5 * 255) // 31 # Blue
return RGB888_Data
def _Save_ASCII_To_CSV(self, pixel_data_ascii, csv_filename):
"""Save ASCII pixel data to CSV file with display dimensions"""
if not csv_filename.lower().endswith('.csv'):
csv_filename += '.csv'
with open(csv_filename, 'w', newline='') as CSV_File:
Writer = csv.writer(CSV_File)
# Process data in chunks of 4 characters (one pixel)
Pixel_Index = 0
Row_Data = []
for i in range(0, len(pixel_data_ascii), 4):
# Get 4-character hex value for current pixel
Pixel_Hex = pixel_data_ascii[i:i+4]
Row_Data.append(Pixel_Hex)
Pixel_Index += 1
# When we've collected a full row worth of pixels, write to CSV
if Pixel_Index % self.Width == 0:
Writer.writerow(Row_Data)
Row_Data = []
print(f"ASCII data saved to {csv_filename} ({self.Height} rows × {self.Width} columns)")
def Close(self):
"""Close serial connection"""
self.Ser.close()
# Usage example
if __name__ == "__main__":
import sys
if len(sys.argv) < 2:
print("Usage: python buffer_reader.py <serial_port> [output_filename]")
print("Example: python buffer_reader.py COM3 screen_capture.png")
sys.exit(1)
port = sys.argv[1]
image_filename = sys.argv[2] if len(sys.argv) > 2 else f"buffer_capture_{int(time.time())}.png"
csv_filename = sys.argv[3] if len(sys.argv) > 3 else f"pixel_data_{int(time.time())}.csv"
try:
reader = RP2350BufferReader(port)
image = reader.Read_Display_Buffer(image_filename, csv_filename)
reader.Close()
print("Buffer read complete!")
except Exception as e:
print(f"Error: {e}")
sys.exit(1)