code.py
  1  # SPDX-FileCopyrightText: 2021 Jeff Epler for Adafruit Industries
  2  #
  3  # SPDX-License-Identifier: MIT
  4  
  5  import os
  6  
  7  import board
  8  import keypad
  9  import ulab.numpy as np
 10  
 11  from adafruit_ble import BLERadio
 12  from adafruit_ble.advertising import Advertisement
 13  
 14  from thermalprinter import CatPrinter
 15  from seekablebitmap import imageopen
 16  
 17  ble = BLERadio()  # pylint: disable=no-member
 18  
 19  buttons = keypad.Keys([board.BUTTON_A, board.BUTTON_B], value_when_pressed=False)
 20  
 21  def wait_for_press(kbd):
 22      """
 23      Wait for a keypress and return the event
 24      """
 25      while True:
 26          event = kbd.events.get()
 27          if event and event.pressed:
 28              return event
 29  
 30  
 31  def show(s):
 32      """
 33      Display a message on the screen
 34      """
 35      board.DISPLAY.auto_refresh = False
 36      print("\n" * 24)
 37      print(s)
 38      board.DISPLAY.auto_refresh = True
 39  
 40  
 41  def show_error(s):
 42      """
 43      Display a message on the screen and wait for a button press
 44      """
 45      show(s + "\nPress a button to continue")
 46      wait_for_press(buttons)
 47  
 48  
 49  def find_cat_printer(radio):
 50      """
 51      Connect to the cat printer device using BLE
 52      """
 53      while True:
 54          show("Scanning for GB02 device...")
 55          for adv in radio.start_scan(Advertisement):
 56              complete_name = getattr(adv, "complete_name")
 57              if complete_name is not None:
 58                  print(f"Saw {complete_name}")
 59              if complete_name == "GB02":
 60                  radio.stop_scan()
 61                  return radio.connect(adv, timeout=10)[CatPrinter]
 62  
 63  
 64  image_files = [
 65      i
 66      for i in os.listdir("/")
 67      if i.lower().endswith(".pbm") or i.lower().endswith(".bmp")
 68  ]
 69  image_files.sort(key=lambda filename: filename.lower())
 70  
 71  
 72  def select_image():
 73      i = 0
 74      while True:
 75          show(
 76              f"Select image file\nA: next image\nB: print this image\n\n{image_files[i]}"
 77          )
 78          event = wait_for_press(buttons)
 79          if event.key_number == 0:  # button "A"
 80              i = (i + 1) % len(image_files)
 81          if event.key_number == 1:  # button "B"
 82              return image_files[i]
 83  
 84  
 85  printer = find_cat_printer(ble)
 86  
 87  def main():
 88      try:
 89          filename = select_image()
 90  
 91          show(f"Loading {filename}")
 92  
 93          image = imageopen(filename)
 94          if image.width != 384:
 95              raise ValueError("Invalid image.  Must be 384 pixels wide")
 96          if image.bits_per_pixel != 1:
 97              raise ValueError("Invalid image.  Must be 1 bit per pixel (black & white)")
 98  
 99          invert_image = image.palette and image.palette[0] == 0
100  
101          show(f"Printing {filename}")
102  
103          for i in range(image.height):
104              row_data = image.get_row(i)
105              if invert_image:
106                  row_data = ~np.frombuffer(row_data, dtype=np.uint8)
107              printer.print_bitmap_row(row_data)
108  
109          # Print blank lines until the paper can be torn off
110          for i in range(80):
111              printer.print_bitmap_row(b"\0" * 48)
112  
113      except Exception as e: # pylint: disable=broad-except
114          show_error(str(e))
115          image_files.remove(filename)
116  
117  while True:
118      main()