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.
Open your terminal and install the uv tool. Choose the command for your operating system:
curl -LsSf https://astral.sh/uv/install.sh | sh
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
pip install uv
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.
To let Python talk to the printer, you need its exact hardware address.
0x0fe6).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()
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.
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:
p.image() function. Break the webcam frame down into a tiny grid, calculate the average brightness of each "block", and map that brightness to a specific ASCII character (e.g., # for dark, . for light). Print a portrait entirely out of text characters!