ESC/POS Commands: a Practical Reference
The ESC/POS commands that matter in production — init, text style, feed, cut, drawer kick, status — with raw bytes and the quirks between brands.
Published 2026-07-24 · For developers →
ESC/POS is Epson's escape-code command language for receipt printers, and the de facto standard the whole industry forked. A job is a one-way byte stream: printable text mixed with commands that start with ESC (0x1B) or GS (0x1D), ending in a feed and a cut. This is a reference to the commands that actually matter in production — with the raw bytes, and the quirks that bite when you move between printer brands.
How ESC/POS works
Three things explain most of the protocol's behavior:
- It is stateful. Commands like bold or center-alignment set a mode that persists until you unset it. Every job should therefore start with
ESC @to reset the printer to a known state — otherwise you inherit whatever the last job left behind. - It is (almost) one-way. The printer never acknowledges anything. You write bytes; it prints them. The exceptions are the real-time status queries (
DLE EOT), which get a single reply byte — covered below. - Text is just text. Anything that is not a recognized command sequence prints as characters in the current code page, and
LF(0x0A) prints the line buffer and advances the paper.
A minimal, complete receipt:
1B 40 ESC @ initialize
1B 61 01 ESC a 1 center
1B 45 01 ESC E 1 bold on
43 4F 46 46 45 45 0A "COFFEE" LF print a line
1B 45 00 ESC E 0 bold off
1B 64 05 ESC d 5 feed 5 lines
1D 56 00 GS V 0 full cutThe commands that matter
The subset below covers the overwhelming majority of real receipts. Everything else in the 200-plus-page vendor manuals is images, page mode, kanji handling, and model-specific extras.
| Command | Bytes | What it does |
|---|---|---|
| Initialize | 1B 40 | Reset all modes to defaults. Start every job with this |
| Line feed | 0A | Print the buffer, advance one line |
| Feed n lines | 1B 64 n | Print, then feed n lines |
| Align | 1B 61 n | 0 left, 1 center, 2 right |
| Bold | 1B 45 n | 1 on, 0 off |
| Underline | 1B 2D n | 0 off, 1 thin, 2 thick |
| Print mode | 1B 21 n | Bitfield: bold, double-height, double-width, underline |
| Character size | 1D 21 n | High nibble width ×1–8, low nibble height ×1–8 |
| Full cut | 1D 56 00 | Cut across the full width |
| Partial cut | 1D 56 01 | Leave a small tab of paper |
| Feed and cut | 1D 56 42 n | Feed n motion units, then partial cut |
| Drawer kick | 1B 70 m t1 t2 | Pulse the RJ12 drawer connector |
| Barcode | 1D 6B m n d1..dn | 1D barcode, system m, n data bytes |
| QR code | 1D 28 6B ... | Multi-step 2D sequence (below) |
| Status query | 10 04 n | Real-time status, single-byte reply |
| Code page | 1B 74 n | Select character table for bytes 0x80–0xFF |
Initialize: ESC @
1B 40 clears print modes, resets line spacing, and empties the line buffer. It does not clear the receive buffer or recover from an error state — a printer mid-paper-jam ignores it. Cheap habit, real payoff: jobs become order-independent.
Text styling: ESC ! and GS !
There are two size/style systems, and brands differ on which they honor fully.
ESC ! n (1B 21 n) sets several attributes in one byte — a bitfield where 0x08 is bold, 0x10 double height, 0x20 double width, 0x80 underline. So 1B 21 38 gets you bold double-height double-width in one command.
GS ! n (1D 21 n) is the finer-grained size command: the high nibble selects width multiplier 1–8, the low nibble height 1–8. 1D 21 11 is double width and double height; 1D 21 00 returns to normal.
In practice: use ESC ! for the common emphasis cases, GS ! when you need big totals. Test both on your target hardware — some budget printers implement only multipliers 1 and 2, and some ignore the width nibble entirely.
Alignment is ESC a n (1B 61 n) and applies to entire lines: it must be sent at the start of a line, before any printable characters, or it silently applies to the next line.
Feeding and cutting: GS V and friends
GS V has accumulated variants over 30 years:
| Variant | Bytes | Behavior |
|---|---|---|
| Function A, full | 1D 56 00 | Full cut, no feed |
| Function A, partial | 1D 56 01 | Partial cut (one tab of paper left) |
| Function B, feed + cut | 1D 56 42 n | Feed n motion units, then cut |
| Legacy | 1B 69 / 1B 6D | Old ESC-based full/partial cut, still honored by many models |
The trap everyone hits once: the cutter blade sits several millimeters above the print head. Cut immediately after your last line and the blade slices through the bottom of the receipt. You must feed the head-to-cutter distance first — typically 3 to 6 lines depending on the mechanism. GS V 66 n bakes the feed into the cut; on printers where it misbehaves, an explicit ESC d n before 1D 56 00 does the same job.
This clearance is a property of the printer, not the receipt — which is why hardcoding it into your app's templates goes wrong the moment hardware changes. (On a ProxyNode it is a config value on the device: the node feeds its configured clearance before every cut, and dialing in a new printer is a curl to PUT /config, not an app release.)
Cash drawer: ESC p
Cash drawers do not speak ESC/POS — they are a solenoid wired to an RJ12 jack on the printer. ESC p m t1 t2 (1B 70 m t1 t2) fires a pulse on connector pin m (0 for pin 2, 1 for pin 5), with t1/t2 setting the pulse on/off time in 2 ms units:
1B 70 00 19 FA ESC p 0 25 250 — kick pin 2, 50 ms on, 500 ms offToo short a pulse and heavier drawers do not open; too long and you can overheat the solenoid. Wiring, timings, and the "drawer opens but status says closed" cases get a full treatment in the cash drawer kick guide.
Barcodes: GS k
One-dimensional barcodes use GS k (1D 6B). Prefer "Function B", which takes an explicit length byte instead of a NUL terminator — it is unambiguous and handles Code 128's full byte range:
1D 68 50 GS h 80 barcode height: 80 dots
1D 77 02 GS w 2 module width: 2
1D 6B 49 0C ... GS k 73 12 Function B, system 73 (Code 128), 12 data bytesCommon system IDs under Function B: 65 UPC-A, 67 EAN-13, 69 Code 39, 73 Code 128. Code 128 has one extra quirk: many printers require the data to start with a code-set selector like {B before your ASCII payload — omit it and you get either nothing or garbage, depending on brand.
QR codes: GS ( k
QR is not one command but a conversation with the printer's symbol buffer — select model, set module size, store data, print. All four steps use the GS ( k (1D 28 6B) envelope with a little-endian length:
1D 28 6B 04 00 31 41 32 00 select QR model 2
1D 28 6B 03 00 31 43 06 module size 6
1D 28 6B 10 00 31 50 30 ... store data (len = payload + 3)
1D 28 6B 03 00 31 51 30 print the stored symbolThe length field trips people up: for the store-data step it counts the payload plus the three header bytes (31 50 30). A 13-byte URL means a length of 16 — 10 00 little-endian. Get it wrong by one and the printer either eats your next command as QR data or prints a truncated symbol.
Budget printers are also where QR support is patchiest: some ignore GS ( k entirely, and the only path to a QR is rendering it as a raster image.
Real-time status: DLE EOT
The one place ESC/POS talks back. Send 10 04 n and the printer replies with a single status byte, even mid-job — n selects printer state (1), offline causes (2), error causes (3), or paper sensors (4). In every valid reply, bits 1 and 4 are fixed high and bits 0 and 7 fixed low, so a healthy idle printer answers 0x12 to all four queries.
10 04 01 printer status → bit 3 set = offline
10 04 02 offline cause → bit 2 cover open, bit 5 paper out
10 04 04 paper sensors → bits 2+3 near-end, bits 5+6 paper endThere is enough depth here — parsing replies out of a stream that also contains 0x10 bytes inside image data, printers that never answer, paper sensors that lie — that it has its own guide.
Code pages and international text
Bytes 0x00–0x7F are ASCII. What 0x80–0xFF mean depends on the selected code page: ESC t n (1B 74 n) picks a table, but the mapping of n to table varies by manufacturer. Page 0 is CP437 and page 16 is usually Windows-1252 on Epson — on a generic printer, all bets are off. Most printers do not accept UTF-8 at all; you must transcode to the target code page or render text as an image. If your receipts show é as graphics characters, this is why.
Brand quirks: the standard everyone forks
ESC/POS is a specification only in the sense that Epson publishes one. Every other manufacturer implements an approximation, and the differences are exactly where production bugs live:
- Cut variants differ — some honor only the legacy
ESC i, some onlyGS V 66 n. ESC !bitfields are partially implemented; size multipliers cap at 2 on many budget models.- Code page numbering is vendor-specific beyond page 0.
- QR support ranges from full
GS ( kto nothing. - DLE EOT replies range from spec-accurate to never-answers.
The community's answer is escpos-printer-db, the capabilities database maintained by the python-escpos project — a machine-readable record of which model supports which features and code pages. If you support arbitrary hardware, build against it rather than rediscovering each quirk from support tickets.
This fragmentation is the reason cloud POS vendors keep short certified-printer whitelists, and it is the problem ProxyNodes exists to absorb: your app targets one JSON API, and the node owns the byte-level dialect of whatever printer is attached. A survey of the alternatives is in the receipt printer API guide.
Testing commands without a printer
You do not need hardware to develop against this reference. Two zero-hardware options:
- Pipe bytes through a decoder and eyeball the command stream (any hex dump plus this page works).
- Run a receipt printer simulator that accepts real ESC/POS on port 9100, renders the receipt, answers DLE EOT queries, and can fault-inject paper-out and cover-open — so your error handling gets exercised, not just your happy path. More on emulator options in the ESC/POS emulator guide, and more printing guides on the developers hub.
Frequently asked questions
- What is the ESC/POS command to cut the paper?
- GS V — bytes 1D 56 00 for a full cut, 1D 56 01 for a partial cut, or 1D 56 42 n to feed n units and then cut. Feed 3 to 6 lines before cutting, because the blade sits above the print head and will otherwise cut through your last lines of text.
- How do I center or bold text in ESC/POS?
- Center with ESC a 1 (bytes 1B 61 01), sent at the start of a line. Bold on with ESC E 1 (1B 45 01) and off with 1B 45 00. Both are modes that persist until changed, so reset styles after each block or start jobs with ESC @ (1B 40).
- Why does my receipt print correctly on one printer but garble on another?
- ESC/POS is forked by every manufacturer. The usual culprits are code page numbering, unsupported cut or QR commands, and partial ESC ! implementations. Check the escpos-printer-db capabilities database for the target model, or test against an emulator that mimics the target.
- Does ESC/POS support UTF-8?
- Generally no. Printers interpret bytes above 0x7F through a selected code page (ESC t n), so you must transcode text to that code page or render it as an image. A handful of recent models add a UTF-8 mode, but you cannot rely on it across hardware.
- How do I know if the printer is out of paper?
- Send the real-time query DLE EOT 4 (bytes 10 04 04) and read the one-byte reply: bits 5 and 6 set mean paper end, bits 2 and 3 mean near-end. Not every printer answers, and cheap ones sometimes lack the near-end sensor, so treat silence as unknown rather than as OK.
Related reading
- ESC/POS Printer Status: DLE EOT ExplainedThe DLE EOT real-time status commands byte by byte — paper, cover, drawer, errors — plus why many printers lie or stay silent, and what to do about it.
- Cash Drawer Kick: the ESC/POS CommandHow cash drawers really open — the ESC p pulse through the printer's RJ12 port — with the exact bytes, pin timings, and fixes when the drawer won't kick.
- Receipt Printer API: the Complete GuideEvery way software prints receipts in 2026 — ESC/POS, port 9100, vendor SDKs, cloud relays — and how a local HTTP API on the LAN compares.
- ESC/POS Emulator: Print Without a PrinterCompare ESC/POS emulators and receipt printer simulators for development — and how to emulate paper-out, cover-open, and DLE EOT status in software.