Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
379075155This file is pinned to the selected release. Download the original to open it in your design tool.
# Converts, compresses and transmits images to dot matrix ESLs
# 2018 furrtek - furrtek.org
# See LICENSE
import pr
import tx
from imageio.v2 import imread
import sys
bytes_per_frame = 20
bits_per_frame = bytes_per_frame * 8
def image_convert(image, color_pass):
pixels = []
for row in image:
for rgb in row:
r, g, b = rgb[0] / 255, rgb[1] / 255, rgb[2] / 255
is_red = r > 0.5 and g < 0.5 and b < 0.5
is_yellow = r > 0.5 and g > 0.5 and b < 0.5
if color_pass:
# 0 = red or yellow, 1 = use first block (black/white)
pixels.append(0 if (is_red or is_yellow) else 1)
elif is_red and color_mode:
pixels.append(1) # red: first=1, second=0
elif is_yellow and color_mode:
pixels.append(0) # yellow: first=0, second=0
else:
luma = 0.21 * r + 0.72 * g + 0.07 * b
pixels.append(0 if luma < 0.5 else 1) # 0 codes black
return pixels
def record_run(run_count):
# Convert to binary
del bits[:]
while run_count:
bits.insert(0, run_count & 1)
run_count >>= 1
# Zero length coding - 1
for b in bits[1:]:
compressed.append(0)
if len(bits):
compressed.extend(bits)
def usage():
print("img2dm - Transmits image to Dot Matrix ESL\n")
print("Usage:")
print("img2dm.py port image barcode (page color x y pp4)\n")
print(" port: serial port name (0 for ESL Blaster)")
print(" image: image file")
print(" barcode: 17-character ESL barcode data")
print(" page: page number to update (0~15), default: 0")
print(" color: 0:Black and white only, 1:Color-capable ESL, default: 0")
print(" x y: top-left position of image, default: 0 0")
print(" pp4: force slow PP4 protocol, default: 0")
exit()
arg_count = len(sys.argv)
if arg_count < 4:
usage()
if arg_count >= 5:
page = int(sys.argv[4])
if page < 0 or page > 15:
print("Page must be between 0 and 15.")
exit()
else:
page = 1
port = sys.argv[1]
pp16 = 0
# Search for connected ESL Blaster if required
if (port == "0"):
blaster_info = tx.search_esl_blaster()
if blaster_info[0] == False:
exit()
if blaster_info[2] >= 2:
pp16 = 1 # ESL Blaster FW V2 and above is PP16 compatible
# Open image file
image = imread(sys.argv[2])
width = image.shape[1]
height = image.shape[0]
# ESLs only accept images with pixel counts multiple of 8
pixel_count = width * height
if pixel_count & 7:
print("The image's pixel count (currently %d) must be a multiple of 8. Please adjust its size." % pixel_count)
exit()
# Get PLID from barcode string
PLID = pr.get_plid(sys.argv[3])
color_mode = int(sys.argv[5]) if arg_count >= 6 else 0
pos_x = int(sys.argv[6]) if arg_count >= 7 else 0
pos_y = int(sys.argv[7]) if arg_count >= 8 else 0
if arg_count >= 9:
if int(sys.argv[8]):
pp16 = 0
# Medium size is 208*112
# Large size is 296*128
print("Image is %i*%i in %s mode, please make sure that this suits your ESL's display." % (width, height, "color" if color_mode else "black and white"))
# First pass for black and white
pixels = image_convert(image, 0)
if color_mode:
# Append second pass for color, if needed
pixels += image_convert(image, 1)
size_raw = len(pixels)
# First pixel
bits = []
compressed = []
run_pixel = pixels[0]
run_count = 1
compressed.append(run_pixel)
for pixel in pixels[1:]:
if pixel == run_pixel:
# Add to run
run_count += 1
else:
# Record run
record_run(run_count)
run_count = 1
run_pixel = pixel
# Record last run
if run_count > 1:
record_run(run_count)
size_compressed = len(compressed)
# Decide on compression or not
# size_compressed = size_raw # Disable compression
if size_compressed < size_raw:
print("Compression ratio: %.1f%% (%d -> %d bytes)" % (100 - ((size_compressed * 100) / float(size_raw)), size_raw, size_compressed))
data = compressed
compression_type = 2
else:
print("Compression ratio suxx, using raw data instead")
data = pixels
compression_type = 0
# Pad data to multiple of bits_per_frame
data_size = len(data)
padding = bits_per_frame - (data_size % bits_per_frame)
for b in range(0, padding):
data.append(0)
padded_data_size = len(data)
frame_count = padded_data_size // bits_per_frame
#print("Data size: %i (%i frames)" % (data_size, frame_count))
frames = []
# Wake-up ping frame
frames.append(pr.make_ping_frame(PLID, pp16, 400))
# Parameters frame
frame = pr.make_mcu_frame(PLID, 0x05)
pr.append_word(frame, padded_data_size // 8) #data_size // 8) # Total byte count for group
frame.append(0x00) # Unused
frame.append(compression_type)
frame.append(page)
pr.append_word(frame, width)
pr.append_word(frame, height)
pr.append_word(frame, pos_x)
pr.append_word(frame, pos_y)
pr.append_word(frame, 0x0000) # Keycode
frame.append(0x88) # 0x80 = update, 0x08 = set base page
pr.append_word(frame, 0x0000) # Enabled pages (bitmap)
frame.extend([0x00, 0x00, 0x00, 0x00])
pr.terminate_frame(frame, pp16, 1)
frames.append(frame)
# Data frames
i = 0
for fr in range(0, frame_count):
frame = pr.make_mcu_frame(PLID, 0x20)
pr.append_word(frame, fr)
for by in range(0, bytes_per_frame):
v = 0
# Bit string to byte
for bi in range(0, 8):
v <<= 1
v += data[i + bi]
frame.append(v)
i += 8
pr.terminate_frame(frame, pp16, 1)
frames.append(frame)
# Refresh frame
frames.append(pr.make_refresh_frame(PLID, pp16))
# DEBUG
f = open("frames.txt", "w")
for fr in frames:
for b in fr:
f.write(format(b, '02X') + " ")
f.write("\n")
input("Place ESL in front of transmitter and press enter.")
# Send data to IR transmitter
if (port == "0"):
tx.transmit_esl_blaster(frames, pp16, blaster_info[0])
else:
tx.transmit_serial(frames, port)
print("Done. Please allow a few seconds for the ESL to refresh.")