Adafruit_Learning_System_Gu.../CircuitPython_BLEThermalPrinter/code.py
2021-09-28 11:35:03 -05:00

142 lines
4.3 KiB
Python

import seekablebitmap
import gc
import os
import struct
import time
import board
import digitalio
import keypad
import ulab.numpy as np
import adafruit_ble
from adafruit_ble import BLERadio
from adafruit_ble.advertising import Advertisement
from thermalprinter import CatPrinter
from seekablebitmap import imageopen
ble = BLERadio() # pylint: disable=no-member
buttons = keypad.Keys([board.BUTTON_A, board.BUTTON_B], value_when_pressed=False)
def pnmopen(filename):
"""
Scan for netpbm format info, skip over comments, and read header data.
Return the format, header, and the opened file positioned at the start of
the bitmap data.
"""
# pylint: disable=too-many-branches
image_file = open(filename, "rb")
magic_number = image_file.read(2)
image_file.seek(2)
pnm_header = []
next_value = bytearray()
while True:
# We have all we need at length 3 for formats P2, P3, P5, P6
if len(pnm_header) == 3:
return image_file, magic_number, pnm_header
if len(pnm_header) == 2 and magic_number in [b"P1", b"P4"]:
return image_file, magic_number, pnm_header
next_byte = image_file.read(1)
if next_byte == b"":
raise RuntimeError("Unsupported image format {}".format(magic_number))
if next_byte == b"#": # comment found, seek until a newline or EOF is found
while image_file.read(1) not in [b"", b"\n"]: # EOF or NL
pass
elif not next_byte.isdigit(): # boundary found in header data
if next_value:
# pull values until space is found
pnm_header.append(int("".join(["%c" % char for char in next_value])))
next_value = bytearray() # reset the byte array
else:
next_value += next_byte # push the digit into the byte array
def wait_for_press(kbd):
"""
Wait for a keypress and return the event
"""
while True:
event = kbd.events.get()
if event and event.pressed:
return event
def show(s):
"""
Display a message on the screen
"""
board.DISPLAY.auto_refresh = False
print("\n" * 24)
print(s)
board.DISPLAY.auto_refresh = True
def show_error(s):
"""
Display a message on the screen and wait for a button press
"""
show(s + "\nPress a button to continue")
wait_for_press(buttons)
def find_cat_printer(radio):
"""
Connect to the cat printer device using BLE
"""
while True:
show("Scanning for GB02 device...")
for adv in radio.start_scan(Advertisement):
complete_name = getattr(adv, 'complete_name')
if complete_name is not None:
print(f"Saw {complete_name}")
if complete_name == 'GB02':
radio.stop_scan()
return radio.connect(adv, timeout=10)[CatPrinter]
image_files = [i for i in os.listdir('/') if i.lower().endswith(".pbm") or i.lower().endswith(".bmp")]
image_files.sort(key=lambda filename: filename.lower())
def select_image():
i = 0
while True:
show(f"Select image file\nA: next image\nB: print this image\n\n{image_files[i]}")
event = wait_for_press(buttons)
if event.key_number == 0: # button "A"
i = (i + 1) % len(image_files)
if event.key_number == 1: # button "B"
return image_files[i]
printer = find_cat_printer(ble)
while True:
try:
filename = select_image()
show(f"Loading {filename}")
image = imageopen(filename)
if image.width != 384:
raise ValueError("Invalid image. Must be 384 pixels wide")
if image.bits_per_pixel != 1:
raise ValueError("Invalid image. Must be 1 bit per pixel (black & white)")
invert_image = image.palette and image.palette[0] == 0
show(f"Printing {filename}")
for i in range(image.height):
row_data = image.get_row(i)
if invert_image:
row_data = ~np.frombuffer(row_data, dtype=np.uint8)
printer.print_bitmap_row(row_data)
# Print blank lines until the paper can be torn off
for i in range(80):
printer.print_bitmap_row(b'\0' * 48)
except Exception as e:
show_error(str(e))
image_files.remove(filename)
continue