- First complete version of firmware. Currently being tested in the rehearsal room

- Added bunch of screens, fonts and images
 - Added script to read out frame buffer (function currently disabled in Firmware)
This commit is contained in:
2025-10-26 20:57:58 +01:00
parent 90bca063e6
commit 89c875e38f
62 changed files with 4668 additions and 489 deletions

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)