diff --git a/Printer/reliance_printer.py b/Printer/reliance_printer.py deleted file mode 100644 index 4398b79..0000000 --- a/Printer/reliance_printer.py +++ /dev/null @@ -1,11 +0,0 @@ -from Printer import base_printer -import time - -class ReliancePrinter(base_printer.BasePrinter): - def __init__(self, port): - super().__init__(port, 9600) - self.ser.dtr = True - self.ser.rts = True - - time.sleep(1) - self.ser.reset_input_buffer() \ No newline at end of file diff --git a/py_esc_pos/__init__.py b/py_esc_pos/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/commands.py b/py_esc_pos/commands.py similarity index 91% rename from commands.py rename to py_esc_pos/commands.py index 12a6d19..5561a73 100644 --- a/commands.py +++ b/py_esc_pos/commands.py @@ -23,10 +23,15 @@ class Commands: CENTER = b'\x01' RIGHT = b'\x02' + RT_PRINTER = b'\x01' + RT_OFFLINE = b'\x02' + RT_ERROR = b'\x03' + RT_PAPER = b'\x04' + class PhoenixCommands(Commands): SELECT_FONT_A = b'\x1b\x50' - SELECT_FONT_B = b'\x1b\x54' - SELECT_FONT_C = b'\x1b\x55' + SELECT_FONT_C = b'\x1b\x54' + SELECT_FONT_D = b'\x1b\x55' PAPER_STATUS = b'\x1b\x76' PRINT_N_FEED_PAPER_N_LINES = b'\x1b\x64' PRINT_N_FEED_PAPER = b'\x1b\x4a' @@ -37,15 +42,6 @@ class PhoenixCommands(Commands): TOGGLE_AUTO_CUT = b'\x1c\x7d\x60' DYNAMIC_2D_BARCODE = b'\x1d\x28\x6b' PRINT_REAL_TIME_CLOCK = b'\x1c\x7d\x70' - RT_PRINTER = b'\x01' - RT_OFFLINE = b'\x02' - RT_ERROR = b'\x03' - RT_PAPER = b'\x04' - - # Actions - TEST_COIN_IN = b'\x60' - TEST_NOTE_IN = b'\x61' - PRINT_RTC = b'\x1c\x7d\x70' class RelianceCommands(Commands): HORIZONTAL_TAB = b'\x09' @@ -72,7 +68,7 @@ class RelianceCommands(Commands): BARCODE_GENERATOR_2D = b'\x1c\x7d\x25' SET_2D_BARCODE_SIZE = b'\x1c\x7d\x74' BARCODE_GENERATOR = b'\x1d\x6b' - SET_1d_BARCODE_WIDTH_MULT = b'\x1d\x77' + SET_1D_BARCODE_WIDTH_MULT = b'\x1d\x77' SET_1D_BARCODE_HEIGHT = b'\x1d\x68' SET_HRI_PRINTING_POSITION = b'\x1d\x48' SET_HRI_FONT = b'\x1d\x66' diff --git a/main.py b/py_esc_pos/main.py similarity index 56% rename from main.py rename to py_esc_pos/main.py index 63d4d5b..414cf69 100644 --- a/main.py +++ b/py_esc_pos/main.py @@ -1,4 +1,4 @@ -from Menu.main_menu import MainMenu +from py_esc_pos.menu.main_menu import MainMenu if __name__ == "__main__": app = MainMenu() diff --git a/Menu/main_menu.py b/py_esc_pos/menu/main_menu.py similarity index 70% rename from Menu/main_menu.py rename to py_esc_pos/menu/main_menu.py index 04bd129..1055f5c 100644 --- a/Menu/main_menu.py +++ b/py_esc_pos/menu/main_menu.py @@ -2,10 +2,12 @@ from rich.prompt import Prompt from rich.panel import Panel from rich.table import Table -from Menu.print_menu import PrintMenu -from Menu.util import get_raster_blob, find_port, verify_printer_connection -from commands import PhoenixCommands, RelianceCommands, Commands -from Printer import phoenix_printer, reliance_printer + +from py_esc_pos.commands import PhoenixCommands, RelianceCommands, Commands +from py_esc_pos.printer import phoenix_printer, reliance_printer + +from print_menu import PrintMenu +from util import get_raster_blob, find_port, verify_printer_connection console = Console() @@ -34,7 +36,7 @@ def connect(self): if not port: return False printer_type = Prompt.ask( - "\nSelect Printer Type", + "\nSelect printer Type", choices=["phoenix", "reliance"] ).lower() @@ -53,16 +55,19 @@ def run(self): console.print("[bold red]Failed to connect to printer. Exiting.[/bold red]") return + self.menu() + + def menu(self): while True: menu_text = ( - "1. [bold cyan]Print Menu[/bold cyan] (Text, Font, Style)\n" + "1. [bold cyan]Print menu[/bold cyan] (Text, Font, Style)\n" "2. [bold cyan]Print image[/bold cyan] \n" "3. [bold magenta]Send Raw Input[/bold magenta]\n" "4. [bold green]View Command List[/bold green]\n" "0. Exit" ) - console.print(Panel(menu_text, title="Main Menu", expand=False)) + console.print(Panel(menu_text, title="Main menu", expand=False)) choice = Prompt.ask("Action", choices=["0", "1", "2", "3", "4"], default="0") if choice == "0": @@ -85,9 +90,17 @@ def handle_print_image(self): path = Prompt.ask("Enter image path") try: raster_blob = get_raster_blob(path) - self.printer.send_command(PhoenixCommands.INIT) - self.printer.send_command(raster_blob) - self.printer.send_command(PhoenixCommands.FULL_CUT) + if self.printer.get_type() == "PhoenixPrinter": + self.printer.send_command(PhoenixCommands.INIT) + self.printer.send_command(raster_blob) + self.printer.send_command(PhoenixCommands.FULL_CUT) + elif self.printer.get_type() == "ReliancePrinter": + self.printer.send_command(RelianceCommands.INIT) + self.printer.send_command(raster_blob) + self.printer.send_command(RelianceCommands.EJECTOR + b'\x05') + else: + console.print("[bold red]Unsupported printer type for image printing.[/bold red]") + return console.print("[bold green]Image sent successfully![/bold green]") except Exception as e: console.print(f"[bold red]Error:[/bold red] {e}") @@ -100,18 +113,18 @@ def handle_raw_input(self): response = self.printer.read_response(timeout=2.0) hex_response = " ".join(f"{b:02X}" for b in response) if hex_response: - console.print(f"Printer response (hex): [green]{hex_response}[/green]") - console.print(f"Printer response: [green]00[/green]") + console.print(f"printer response (hex): [green]{hex_response}[/green]") + console.print(f"printer response: [green]00[/green]") def display_commands(self): - is_phoenix = "Phoenix" in str(type(self.printer)) - phoenix_cmds = PhoenixCommands if is_phoenix else RelianceCommands + is_phoenix = self.printer.get_type() == "PhoenixPrinter" + cmds = PhoenixCommands if is_phoenix else RelianceCommands - table = Table(title=f"{phoenix_cmds.__name__} List", show_header=True, header_style="bold magenta") + table = Table(title=f"{cmds.__name__} List", show_header=True, header_style="bold magenta") table.add_column("Command Name", style="cyan") table.add_column("Hex Value", style="green", justify="right") - commands_to_show = {cmds_name: cmds_bytes for cmds_name, cmds_bytes in phoenix_cmds.__dict__.items() if cmds_name.isupper()} + commands_to_show = {cmds_name: cmds_bytes for cmds_name, cmds_bytes in cmds.__dict__.items() if cmds_name.isupper()} commands_to_show.update({cmds_name: cmds_bytes for cmds_name, cmds_bytes in Commands.__dict__.items() if cmds_name.isupper()}) for name, value in sorted(commands_to_show.items()): @@ -119,4 +132,4 @@ def display_commands(self): table.add_row(name, hex_val) console.print(table) - Prompt.ask("\nPress [bold]Enter[/bold] to return to Main Menu") + Prompt.ask("\nPress [bold]Enter[/bold] to return to Main menu") diff --git a/Menu/print_menu.py b/py_esc_pos/menu/print_menu.py similarity index 94% rename from Menu/print_menu.py rename to py_esc_pos/menu/print_menu.py index 82d5d03..13e08db 100644 --- a/Menu/print_menu.py +++ b/py_esc_pos/menu/print_menu.py @@ -3,7 +3,7 @@ from rich.panel import Panel from rich.prompt import Prompt from rich.table import Table -from commands import PhoenixCommands, RelianceCommands, Commands +from py_esc_pos.commands import PhoenixCommands console = Console() @@ -11,7 +11,7 @@ def add_font_type(print_buffer, font_type): if not print_buffer: console.print("[bold red]No lines to set font for![/bold red]") return - type_map = {"A": PhoenixCommands.SELECT_FONT_A, "B": PhoenixCommands.SELECT_FONT_B, "C": PhoenixCommands.SELECT_FONT_C} + type_map = {"A": PhoenixCommands.SELECT_FONT_A, "C": PhoenixCommands.SELECT_FONT_C, "D": PhoenixCommands.SELECT_FONT_D} print_buffer[-1]["type"] = type_map[font_type] def add_font_style(print_buffer, style): @@ -66,7 +66,7 @@ def menu_print_settings(self): "3. [bold cyan]Set Font Style[/bold cyan] (Last Line)\n" "4. [red]Remove Last Line[/red]\n" "5. [bold magenta]PROCESS PRINT JOB[/bold magenta]\n" - "6. [yellow]Back to Main Menu[/yellow]\n" + "6. [yellow]Back to Main menu[/yellow]\n" "0. Exit" ) menu_panel = Panel(menu_text, title="Print Settings", expand=False) diff --git a/Menu/util.py b/py_esc_pos/menu/util.py similarity index 94% rename from Menu/util.py rename to py_esc_pos/menu/util.py index f54e3e9..7096629 100644 --- a/Menu/util.py +++ b/py_esc_pos/menu/util.py @@ -1,7 +1,7 @@ from PIL import Image, ImageOps import serial.tools.list_ports as port_list -from commands import PhoenixCommands +from py_esc_pos.commands import PhoenixCommands def find_port(): return list(port_list.comports()) diff --git a/Printer/base_printer.py b/py_esc_pos/printer/base_printer.py similarity index 86% rename from Printer/base_printer.py rename to py_esc_pos/printer/base_printer.py index 1caeb51..f886db0 100644 --- a/Printer/base_printer.py +++ b/py_esc_pos/printer/base_printer.py @@ -10,7 +10,7 @@ def __init__(self, port, baudrate): parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, ) - + self.printer_type = "BasePrinter" time.sleep(1) self.ser.reset_input_buffer() self.ser.reset_output_buffer() @@ -31,4 +31,7 @@ def close(self): def read_response(self, timeout=1.0): self.ser.timeout = timeout response = self.ser.read_all() - return response \ No newline at end of file + return response + + def get_type(self): + return self.printer_type \ No newline at end of file diff --git a/Printer/phoenix_printer.py b/py_esc_pos/printer/phoenix_printer.py similarity index 79% rename from Printer/phoenix_printer.py rename to py_esc_pos/printer/phoenix_printer.py index ecb1dad..2d1366e 100644 --- a/Printer/phoenix_printer.py +++ b/py_esc_pos/printer/phoenix_printer.py @@ -1,14 +1,15 @@ -from Printer import base_printer +from py_esc_pos.printer import base_printer +from py_esc_pos.commands import PhoenixCommands import time -from commands import PhoenixCommands ## @class PhoenixPrinter -# @brief Implementation for the Pyramid Phoenix Thermal Printer. +# @brief Implementation for the Pyramid phoenix Thermal printer. # @details This class handles hardware-specific handshaking and status parsing. -# @see [Phoenix Status Documentation](https://escpos.readthedocs.io/en/latest/phoenix_status.html) +# @see [phoenix Status Documentation](https://escpos.readthedocs.io/en/latest/phoenix_status.html) class PhoenixPrinter(base_printer.BasePrinter): def __init__(self, port): super().__init__(port, 9600) + self.printer_type = "PhoenixPrinter" time.sleep(1) self.ser.reset_input_buffer() @@ -16,7 +17,7 @@ def __init__(self, port): ## @brief Verifies the logic link between the printer and host. # @details Sends a DLE EOT (Real Time Status) command to check if the printer is responsive and online. # @return String description of connection status. - # @see [Phoenix Real Time Status Documentation](https://escpos.readthedocs.io/en/latest/phoenix_status.html#p1004) + # @see [phoenix Real Time Status Documentation](https://escpos.readthedocs.io/en/latest/phoenix_status.html#p1004) # @note A response of 0xAC typically indicates a hardware communication error such as incorrect parity or a wiring fault. def verify_logic_link(self): self.ser.reset_input_buffer() diff --git a/py_esc_pos/printer/reliance_printer.py b/py_esc_pos/printer/reliance_printer.py new file mode 100644 index 0000000..ba5dead --- /dev/null +++ b/py_esc_pos/printer/reliance_printer.py @@ -0,0 +1,25 @@ +from py_esc_pos.printer import base_printer +from py_esc_pos.commands import RelianceCommands +import time + +class ReliancePrinter(base_printer.BasePrinter): + def __init__(self, port): + super().__init__(port, 9600) + self.printer_type = "ReliancePrinter" + + time.sleep(1) + self.ser.reset_input_buffer() + + def verify_logic_link(self): + self.ser.reset_input_buffer() + self.ser.write(RelianceCommands.REAL_TIME_STATUS + RelianceCommands.RT_OFFLINE) + time.sleep(0.25) + + if self.ser.in_waiting > 0: + res = self.ser.read(1)[0] + if res == 0xAC: + return "CONNECTED_BUT_MANGLED (Check Parity/Stop Bits)" + + is_online = (res & 0x08) == 0x08 + return "ONLINE" if is_online else "OFFLINE" + return "NO_RESPONSE" \ No newline at end of file diff --git a/Sample_Codes/Phoenix/1_sample_connection.py b/sample_codes/phoenix/1_sample_connection.py similarity index 73% rename from Sample_Codes/Phoenix/1_sample_connection.py rename to sample_codes/phoenix/1_sample_connection.py index b2b05d4..1741301 100644 --- a/Sample_Codes/Phoenix/1_sample_connection.py +++ b/sample_codes/phoenix/1_sample_connection.py @@ -1,6 +1,7 @@ # @brief This sample demonstrates how to establish a link with the Phoenix printer. -from Printer.phoenix_printer import PhoenixPrinter -from Menu.util import find_port + +from py_esc_pos.printer.phoenix_printer import PhoenixPrinter +from py_esc_pos.menu.util import find_port ## Connects to the first available printer and checks status. # @returns A PhoenixPrinter instance if successful. @@ -13,4 +14,7 @@ def simple_connect(): # verify_logic_link is the 'handshake' status = printer.verify_logic_link() print(f"Printer Status: {status}") - return printer \ No newline at end of file + return printer + +if __name__ == "__main__": + simple_connect() \ No newline at end of file diff --git a/Sample_Codes/Phoenix/2_sample_print_command.py b/sample_codes/phoenix/2_sample_print_command.py similarity index 68% rename from Sample_Codes/Phoenix/2_sample_print_command.py rename to sample_codes/phoenix/2_sample_print_command.py index b3ff297..b018e8b 100644 --- a/Sample_Codes/Phoenix/2_sample_print_command.py +++ b/sample_codes/phoenix/2_sample_print_command.py @@ -1,33 +1,31 @@ # @brief Basic Command Execution for Phoenix Printers. # @details This sample demonstrates how to initialize the printer, print text, and trigger a full cut using ESC/POS constants. -from Printer.phoenix_printer import PhoenixPrinter -from commands import PhoenixCommands -from Menu.util import find_port +from py_esc_pos.printer.phoenix_printer import PhoenixPrinter +from py_esc_pos.commands import PhoenixCommands +from py_esc_pos.menu.util import find_port ## Illustrates the standard "Initialize -> Action -> Cut" workflow. # @see [Phoenix Paper Movement Commands](https://escpos.readthedocs.io/en/latest/paper_movement.html) def run_basic_print(): - # 1. Setup Connection ports = find_port() if not ports: print("No printer found.") return - printer = PhoenixPrinter(ports[1].device) + # Use the first detected printer port by default. + printer = PhoenixPrinter(ports[0].device) try: - # 2. Initialize (ESC @) - # Always good practice to clear the buffer and reset settings - print("Initializing printer...") + # Initialize printer.send_command(PhoenixCommands.INIT) - # 3. Send Text Data + # Send Text Data print("Sending text...") printer.send_command(b"Phoenix Sample Print\n") printer.send_command(b"--------------------\n") - printer.send_command(b"SWT-189: Command Sample\n\n") + printer.send_command(b"Command Sample\n\n") - # 4. Feed and Cut (GS V) + # Feed and Cut (GS V) # We feed a bit of paper so the text clears the cutter blade print("Cutting paper...") printer.send_command(PhoenixCommands.FULL_CUT) diff --git a/Sample_Codes/Phoenix/3_sample_font_control.py b/sample_codes/phoenix/3_sample_font_control.py similarity index 55% rename from Sample_Codes/Phoenix/3_sample_font_control.py rename to sample_codes/phoenix/3_sample_font_control.py index f14ad70..049e428 100644 --- a/Sample_Codes/Phoenix/3_sample_font_control.py +++ b/sample_codes/phoenix/3_sample_font_control.py @@ -1,17 +1,10 @@ # @brief Demonstrates font styling and selection on Phoenix printers. -# @details This sample covers bold, underline, and switching between Font A (12x24) and Font B (9x17). +# @details This sample covers bold, underline, and switching between Font A (12x24), Font C (24x48), and (16x24). # @see [Font Controlling Commands](https://escpos.readthedocs.io/en/latest/font_cmds.html) -import sys -import os - -# Ensure project root is in path for Printer and commands modules -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))) - -from Printer.phoenix_printer import PhoenixPrinter -from commands import PhoenixCommands -from Menu.util import find_port - +from py_esc_pos.printer.phoenix_printer import PhoenixPrinter +from py_esc_pos.commands import PhoenixCommands +from py_esc_pos.menu.util import find_port def run_font_sample(): ports = find_port() @@ -19,47 +12,51 @@ def run_font_sample(): print("No printer found.") return - printer = PhoenixPrinter(ports[1].device) + # Use the first detected printer port by default. + printer = PhoenixPrinter(ports[0].device) try: # Initialize printer.send_command(PhoenixCommands.INIT) - # 1. Emphasis (Bold) Mode - # @see https://escpos.readthedocs.io/en/latest/font_cmds.html#emphasis-mode-1b-45 + # Emphasis (Bold) Mode + # @see [Phoenix Emphasis Command] (https://escpos.readthedocs.io/en/latest/font_cmds.html#emphasis-mode-1b-45) printer.send_command(b"Standard Text") printer.send_command(PhoenixCommands.EMPHASIS_MODE + PhoenixCommands.ON) printer.send_command(b"Emphasized (Bold) Text") printer.send_command(PhoenixCommands.LINE_FEED) printer.send_command(PhoenixCommands.EMPHASIS_MODE + PhoenixCommands.OFF) - # 2. Underline Mode - # @see https://escpos.readthedocs.io/en/latest/font_cmds.html#underline-mode-1b-2d + # Underline Mode + # @see [Phoenix Underline Command] (https://escpos.readthedocs.io/en/latest/font_cmds.html#underline-mode-1b-2d) printer.send_command(PhoenixCommands.UNDERLINE_MODE + PhoenixCommands.ON) printer.send_command(b"Underlined Text") printer.send_command(PhoenixCommands.LINE_FEED) printer.send_command(PhoenixCommands.UNDERLINE_MODE + PhoenixCommands.OFF) - # 3. Font Selection - # Font A is standard (12w x 24h), Font B is condensed (9w x 17h) - # @see https://escpos.readthedocs.io/en/latest/font_cmds.html#select-character-font-1b-4d + # Font Selection + # Font A is standard (12w x 24h), Font B is expanded (24w x 48h), Font C is wider (16w x 24h) + # @see [Phoenix Font A Command] (https://escpos.readthedocs.io/en/latest/font_cmds.html#select-character-font-1b-4d) printer.send_command(b"This is Font A") printer.send_command(PhoenixCommands.LINE_FEED) - printer.send_command(PhoenixCommands.SELECT_FONT_B) - printer.send_command(b"This is Font B (Condensed)") + printer.send_command(PhoenixCommands.SELECT_FONT_C) + printer.send_command(b"This is Font B (Expanded)") + printer.send_command(PhoenixCommands.LINE_FEED) + printer.send_command(PhoenixCommands.SELECT_FONT_D) + printer.send_command(b"This is Font C (Wider)") printer.send_command(PhoenixCommands.LINE_FEED) printer.send_command(PhoenixCommands.SELECT_FONT_A) + printer.send_command(b"Back to Font A") + printer.send_command(PhoenixCommands.LINE_FEED) # Feed and Cut printer.send_command(PhoenixCommands.LINE_FEED * 2) # Feed some lines to clear the cutter printer.send_command(PhoenixCommands.FULL_CUT) - print("Font sample sent successfully.") except Exception as e: print(f"Error: {e}") finally: printer.close() - if __name__ == "__main__": run_font_sample() \ No newline at end of file diff --git a/Sample_Codes/Phoenix/4_sample_positioning.py b/sample_codes/phoenix/4_sample_positioning.py similarity index 67% rename from Sample_Codes/Phoenix/4_sample_positioning.py rename to sample_codes/phoenix/4_sample_positioning.py index a5f5b74..e446ba1 100644 --- a/Sample_Codes/Phoenix/4_sample_positioning.py +++ b/sample_codes/phoenix/4_sample_positioning.py @@ -1,35 +1,25 @@ # @brief Demonstrates supported positioning and alignment for Phoenix printers. # @details Focuses on Justification (Left/Center/Right) and Line Spacing, -# as Phoenix uses these for layout over coordinate-based positioning. -# @see [Layout Commands](https://escpos.readthedocs.io/en/latest/layout_cmds.html) - -import sys -import os - -# Ensure project root is in path -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))) - -from Printer.phoenix_printer import PhoenixPrinter -from commands import PhoenixCommands -from Menu.util import find_port - +# as phoenix uses these for layout over coordinate-based positioning. +# @see [Images and Barcode Commands](https://escpos.readthedocs.io/en/latest/layout_cmds.html) +from py_esc_pos.printer.phoenix_printer import PhoenixPrinter +from py_esc_pos.commands import PhoenixCommands +from py_esc_pos.menu.util import find_port def run_phoenix_positioning(): ports = find_port() if not ports: print("No printer found.") return - printer = PhoenixPrinter(ports[1].device) + # Use the first detected port by default so the sample works when only one printer port is available. + printer = PhoenixPrinter(ports[0].device) try: printer.send_command(PhoenixCommands.INIT) - # 1. Justification (ESC a n) - # @see https://escpos.readthedocs.io/en/latest/layout_cmds.html#select-justification-1b-61 - print("Testing Justification...") - # Left (Default) + # @see [Phoenix Justification Commands] (https://escpos.readthedocs.io/en/latest/layout.html#b61) printer.send_command(PhoenixCommands.SELECT_JUSTIFICATION + PhoenixCommands.LEFT) printer.send_command(b"Left Aligned Text\n") @@ -51,7 +41,6 @@ def run_phoenix_positioning(): # Final Cut printer.send_command(PhoenixCommands.FULL_CUT) - print("Phoenix positioning sample sent.") except Exception as e: print(f"Error: {e}") diff --git a/sample_codes/phoenix/5_sample_image_barcode.py b/sample_codes/phoenix/5_sample_image_barcode.py new file mode 100644 index 0000000..8cd45e2 --- /dev/null +++ b/sample_codes/phoenix/5_sample_image_barcode.py @@ -0,0 +1,69 @@ +# @brief Demonstrates image and barcode generation for Phoenix printers. +# @details Covers 2D Barcodes (QR Codes) and Raster Image printing. +# @see [Images and Barcode Commands](https://escpos.readthedocs.io/en/latest/imaging.html) + +from py_esc_pos.printer.phoenix_printer import PhoenixPrinter +from py_esc_pos.commands import PhoenixCommands +from py_esc_pos.menu.util import find_port, get_raster_blob + +IMAGE_PATH = r"sample_image.png" # Replace with your image path + +def run_image_barcode_sample(): + ports = find_port() + if not ports: + print("No printer found.") + return + + # Use the first detected port by default so the sample works when only one printer port is available. + printer = PhoenixPrinter(ports[0].device) + + try: + raster_blob = get_raster_blob(IMAGE_PATH) + qr_data = "https://pyramidacceptors.com" + # pL calculation: Data length (28) + 3 overhead bytes (cn, fn, m) = 31 (0x1F) + pL = bytes([len(qr_data) + 3]) + pH = b"\x00" + pL2 = b"\03" + pH2 = b"\x00" + + # Store the data (Function 180) + # cn=49 (0x31), fn=80 (0x50), m=48 (0x31) + function180 = b"\x31\x50\x31" + # Print the stored data (Function 181) + # cn=49 (0x31), fn=81 (0x51), m=48 (0x31) + function181 = b"\x31\x51\x31" + + # --- RASTER IMAGE SAMPLES --- + # @see [Raster Image](https://escpos.readthedocs.io/en/latest/imaging.html#raster-image-1d-76-30-m-xl-xh-yl-yh-d1-dk-rel-phx) + printer.send_command(b"PRINTING RASTER IMAGE:\n") + printer.send_command(PhoenixCommands.LINE_FEED) + printer.send_command(raster_blob) + printer.send_command(PhoenixCommands.LINE_FEED) + printer.send_command(b"Raster image printed successfully.\n") + printer.send_command(PhoenixCommands.LINE_FEED) + + # --- 2D BARCODE (QR CODE) SAMPLES --- + # @see [Dynamic 2D Barcode](https://escpos.readthedocs.io/en/latest/imaging.html#dynamic-2d-barcode-1d-28-6b-phx) + printer.send_command(PhoenixCommands.INIT) + printer.send_command(b"2D BARCODE (QR CODE):\n") + + # Send the commands to store and print the QR code + printer.send_command(PhoenixCommands.DYNAMIC_2D_BARCODE) + printer.send_command(pL + pH) + printer.send_command(function180) + printer.send_command(qr_data.encode('utf-8')) + + # The printer will print the stored QR code when it receives the print command. + printer.send_command(PhoenixCommands.DYNAMIC_2D_BARCODE) + printer.send_command(pL2 + pH2) + printer.send_command(function181) + printer.send_command(PhoenixCommands.LINE_FEED) + printer.send_command(b"Complete QR code sample\n") + printer.send_command(PhoenixCommands.LINE_FEED * 2) # Feed some lines to clear the cutter + printer.send_command(PhoenixCommands.FULL_CUT) + + except Exception as e: + print(f"Error: {e}") + +if __name__ == "__main__": + run_image_barcode_sample() \ No newline at end of file diff --git a/sample_codes/reliance/1_sample_connection.py b/sample_codes/reliance/1_sample_connection.py new file mode 100644 index 0000000..122b9f4 --- /dev/null +++ b/sample_codes/reliance/1_sample_connection.py @@ -0,0 +1,19 @@ +# @brief This sample demonstrates how to establish a link with the Reliance printer. + +from py_esc_pos.printer.reliance_printer import ReliancePrinter +from py_esc_pos.menu.util import find_port +## Connects to the first available printer and checks status. +# @returns A ReliancePrinter instance if successful. +def simple_connect(): + ports = find_port() + if ports: + # Initialize printer on the first found port + printer = ReliancePrinter(ports[0].device) + + # verify_logic_link is the 'handshake' + status = printer.verify_logic_link() + print(f"Printer Status: {status}") + return printer + +if __name__ == "__main__": + simple_connect() \ No newline at end of file diff --git a/sample_codes/reliance/2_sample_print_command.py b/sample_codes/reliance/2_sample_print_command.py new file mode 100644 index 0000000..25e35bf --- /dev/null +++ b/sample_codes/reliance/2_sample_print_command.py @@ -0,0 +1,40 @@ +# @brief Basic Command Execution for Reliance printers. +# @details This sample demonstrates how to initialize the printer, print text, and trigger a full cut using ESC/POS constants. + +from py_esc_pos.printer.reliance_printer import ReliancePrinter +from py_esc_pos.commands import RelianceCommands +from py_esc_pos.menu.util import find_port +## Illustrates the standard "Initialize -> Action -> Cut" workflow. +# @see [Reliance Paper Movement Commands](https://escpos.readthedocs.io/en/latest/paper_movement.html) +def run_basic_print(): + ports = find_port() + if not ports: + print("No printer found.") + return + + # Use the correct port index based on your setup. Here we use ports[0] as in previous samples. + printer = ReliancePrinter(ports[0].device) + + try: + # Initialize + printer.send_command(RelianceCommands.INIT) + + # Send Text Data + print("Sending text...") + printer.send_command(b"Reliance Sample Print\n") + printer.send_command(b"--------------------\n\n\n\n\n") + printer.send_command(b"Command Sample\n\n") + + # Cut and Eject Paper + # @see [Reliance Ejector Commands](https://escpos.readthedocs.io/en/latest/paper_movement.html#ejector-1d-65-rel) + print("Cutting paper...") + printer.send_command(RelianceCommands.EJECTOR +b'\x05') + + + except Exception as e: + print(f"Failed to send command: {e}") + finally: + printer.close() + +if __name__ == "__main__": + run_basic_print() \ No newline at end of file diff --git a/sample_codes/reliance/3_sample_font_control.py b/sample_codes/reliance/3_sample_font_control.py new file mode 100644 index 0000000..52433ad --- /dev/null +++ b/sample_codes/reliance/3_sample_font_control.py @@ -0,0 +1,85 @@ +## @brief Demonstrates font styling and orientation for Reliance printers. +# @details Focuses on text rotation (90° and 180°), reverse printing, and style combinations. +# @see Font Controlling Commands + +from py_esc_pos.menu.util import find_port +from py_esc_pos.printer.reliance_printer import ReliancePrinter +from py_esc_pos.commands import RelianceCommands +def run_font_orientation_sample(): + ports = find_port() + if not ports: + print("No printer found.") + return + + printer = ReliancePrinter(ports[0].device) + + try: + # Initialize + printer.send_command(RelianceCommands.INIT) + + # --- NORMAL ORIENTATION & STYLES --- + printer.send_command(b"NORMAL ORIENTATION:\n") + printer.send_command(b"Standard Text\n") + + # Bold (Emphasis) + # @see [Reliance Emphasis Commands] (https://escpos.readthedocs.io/en/latest/font_cmds.html#emphasis-mode-1b-45-rel-phx) + printer.send_command(RelianceCommands.EMPHASIS_MODE + b'\x01') + printer.send_command(b"Bold Text\n") + printer.send_command(RelianceCommands.EMPHASIS_MODE + b'\x00') + + # Underline + # @see [Reliance Underline Commands] (https://escpos.readthedocs.io/en/latest/font_cmds.html#underline-mode-1b-2d-rel-phx) + printer.send_command(RelianceCommands.UNDERLINE_MODE + b'\x01') + printer.send_command(b"Underlined Text\n") + printer.send_command(RelianceCommands.UNDERLINE_MODE + b'\x00') + + # --- 90 DEGREE ROTATION --- + # @see [Reliance Rotation Commands](https://escpos.readthedocs.io/en/latest/font_cmds.html#rotation-1b-56-rel) + printer.send_command(b"\nROTATION SAMPLES:\n") + + # Turn 90° Clockwise ON + # @see [Reliance Rotate 90° Commands](https://escpos.readthedocs.io/en/latest/font_cmds.html#rotation-1b-56-rel) + printer.send_command(RelianceCommands.ROTATE_90_DEGREES + b'\x01') + printer.send_command(b"Rotated 90 Degrees\n") + # Turn 90° Clockwise OFF + printer.send_command(RelianceCommands.ROTATE_90_DEGREES + b'\x00') + + # --- UPSIDE DOWN MODE (180°) --- + # @see [Reliance Upside Down Commands](https://escpos.readthedocs.io/en/latest/font_cmds.html#upside-down-mode-1b-7b-rel) + printer.send_command(b"\nUPSIDE DOWN SAMPLE:\n") + + # Turn Upside Down ON (Effective at the start of a line) + printer.send_command(RelianceCommands.UPSIDE_DOWN_MODE + b'\x01') + printer.send_command(b"This is Upside Down\n") + # Turn Upside Down OFF + printer.send_command(RelianceCommands.UPSIDE_DOWN_MODE + b'\x00') + + # --- REVERSE PRINT (WHITE ON BLACK) --- + # @see [Reliance Reverse Print Commands](https://escpos.readthedocs.io/en/latest/font_cmds.html#reverse-print-mode-1d-42-rel-phx) + printer.send_command(b"\nREVERSE MODE:\n") + printer.send_command(RelianceCommands.REVERSE_PRINT_MODE + b'\x01') + printer.send_command(b" WHITE ON BLACK \n") + printer.send_command(RelianceCommands.REVERSE_PRINT_MODE + b'\x00') + + # --- COMBINING SIZE & ROTATION --- + printer.send_command(b"\nLARGE ROTATED:\n") + # Double Width + Double Height + printer.send_command(RelianceCommands.SELECT_CHAR_SIZE + b'\x11') + printer.send_command(RelianceCommands.ROTATE_90_DEGREES + b'\x01') + printer.send_command(b"BIG ROT\n") + + # Reset to Default + printer.send_command(RelianceCommands.INIT) + + # Eject and Cut + printer.send_command(b"\n\nFont Orientation Complete\n\n") + printer.send_command(RelianceCommands.EJECTOR + b'\x05') + + except Exception as e: + print(f"Font sample failed: {e}") + finally: + printer.close() + + +if __name__ == "__main__": + run_font_orientation_sample() \ No newline at end of file diff --git a/sample_codes/reliance/4_sample_layout_control.py b/sample_codes/reliance/4_sample_layout_control.py new file mode 100644 index 0000000..cfe2396 --- /dev/null +++ b/sample_codes/reliance/4_sample_layout_control.py @@ -0,0 +1,92 @@ +## @brief Demonstrates layout and positioning for Reliance printers. +# @details Focuses on Justification, Line Spacing, Margins, and Character Spacing. +# Reliance printers support advanced layout control via motion units. +# @see [Layout Commands](https://escpos.readthedocs.io/en/latest/layout.html) + +from py_esc_pos.menu.util import find_port +from py_esc_pos.printer.reliance_printer import ReliancePrinter +from py_esc_pos.commands import RelianceCommands +def run_layout_sample(): + ports = find_port() + if not ports: + print("No printer found.") + return + + printer = ReliancePrinter(ports[0].device) + + try: + # Initialize + printer.send_command(RelianceCommands.INIT) + + # --- JUSTIFICATION SAMPLES --- + # @see [Reliance Justification Commands](https://escpos.readthedocs.io/en/latest/layout.html#select-justification-1b-61-rel-phx) + printer.send_command(b"JUSTIFICATION:\n") + + # Left (Default) + printer.send_command(RelianceCommands.SELECT_JUSTIFICATION + RelianceCommands.LEFT) + printer.send_command(b"This is Left Aligned\n") + + # Center + printer.send_command(RelianceCommands.SELECT_JUSTIFICATION + RelianceCommands.CENTER) + printer.send_command(b"This is Centered\n") + + # Right + printer.send_command(RelianceCommands.SELECT_JUSTIFICATION + RelianceCommands.RIGHT) + printer.send_command(b"This is Right Aligned\n\n") + + # Reset to Left for further tests + printer.send_command(RelianceCommands.SELECT_JUSTIFICATION + RelianceCommands.LEFT) + + # --- LINE SPACING SAMPLES --- + printer.send_command(b"LINE SPACING:\n") + + # Tight Spacing (1/8 inch) + # @see [Reliance Line Spacing Commands](https://escpos.readthedocs.io/en/latest/layout.html#select-1-8-inch-line-spacing-1b-30-rel) + printer.send_command(RelianceCommands.SELECT_1_8_INCH_LINE_SPACING) + printer.send_command(b"Line 1: 1/8 inch spacing\n") + printer.send_command(b"Line 2: 1/8 inch spacing\n") + + # Standard Spacing (1/6 inch) + # @see [Reliance Line Spacing Commands](https://escpos.readthedocs.io/en/latest/layout.html#select-1-6-inch-line-spacing-1b-32-rel) + printer.send_command(RelianceCommands.SELECT_1_6_INCH_LINE_SPACING) + printer.send_command(b"Line 3: 1/6 inch spacing\n") + + # Custom Large Spacing (ESC 3 n) - Let's set n=100 + # @see [Reliance Line Spacing Commands](https://escpos.readthedocs.io/en/latest/layout.html#line-spacing-1b-33-rel) + printer.send_command(RelianceCommands.LINE_SPACING + b'\x64') + printer.send_command(b"Line 4: Custom Large Spacing\n") + + # Back to default + printer.send_command(RelianceCommands.SELECT_1_6_INCH_LINE_SPACING) + printer.send_command(b"\n") + + # --- MARGINS & CHARACTER SPACING --- + printer.send_command(b"MARGINS & SPACING:\n") + + # Right Side Character Spacing (ESC SP n) - Adds space between characters + # Setting n=4 + # @see [Reliance Character Spacing Commands](https://escpos.readthedocs.io/en/latest/layout.html#right-side-character-spacing-1b-20-rel) + printer.send_command(RelianceCommands.RIGHT_SIDE_CHAR_SPACING + b'\x04') + printer.send_command(b"Widened Character Spacing\n") + printer.send_command(RelianceCommands.RIGHT_SIDE_CHAR_SPACING + b'\x00') # Reset + + # Left Margin (GS L nL nH) + # To set a ~1-inch margin (depending on motion units), we use nL=80, nH=0 + # @see [Reliance Left Margin Commands](https://escpos.readthedocs.io/en/latest/layout.html#left-margin-1d-4c-rel) + printer.send_command(RelianceCommands.LEFT_MARGIN + b'\x50\x00') + printer.send_command(b"This text has a left margin.\n") + + # Reset to Default + printer.send_command(RelianceCommands.INIT) + + # Eject and Cut + printer.send_command(b"\n\nLayout Sample Complete\n\n") + printer.send_command(RelianceCommands.EJECTOR + b'\x05') + + except Exception as e: + print(f"Layout command failed: {e}") + finally: + printer.close() + +if __name__ == "__main__": + run_layout_sample() \ No newline at end of file diff --git a/sample_codes/reliance/5_sample_image_barcode.py b/sample_codes/reliance/5_sample_image_barcode.py new file mode 100644 index 0000000..36d4cbc --- /dev/null +++ b/sample_codes/reliance/5_sample_image_barcode.py @@ -0,0 +1,82 @@ +## @brief Demonstrates image and barcode generation for Reliance printers. +# @details Covers 1D Barcodes, 2D Barcodes (QR Codes), and Raster Image printing. +# @see [Images and Barcode Commands](https://escpos.readthedocs.io/en/latest/imaging.html#) + +from py_esc_pos.menu.util import find_port, get_raster_blob +from py_esc_pos.printer.reliance_printer import ReliancePrinter +from py_esc_pos.commands import RelianceCommands + +IMAGE_PATH = r"sample_image.png" # Replace with your image path + +def run_imaging_sample(): + ports = find_port() + if not ports: + print("No printer found.") + return + + # Use the correct port index based on your setup. + printer = ReliancePrinter(ports[0].device) + + try: + # 2. Initialize + print("Initializing printer...") + printer.send_command(RelianceCommands.INIT) + + # --- 1D BARCODE SAMPLES --- + # @see [Barcode Generator](https://escpos.readthedocs.io/en/latest/imaging.html#barcode-generator-1-1d-6b-m-d1-dk-00-rel) + printer.send_command(b"1D BARCODE (CODE 39):\n") + + # Set Barcode Height to 100 dots and Width Multiplier to 2 + printer.send_command(RelianceCommands.SET_1D_BARCODE_HEIGHT + b'\x64') + printer.send_command(RelianceCommands.SET_1D_BARCODE_WIDTH_MULT + b'\x02') + + # Set HRI (Human Readable Interpretation) to print below the barcode + printer.send_command(RelianceCommands.SET_HRI_PRINTING_POSITION + b'\x02') + + # Print Code 39 Barcode (System m=4, ends with NULL) + printer.send_command(RelianceCommands.BARCODE_GENERATOR + b'\x04' + b"RELIANCE123" + b'\x00') + printer.send_command(b"\n\n") + + # --- 2D BARCODE (QR CODE) SAMPLES --- + # @see [2D Barcode Generator](https://escpos.readthedocs.io/en/latest/imaging.html#d-barcode-generator-1c-7d-25-k-d1-dk-rel) + printer.send_command(b"2D BARCODE (QR CODE):\n") + + # Set QR Code size (3 to 8 dots per cell) + # @see [Set 2D Barcode Size](https://escpos.readthedocs.io/en/latest/imaging.html#set-2d-barcode-size-1c-7d-74-k-rel) + printer.send_command(RelianceCommands.SET_2D_BARCODE_SIZE + b'\x06') + + # Command must be enclosed by Line Feeds + qr_data = b"https://pyramidacceptors.com" + qr_len = len(qr_data).to_bytes(1, 'little') + + printer.send_command(RelianceCommands.LINE_FEED) + printer.send_command(RelianceCommands.BARCODE_GENERATOR_2D + qr_len + qr_data) + printer.send_command(RelianceCommands.LINE_FEED) + printer.send_command(b"\n") + + # --- RASTER IMAGE SAMPLES --- + # @see [Raster Image](https://escpos.readthedocs.io/en/latest/imaging.html#raster-image-1d-76-30-m-xl-xh-yl-yh-d1-dk-rel-phx) + printer.send_command(b"IMAGE FROM FILE:\n") + try: + # Generate the blob using the helper function + image_blob = get_raster_blob(IMAGE_PATH, printer_width_pixels=384) + printer.send_command(image_blob) + except Exception as img_err: + print(f"Image processing failed: {img_err}") + printer.send_command(b"[Image Load Error]\n") + + # 3. Finalization + # Reset to Default + printer.send_command(RelianceCommands.INIT) + + # 4. Cut and Eject Paper + printer.send_command(b"\n\nImaging Sample Complete\n\n") + printer.send_command(RelianceCommands.EJECTOR + b'\x05') + + except Exception as e: + print(f"Imaging command failed: {e}") + finally: + printer.close() + +if __name__ == "__main__": + run_imaging_sample() \ No newline at end of file diff --git a/sample_codes/reliance/6_sample_print_logo.py b/sample_codes/reliance/6_sample_print_logo.py new file mode 100644 index 0000000..aac946f --- /dev/null +++ b/sample_codes/reliance/6_sample_print_logo.py @@ -0,0 +1,55 @@ +## @brief Demonstrates both Standard and Simplified logo printing for Reliance. +# @details Standard allows for partial logo printing, while Simplified uses flash dimensions. +# @see [Images and Barcode](https://escpos.readthedocs.io/en/latest/imaging.html#images-and-barcode) + +from py_esc_pos.printer.reliance_printer import ReliancePrinter +from py_esc_pos.commands import RelianceCommands +from py_esc_pos.menu.util import find_port + +def run_logo_print(): + ports = find_port() + if not ports: + print("No printer found.") + return + + printer = ReliancePrinter(ports[0].device) + + try: + # Initialize + printer.send_command(RelianceCommands.INIT) + + # --- METHOD 1: PRINT LOGO (SIMPLIFIED) --- + # Prints logo 'n' using the dimensions already stored in flash. + # @see [Print Graphic Bank/Logo (Simplified)](https://escpos.readthedocs.io/en/latest/imaging.html#print-graphic-bank-logo-simplified-1c-79-rel) + logo_index = b'\x01' + reserved = b'\x00' # Must be zero + + printer.send_command(b"Logo (Simplified):\n") + printer.send_command(RelianceCommands.PRINT_GRAPHIC_BANK_LOGO_SIMPLIFIED + logo_index + reserved) + printer.send_command(b"End of Logo (Simplified)\n") + printer.send_command(RelianceCommands.LINE_FEED * 2) + + # --- METHOD 2: PRINT LOGO (STANDARD) --- + # Format: n + xH + xL + yH + yL + # xH/xL: Starting dotline (0 in this case) + # yH/yL: Number of dotlines to print (200 in this case, hex 0xC8) + # @see [Print Graphic Bank/Logo](https://escpos.readthedocs.io/en/latest/imaging.html#print-graphic-bank-logo-1b-fa) + start_line = b'\x00\x00' # xH, xL + line_count = b'\x00\xc8' # yH, yL (200 dots) + + printer.send_command(b"Logo (Standard):\n") + printer.send_command(RelianceCommands.PRINT_GRAPHIC_BANK_LOGO + logo_index + start_line + line_count) + printer.send_command(b"End of Logo (Standard)\n") + + # Cut and Eject Paper + printer.send_command(RelianceCommands.LINE_FEED * 4) + printer.send_command(RelianceCommands.EJECTOR + b'\x05') + print("Logo samples sent successfully.") + + except Exception as e: + print(f"Failed to send command: {e}") + finally: + printer.close() + +if __name__ == "__main__": + run_logo_print() \ No newline at end of file