Interaction Design History Spring 2026 | Week 4 | March 30, 2026

Tangible Output — Hacking the Receipt Printer

Agenda Overview

Lecture: ESC/POS and the Magic of Hardware

Technical Setup

Before we break into groups, everyone needs to set up their Python environment to talk to our Rongta RP326 printers. We'll be using a lightning-fast package manager called uv to handle our libraries automatically.

Step 1: Install uv (Mac, Linux, & Windows)

Open your terminal and install the uv tool. Choose the command for your operating system:

Step 2: Mac Users ONLY - System Drivers & Camera Permissions

If you are on a Mac, you need a system-level library to talk to USB devices. Run this in your terminal (requires Homebrew):

brew install libusb

Important: Mac users must also explicitly grant their Terminal application (or VS Code) Camera permissions by going to System Settings > Privacy & Security > Camera.

Step 3: Find Your USB Vendor & Product IDs

To let Python talk to the printer, you need its exact hardware address.

Step 4: Create the Script

Create a file named photobooth.py in your project folder and copy in this starter code. Don't forget to update the VENDOR_ID and PRODUCT_ID!

# /// script
# requires-python = ">=3.9"
# dependencies = [
#     "opencv-python",
#     "pillow",
#     "python-escpos",
#     "pyusb",
# ]
# ///

import cv2
from PIL import Image
from escpos.printer import Usb

# ==========================================
# 1. SETUP THE PRINTER
# ==========================================
# Students: Replace 0xXXXX with your specific Vendor and Product IDs!
VENDOR_ID = 0x0fe6  # Example ID, needs to be updated
PRODUCT_ID = 0x811e # Example ID, needs to be updated

try:
    print("Connecting to Rongta RP326...")
    p = Usb(idVendor=VENDOR_ID, idProduct=PRODUCT_ID, profile="RP326")
    print("Printer connected successfully!")
except Exception as e:
    print(f"Printer connection failed: {e}")
    print("Check your USB IDs and make sure the printer is turned on!")
    exit()

# ==========================================
# 2. SETUP THE WEBCAM
# ==========================================
# '0' is usually the default built-in laptop webcam. 
cap = cv2.VideoCapture(0)

print("Webcam active. Press SPACEBAR to print a photo. Press ESC to quit.")

while True:
    ret, frame = cap.read()
    if not ret:
        print("Failed to grab frame from camera.")
        break

    cv2.imshow("Thermal Photobooth - Press SPACE to print!", frame)
    key = cv2.waitKey(1) & 0xFF

    # ==========================================
    # 3. THE PRINT LOGIC (Triggered by Spacebar)
    # ==========================================
    if key == 32:  # ASCII code for Spacebar
        print("SNAP! Processing image...")
        
        rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        pil_img = Image.fromarray(rgb_frame)
        
        # --- IMAGE PROCESSING FOR THERMAL PRINTERS ---
        # 80mm receipt paper is exactly 512 pixels wide. 
        target_width = 512
        width_percent = (target_width / float(pil_img.size[0]))
        target_height = int((float(pil_img.size[1]) * float(width_percent)))
        
        resized_img = pil_img.resize((target_width, target_height), Image.Resampling.LANCZOS)
        
        # Convert to 1-bit monochrome (pure black and white) with dithering
        dithered_img = resized_img.convert('1')
        dithered_img.save("last_photo.jpg")
        
        # Send to printer
        print("Printing...")
        p.image("last_photo.jpg")
        p.text("\n--- Thanks for stopping by! ---\n\n\n")
        p.cut()
        print("Done! Ready for the next one.")

    # ==========================================
    # 4. QUIT LOGIC (Triggered by ESC)
    # ==========================================
    elif key == 27:
        print("Closing photobooth...")
        break

cap.release()
cv2.destroyAllWindows()
Step 5: Run the Photobooth

Because our script includes the dependencies at the very top, you don't need to manually install OpenCV or Pillow! Just navigate to your project folder in the terminal and run the script with uv:

uv run photobooth.py

It will automatically create an environment, download the libraries in milliseconds, and launch your webcam.


15 Minute Break


Group Activity: Iterating the 1-Bit Photobooth

Now that everyone has a working base script, we are breaking up into 3 groups. Pair program to modify the code and create a unique experience based on one of the prompts below:

Prototyping Prompts

Reading & Design Review

Homework