This crate implements a subset of Epson's ESC/POS protocol for thermal receipt printers. It allows you to generate and print documents with basic text formatting, cutting, barcodes, QR codes and raster images on a compatible printer. It also provides a way to check the printer status.

Printed on Aures ODP 333
This project is strongly inspired by recibo (Rust), escposify (Rust) and escpos (Go).
For standard functionalities (e.g., printing text), no additional dependencies are required:
[dependencies]
escpos = "0.19.0"
If you need all features, you can use the full feature:
[dependencies]
escpos = { version = "0.19.0", features = ["full"] }
Or you can use cargo add command:
cargo add escpos
cargo add escpos -F full
Tool used: tarpaulin
cargo install cargo-tarpaulin
cargo tarpaulin --all-features --tests
Results:
57.62% coverage, 1172/2034 lines coveredTool used: cargo-msrv
cargo install cargo-msrv
cargo msrv find
cargo msrv verify
| Name | Description | Default |
|---|---|---|
std |
Enable std support (disable for no_std + alloc environments) |
✅ |
barcodes |
Print barcodes (UPC-A, UPC-E, EAN8, EAN13, CODE39, ITF or CODABAR) | ✅ |
codes_2d |
Print 2D codes (QR Code, PDF417, GS1 DataBar, DataMatrix, Aztec, etc.) | ✅ |
graphics |
Print raster images (requires std) |
❌ |
usb |
Enable USB feature (requires std) |
❌ |
native_usb |
Enable native USB feature (requires std) |
❌ |
hidapi |
Enable HidApi feature (requires std) |
❌ |
serial_port |
Enable Serial port feature (requires std) |
❌ |
usbprint |
Enable Windows USB print driver (usbprint.sys via Win32 API) |
❌ |
ui |
Enable ui feature (UI components) | ❌ |
full |
Enable all features | ❌ |
no_std supportThe crate can be used on bare-metal targets (microcontrollers, kernels…) by disabling default features. Only alloc is
required:
[dependencies]
escpos = { version = "0.18", default-features = false, features = ["barcodes", "codes_2d"] }
The built-in Console, Network and File drivers as well as the graphics feature require
std. In no_std mode you implement the Driver trait for your peripheral (UART, SPI, USB endpoint, …) and pass it to
Printer::new. The Printer::driver accessor lets you recover the driver from a Printer.
See examples/no_std_codes.rs for a minimal example with a custom in-memory driver and
barcodes / 2D codes — all the protocol-level APIs (barcodes, QR Code, PDF417, DataMatrix, Aztec, MaxiCode, page codes,
status…) work in no_std. The codes themselves are rendered by the printer's firmware: the crate only serializes the
ESC/POS commands, so the embedded-side cost is minimal.
The examples folder contains various examples of how to use escpos.
The docs also provide code snippets and examples.
To launch an example, use the following command:
RUST_LOG=debug cargo run --example full --features full
The list of all the examples can be found here.
use escpos::printer::Printer;
use escpos::printer_options::PrinterOptions;
use escpos::utils::*;
use escpos::{driver::*, errors::Result};
fn main() -> Result<()> {
env_logger::init();
let driver = NetworkDriver::open("192.168.1.248", 9100, None)?;
Printer::new(driver, Protocol::default(), Some(PrinterOptions::default()))
.debug_mode(Some(DebugMode::Dec))
.init()?
.smoothing(true)?
.bold(true)?
.underline(UnderlineMode::Single)?
.writeln("Bold underline")?
.justify(JustifyMode::CENTER)?
.reverse(true)?
.bold(false)?
.writeln("Hello world - Reverse")?
.feed()?
.justify(JustifyMode::RIGHT)?
.reverse(false)?
.underline(UnderlineMode::None)?
.size(2, 3)?
.writeln("Hello world - Normal")?
.print_cut()?; // print() or print_cut() is mandatory to send the data to the printer
Ok(())
}
use escpos::printer::Printer;
use escpos::utils::*;
use escpos::{driver::*, errors::Result};
fn main() -> Result<()> {
env_logger::init();
let driver = ConsoleDriver::open(true);
Printer::new(driver, Protocol::default(), None)
.debug_mode(Some(DebugMode::Hex))
.init()?
.ean13_option(
"1234567890265",
BarcodeOption::new(
BarcodeWidth::M,
BarcodeHeight::S,
BarcodeFont::A,
BarcodePosition::Below,
)
)?
.feed()?
.print()?; // print() or print_cut() is mandatory to send the data to the printer
Ok(())
}
use escpos::printer::Printer;
use escpos::utils::*;
use escpos::{driver::*, errors::Result};
fn main() -> Result<()> {
env_logger::init();
let driver = ConsoleDriver::open(true);
Printer::new(driver, Protocol::default(), None)
.debug_mode(Some(DebugMode::Hex))
.init()?
.qrcode_option(
"https://www.google.com",
QRCodeOption::new(QRCodeModel::Model1, 6, QRCodeCorrectionLevel::M),
)?
.feed()?
.print_cut()?; // print() or print_cut() is mandatory to send the data to the printer
Ok(())
}
graphics feature enabled)use escpos::printer::Printer;
use escpos::utils::*;
use escpos::{driver::*, errors::Result};
fn main() -> Result<()> {
env_logger::init();
let driver = ConsoleDriver::open(true);
let mut printer = Printer::new(driver, Protocol::default(), None);
printer.debug_mode(Some(DebugMode::Hex))
.init()?
.bit_image_option(
"./resources/images/rust-logo-small.png",
BitImageOption::new(Some(128), None, BitImageSize::Normal)?,
)?
.feed()?
.print_cut()?; // print() or print_cut() is mandatory to send the data to the printer
Ok(())
}
usbprint feature enabled, Windows only)Drives a POS printer through the standard Windows usbprint.sys kernel driver using the Win32 API (CreateFile /
ReadFile / WriteFile). No Zadig / WinUSB / libusb swap required — the device keeps working with the regular Windows
print spooler at the same time.
```rust,ignore use escpos::printer::Printer; use escpos::utils::; use escpos::{driver::, errors::Result};
fn main() -> Result<()> { env_logger::init();
// List USB printers exposed by usbprint.sys
for info in WindowsUsbPrintDriver::list()? {
println!("{} (VID={:?} PID={:?})", info.device_path, info.vendor_id, info.product_id);
}
// Option 1: open by USB vendor/product id
let driver = WindowsUsbPrintDriver::open_by_vid_pid(0x0525, 0xa700)?;
// Option 2: open by full device interface path
// let driver = WindowsUsbPrintDriver::open(r"\\?\USB#VID_0525&PID_A700#...")?;
Printer::new(driver, Protocol::default(), None)
.init()?
.writeln("Hello from Windows usbprint!")?
.print_cut()?;
Ok(())
}
### Check printer status
```rust
use escpos::printer::Printer;
use escpos::utils::*;
use escpos::{driver::*, errors::Result};
fn main() -> Result<()> {
env_logger::init();
let driver = ConsoleDriver::open(true);
Printer::new(driver.clone(), Protocol::default(), None)
.debug_mode(Some(DebugMode::Dec))
.real_time_status(RealTimeStatusRequest::Printer)?
.real_time_status(RealTimeStatusRequest::RollPaperSensor)?
.send_status()?;
let mut buf = [0; 1];
driver.read(&mut buf)?;
let status = RealTimeStatusResponse::parse(RealTimeStatusRequest::Printer, buf[0])?;
println!(
"Printer online: {}",
status.get(&RealTimeStatusResponse::Online).unwrap_or(&false)
);
Ok(())
}
| Status | Command | Description | Feature |
|---|---|---|---|
| ✅ | init() |
Initialize printer (ESC @) |
|
| ✅ | print() |
Print document | |
| ✅ | reset() |
Hardware reset (ESC ? LF 0) |
|
| ✅ | cut() |
Paper cut (GS V A 0) |
|
| ✅ | partial_cut() |
Partial paper cut (GS V A 1) |
|
| ✅ | print_cut() |
Print and paper cut | |
| ✅ | page_code() |
Select character code table (ESC t) |
|
| ✅ | character_set() |
Select an international character set (ESC R) |
|
| ✅ | bold() |
Text bold (ESC E) |
|
| ✅ | underline() |
Text underline (ESC -) |
|
| ✅ | double_strike() |
Text double strike (ESC G) |
|
| ✅ | font() |
Text font (ESC M) |
|
| ✅ | flip() |
Text flip (ESC V) |
|
| ✅ | justify() |
Text justify (ESC a) |
|
| ✅ | reverse() |
Text reverse color (GS B) |
|
| ✅ | size() |
Text size (GS !) |
|
| ✅ | reset_size() |
Reset text size (GS !) |
|
| ✅ | smoothing() |
Smoothing mode (GS b) |
|
| ✅ | feed() |
Line feed (ESC d) |
|
| ✅ | feeds() |
Multiple lines feed (ESC d) |
|
| ✅ | line_spacing() |
Line spacing (ESC 3) |
|
| ✅ | reset_line_spacing() |
Reset line spacing (ESC 2) |
|
| ✅ | upside_down() |
Upside-down mode (ESC {) |
|
| ✅ | cash_drawer() |
Generate pulse (ESC p) |
|
| ✅ | write() |
Write text | |
| ✅ | writeln() |
Write text and line feed | |
| ✅ | custom() |
Custom command | |
| ✅ | custom_with_page_code() |
Custom command with page code | |
| ✅ | motion_units() |
Set horizontal and vertical motion units (GS P) |
|
| ✅ | ean13() |
Print EAN13 with default option | barcode |
| ✅ | ean13_option() |
Print EAN13 with custom option | barcode |
| ✅ | ean8() |
Print EAN8 with default option | barcode |
| ✅ | ean8_option() |
Print EAN8 with custom option | barcode |
| ✅ | upca() |
Print UPC-A with default option | barcode |
| ✅ | upca_option() |
Print UPC-A with custom option | barcode |
| ✅ | upce() |
Print UPC-E with default option | barcode |
| ✅ | upce_option() |
Print UPC-E with custom option | barcode |
| ✅ | code39() |
Print CODE 39 with default option | barcode |
| ✅ | code39_option() |
Print CODE 39 with |
$ claude mcp add escpos-rs \
-- python -m otcore.mcp_server <graph>