# POS_PRINT.md — Printing to the Epson TM-m30

Quick-reference for any agent or script that needs to print arbitrary content on the receipt printer.

USER may explicitly use this file in lieu of a formal request to print.

## Printer Details

Exact model reference: `https://www.amazon.com/dp/B01GO08MW2`

### Core

| Field | Value |
|---|---|
| Model | Epson TM-m30 |
| Print method | Thermal |
| Print speed | 200 mm/second |
| Print resolution | 203 dpi |
| Print direction | Vertical and horizontal |
| Protocol used here | ESC/POS over raw TCP |
| IP | `192.168.0.137` |
| Port | `9100` |
| Profile | `default` (python-escpos) |

### Fonts and characters

| Field | Value |
|---|---|
| Fonts / columns | Font A: 12 x 24, 48 cpl (default); Font B: 10 x 24, 57 cpl; Font C: 9 x 17, 64 cpl |
| Character size | Font A: 1.25 x 3.00 mm; Font B: 1.13 x 3.00 mm; Font C: 0.88 x 2.13 mm |
| Character set | 95 alphanumeric, 18 international, 128 x 43 |

### Barcode and 2D symbols

| Field | Value |
|---|---|
| Barcode | UPC-A, UPC-E, JAN8 / EAN8, JAN13 / EAN13, Code39, Code93, Code128, ITF, CODABAR (NW-7), GS1-128, GS1 DataBar |
| 2D symbols | PDF417, QR Code, MaxiCode, Data Matrix, Aztec Code, 2D GS1 DataBar, Composite Symbology |

### Media handling

| Field | Value |
|---|---|
| Type | Thermal roll paper |
| Paper feed method | Friction feed |
| Width | 3.12 in / 79.5 mm |
| Paper loading | Drop-in |
| Roll diameter (max) | 3.27 in / 83 mm |
| Core diameter (min) | 0.71 in / 18 mm |
| Paper guide | Yes |
| Auto cutter | Yes |

### Connectivity

| Field | Value |
|---|---|
| Ethernet model | Built-in USB + Ethernet 10/100Base-T/TX |
| Bluetooth model | Built-in USB + Bluetooth 3.0 (EDR supported) + Ethernet 10/100Base-T/TX |
| Wi-Fi model | Built-in USB + 802.11b/g/n (2.4 GHz) and 802.11a/n (5 GHz) + Ethernet 10/100Base-T/TX |
| Wireless security mode | WEP, WPA, WPA2 Personal/Enterprise |
| Supported mobile OS | iOS, Android, Windows (Store Apps) |
| Easy setting | SimpleAP, TM Utility, NFC, QR Code |

### General

| Field | Value |
|---|---|
| Printer reliability | 15 million printing lines |
| Auto cutter reliability | 1.5 million cuts |
| NV bit image memory | 12 KB (max) |
| NV graphics | 384 KB (max) |
| Receive buffer | 4 KB or 45 bytes |
| Printer drivers | ePOS-Print SDK, Windows Driver (APD), OPOS, OPOS.NET, JavaPOS (Win), JavaPOS (Linux), TM Virtual Port Driver, CUPS Driver (Linux), Mac Driver |
| Software | TM Utility (iOS, Android), TM-m30 Utility (Windows Desktop), EpsonNet Config (Win, Mac), TM Bluetooth Connector, Deployment Tool, Monitoring Tool |
| Audible alert | Optional external buzzer |
| LED indicators | Blue LED = Power, Orange LED = Error and/or Paper Out |
| Sensors | Paper End, Cover Open |
| D.K.D. function | Yes |
| Power consumption | Approx. 1.5 A (mean) |
| Power source | Adapter C, Adapter RA (printer powers tablet SKU only) |
| Power saving | Yes |
| Temperature | 5 C to 45 C |
| Relative humidity | 10 to 90% |
| Acoustic noise level | 55 dB |
| Standards and approvals | EMI FCC Class A, CAN ICES-3(A)/NMB-3(A), UL60950-1/CSA C22.2 No.60950-1 |
| Dimensions (W x D x H) | 5 x 5 x 5 in (127 x 127 x 127 mm) |
| Weight | 2.64 lb / 1200 g |
| Color | Epson Black, Epson Ultra White |
| Options | Mounting brackets, external buzzer |

## Minimal Print Example

