Skip to content

Instantly share code, notes, and snippets.

@jmpinit
Created December 10, 2024 19:52
Show Gist options
  • Select an option

  • Save jmpinit/5fdc73f9f54c3155681a95afe67f29cd to your computer and use it in GitHub Desktop.

Select an option

Save jmpinit/5fdc73f9f54c3155681a95afe67f29cd to your computer and use it in GitHub Desktop.
Loads a Saleaa logic analyzer dump CSV made from the signals of the LCD in the Brother PE-150 embroidery machine and turns it into an image file
#!/usr/bin/env python3
import csv
import argparse
from PIL import Image
def main():
parser = argparse.ArgumentParser(description='Reconstruct 240x64 LCD image from logic analyzer CSV.')
parser.add_argument('--input', required=True, help='Path to input CSV file')
parser.add_argument('--output', required=True, help='Output image file (e.g. output.png)')
args = parser.parse_args()
WIDTH = 240
HEIGHT = 64
# We'll store the final image data as a 2D list of bits (0/1)
image_data = [[0]*WIDTH for _ in range(HEIGHT)]
# We know the sequence should contain data for 64 rows.
# We'll detect the first row when we see LOAD_ROW_CLK with DIO1=1.
current_row_bits = []
row_index = None # Unknown until first LOAD event with DIO1=1
rows_collected = 0
# Track previous states to detect edges
prev_clk = None
prev_load = None
# Open and read CSV
with open(args.input, 'r', newline='') as f:
reader = csv.reader(f)
header = next(reader)
# Expect header: Time [s],SDI,DIO1,LOAD_ROW_CLK,CLK
# We will index: 0:time,1:SDI,2:DIO1,3:LOAD,4:CLK
for line in reader:
if len(line) < 5:
continue
# Parse signals
# time = float(line[0]) # not needed
SDI = int(line[1])
DIO1 = int(line[2])
LOAD = int(line[3])
CLK = int(line[4])
# Detect edges:
# Falling edge: previous=1 current=0
if prev_clk is not None:
clk_falling = (prev_clk == 1 and CLK == 0)
else:
clk_falling = False
if prev_load is not None:
load_falling = (prev_load == 1 and LOAD == 0)
else:
load_falling = False
# On falling edge of CLK, latch SDI bit
if clk_falling:
current_row_bits.append(SDI)
# If we have collected a full row of 240 bits and we see LOAD falling edge
# that finalizes one row
if len(current_row_bits) == WIDTH:
# Determine row index
if row_index is None:
# First LOAD event sets row_index=0 if DIO1=1
if DIO1 == 1:
row_index = 0
else:
# If we never saw first row with DIO1=1, this might be a corrupted capture
# but let's assume row_index=0 anyway
row_index = 0
else:
# Subsequent LOAD events increment row index
if DIO1 == 0:
row_index += 1
else:
# If DIO1=1 again at another load event, it might indicate a reset?
# Just handle gracefully
row_index = 0
# Store the collected bits into image_data
# current_row_bits is a list of 0/1. Each corresponds to a pixel in the row.
# The number of rows collected must not exceed 64
if 0 <= row_index < HEIGHT:
for x, bit in enumerate(current_row_bits):
image_data[row_index][x] = 1 - bit
# Reset for next row
current_row_bits = []
rows_collected += 1
if rows_collected >= HEIGHT:
# We have all rows needed
break
prev_clk = CLK
prev_load = LOAD
# If we didn't fully get 64 rows, we still output what we have.
# Convert image_data to a Pillow Image
# Mode '1' is a 1-bit image. We'll assume bit=1 means white and bit=0 means black.
img = Image.new('1', (WIDTH, HEIGHT))
# Pillow expects 0=black,1=white in '1' mode.
# We directly set pixels:
pixels = img.load()
for y in range(HEIGHT):
for x in range(WIDTH):
# If you want to invert colors, use 1 - image_data[y][x].
pixels[x, y] = image_data[y][x]
img.save(args.output)
print(f'Saved reconstructed image to {args.output}')
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment