Text on a receipt
A receipt is a stream of characters with a line feed at the end of each line. Five bytes print the first one, and the last of the five is the one that makes the paper move.
What a line of text actually is
Two of those five bytes are not text. 1B 40 is ESC @, the reset, and it
belongs at the top of every sketch — the printer remembers the font size and
alignment from the last job it ran, including the one that crashed, and a
receipt that comes out in double-width capitals is usually this.
The byte at the end is the one that prints. Characters arrive and wait in a
buffer; 0A, the line feed, is what commits the line to paper. In Arduino
terms that is exactly the difference between print and println, and it is
why a sketch that ends on print appears to lose its last line.
The commands worth knowing
ESC/POS has hundreds. Four of them cover most receipts:
| Bytes | What it does |
|---|---|
1B 40 | Reset — font, size and alignment back to default |
1B 21 n | Font size: bit 4 doubles the height, bit 5 the width |
1B 61 n | Align: 0 left, 1 centre, 2 right |
0A | Line feed — print what is buffered |
Everything else in a receipt is ordinary text.
Leave room at the end
The print head sits behind the tear bar, so the last thing printed is still inside the machine when the job finishes. Three or four blank lines at the end push it out where you can read and tear it. That margin at the bottom of every till receipt you have ever been handed is this, and not decoration.
The code
The smallest useful receipt: a reset, a heading in double-width, three lines of items and a total, then enough feed to clear the tear bar. Everything here is ESC/POS, which is what the receipt printer speaks — none of it applies to the label printer.
// Receipt printer wiring for this sketch.
//
// USB-C PD charger (9 V capable) -> base board PD TYPE-C
// base board PRINTER POWER -> printer POWER socket
// base board PRINTER DATA TTL -> printer TTL socket
//
// base board 3V3 MCU header -> ESP32-S3:
// TX -> GPIO16 (this board's RX)
// RX -> GPIO17 (this board's TX)
// 3V3 -> 3V3
// GND -> GND
//
// Arduino IDE: Tools > Board "ESP32S3 Dev Module", Tools > USB CDC On Boot
// "Enabled" so the Serial Monitor works. No library needed.
#define PRN_RX 16 // from the printer's TXD
#define PRN_TX 17 // to the printer's RXD
#define PRN_BAUD 9600 // printed on the printer: TTL:9600 N-8-1 9V
HardwareSerial printer(2);
// ESC @ - forget the font, size and alignment the last job left behind.
void reset() {
printer.write(0x1B); printer.write(0x40);
delay(50);
}
// ESC ! n - bit 4 doubles the height, bit 5 doubles the width.
void size(uint8_t n) {
printer.write(0x1B); printer.write(0x21); printer.write(n);
}
// ESC a n - 0 left, 1 centre, 2 right.
void align(uint8_t n) {
printer.write(0x1B); printer.write(0x61); printer.write(n);
}
void setup() {
Serial.begin(115200);
printer.begin(PRN_BAUD, SERIAL_8N1, PRN_RX, PRN_TX);
delay(500);
reset();
align(1);
size(0x30); // double width and height
printer.println("LONELY BINARY");
size(0x00);
printer.println("Order 00042");
printer.println("--------------------------------");
align(0);
printer.println("Coffee x1 3.50");
printer.println("Latte x1 4.20");
printer.println("Cake x1 4.50");
printer.println("--------------------------------");
align(2);
size(0x10); // double height only
printer.println("Total 12.20");
size(0x00);
// Feed past the tear bar, or the last line is still inside the printer.
printer.println();
printer.println();
printer.println();
Serial.println("sent");
}
void loop() {}If the paper feeds but stays blank, the roll is in upside down: thermal paper only darkens on one side. If characters appear but they are not the ones you sent, the baud rate is wrong. If nothing happens at all, the supply never reached 9 V.
When it does not work
Characters sit in the printer's buffer until a line feed arrives, so a sketch that ends with print rather than println leaves its final line unprinted. It looks like the printer stopped early. Use println, or send a 0x0A of your own.
The print head sits some distance behind the tear bar, so whatever printed last is still in the mechanism. Feed three or four blank lines at the end of a receipt — that is what the blank printlns at the bottom of the sketch are for, and it is why real till receipts have a margin.
The printer remembers its font, size and alignment across jobs, including the job that crashed half way through. That is what ESC @ at the top of the sketch is for: it puts every setting back to default before you start.
The roll is in the wrong way up. Thermal paper has a coating on one side only and the head is underneath, so a roll loaded backwards prints perfectly onto the side nobody can see. Pull a few centimetres out and scratch it with a fingernail — the coated side marks.
The same wiring, a different machine, and four steps where the receipt printer had one.
A label is not a receipt →Edit this page — content/books/thermal-printer/text-on-a-receipt.mdx
Questions about this product
See what other owners have asked, and read their solutions.
This page covers several products. Choose yours to see the right discussions.
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.