```python
import socket
from escpos.printer import Dummy

PRINTER_IP = "192.168.0.137"
PRINTER_PORT = 9100
COLS = 57  # Font B on 80mm paper

# 1. Build the receipt with a Dummy (in-memory) printer
p = Dummy(profile="default")
p.set(font='b', align='center', bold=True)
p.text("Hello from the POS printer!\n")
p.set(font='b', align='left', bold=False)
p.text("This is normal left-aligned text.\n")
p.cut()

# 2. Send raw bytes over TCP
with socket.create_connection((PRINTER_IP, PRINTER_PORT), timeout=10) as sock:
    sock.sendall(p.output)
```

**Dependencies:** `pip3 install python-escpos`
(plus `qrcode` if you use QR codes)

## How It Works

The pattern is always the same — **build bytes, then send bytes**:

1. **Build** — Instantiate `escpos.printer.Dummy`. Call formatting/content methods on it. All ESC/POS command bytes accumulate in `p.output`.
2. **Send** — Open a raw TCP socket to `192.168.0.137:9100` and `sendall(p.output)`. The printer interprets the byte stream natively. No driver, no CUPS, no PDF.

## Formatting Reference

All formatting is done via `p.set()`. Always pass `font='b'` (Font B gives 57 columns on 80 mm paper; Font A gives fewer).

```python
# Bold, centered header
p.set(font='b', align='center', bold=True)
p.text("TITLE\n")

# Normal left-aligned body text
p.set(font='b', align='left', bold=False)
p.text("Body text here.\n")

# Right-aligned
p.set(font='b', align='right', bold=False)
p.text("Right side\n")
```

### Available `p.set()` kwargs

| Kwarg | Values | Notes |
|---|---|---|
| `font` | `'a'`, `'b'` | Always use `'b'` for 57-col width |
| `align` | `'left'`, `'center'`, `'right'` | |
| `bold` | `True` / `False` | |
| `underline` | `0`, `1`, `2` | 0=off, 1=single, 2=double |
| `width` | `1`–`8` | Character width multiplier |
| `height` | `1`–`8` | Character height multiplier |
| `density` | `0`–`8` | Print density |
| `invert` | `True` / `False` | White on black |
| `smooth` | `True` / `False` | Smoothing mode |
| `flip` | `True` / `False` | Upside-down printing |

### Content Methods

```python
p.text("Plain text with newlines\n")       # Text (wrap manually at COLS)
p.qr("https://example.com", native=True, size=3)  # QR code (native=True uses printer HW)
p.image(pil_image)                          # PIL Image object
p.barcode("123456", "EAN13")               # Barcode
p.cut()                                     # Paper cut (always do this at the end)
```

### Separators

```python
COLS = 57
p.text("=" * COLS + "\n")   # Thick separator
p.text("-" * COLS + "\n")   # Thin separator
```

## Reusable Send Function

```python
import socket

def send_to_printer(raw_bytes, ip="192.168.0.137", port=9100):
    """Send raw ESC/POS bytes to the TM-m30 over TCP."""
    with socket.create_connection((ip, port), timeout=10) as sock:
        sock.sendall(raw_bytes)
```

## Troubleshooting

- **Printer unreachable:** `192.168.0.137` can have intermittent ARP issues from some CTs. Ping first or retry.
- **Garbled output:** You probably mixed Font A and Font B column math. Stick to Font B (`font='b'`) and `COLS = 57`.
- **No cut at end:** Always call `p.cut()` as the last content method. Without it the receipt hangs out of the printer.
- **QR code not printing:** Ensure `native=True` is passed to `p.qr()`. The TM-m30 supports hardware QR generation. Also needs the `qrcode` pip package installed.

## Daily Briefing Project

For printing a full daily news briefing (FreshRSS headlines summarized by Ollama, weather, ZFS status, Grafana metrics, market data, Reddit top post, QR codes), see:

- **Project:** `/root/bin/POS-Automation/`
- **Main script:** `/root/bin/POS-Automation/pos_briefing.py`
- **Context:** `/root/bin/POS-Automation/CONTEXT.md`
- **Gitea:** `https://git.sethpc.xyz/Seth/POS-Automation`

That project's `build_receipt()` function (line 356) is the most complete example of a multi-section ESC/POS receipt with headers, dashboard data, QR codes, and article summaries. Fork it or import its patterns if you need structured receipt layouts.
