Skip to content

Commit

Permalink
Merge pull request #19 from fabienbellanger/printerStatus
Browse files Browse the repository at this point in the history
Add printer status
  • Loading branch information
fabienbellanger committed Mar 29, 2024
2 parents 2291a9b + 35674b9 commit a9ab3c9
Show file tree
Hide file tree
Showing 13 changed files with 559 additions and 167 deletions.
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Security
-->

## `0.8.3` (2024-03-14) [CURRENT]
## `0.9.0` (2024-03-27) [CURRENT]

### Added

- Add printer status (`DLE EOT` command)

### Fixed

- Fix USB driver interface number

## `0.8.3` (2024-03-14)

### Added

Expand Down
10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ license = "MIT"
name = "escpos"
readme = "README.md"
repository = "https://github.com/fabienbellanger/escpos-rs"
version = "0.8.3"
version = "0.9.0"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand All @@ -25,12 +25,12 @@ usb = ["dep:rusb"]

[dependencies]
encoding_rs = "0.8.33"
hidapi = {version = "2.6.1", optional = true}
image = {version = "0.25.0", optional = true}
hidapi = { version = "2.6.1", optional = true }
image = { version = "0.25.0", optional = true }
lazy_static = "1.4.0"
log = "0.4.21"
rusb = {version = "0.9.3", optional = true}
serialport = {version = "4.3.0", optional = true}
rusb = { version = "0.9.3", optional = true }
serialport = { version = "4.3.0", optional = true }

[dev-dependencies]
env_logger = "0.11.3"
Expand Down
40 changes: 36 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This crate implements a subset of Epson's ESC/POS protocol for thermal receipt p
It allows you to generate and print documents with basic text formatting, cutting, barcodes, QR codes and raster images
on a compatible printer.

![Ticket](resources/documentation/ticket.jpg) ![Receipt](resources/documentation/receipt.jpg)
![Ticket](resources/documentation/ticket.jpg) ![Receipt](resources/documentation/receipt.jpg)
_Printed on Aures ODP 333_

This project is strongly inspired by [recibo](https://github.com/jamhall/recibo/tree/main) _(Rust)_,
Expand All @@ -21,14 +21,14 @@ For standard functionalities (e.g. printing text), no additional dependencies ar

```toml
[dependencies]
escpos = "0.8.3"
escpos = "0.9.0"
```

If you need all [features](#Features-list), you can use the `full` feature:

```toml
[dependencies]
escpos = { version = "0.8.3", features = ["full"] }
escpos = { version = "0.9.0", features = ["full"] }
```

Or you can use `cargo add` command:
Expand Down Expand Up @@ -66,6 +66,7 @@ RUST_LOG=debug cargo run --example page_codes
RUST_LOG=debug cargo run --example usb --features "usb"
RUST_LOG=debug cargo run --example hidapi --features "hidapi"
RUST_LOG=debug cargo run --example serial_port --features "serial_port"
RUST_LOG=debug cargo run --example status --all-features
```

### Simple text formatting
Expand Down Expand Up @@ -182,6 +183,36 @@ fn main() -> Result<()> {
}
```

### 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(())
}
```

## Commands list

| Status | Command | Description | Feature |
Expand Down Expand Up @@ -297,10 +328,11 @@ fn main() -> Result<()> {

## External resources

- [Epson documentation](https://download4.epson.biz/sec_pubs/pos/reference_en/escpos/ref_escpos_en/introduction.html)
- [Epson documentation](https://download4.epson.biz/sec_pubs/pos/reference_en/escpos)

## Todo

- [ ] Customize drivers timeout read/write
- [ ] Implement all pages codes
- [ ] Add more commands:
- [ ] Graphic (Ex.: `GS 8 L` or `GS ( L`)
44 changes: 44 additions & 0 deletions examples/status.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use escpos::printer::Printer;
use escpos::utils::*;
use escpos::{driver::*, errors::Result};
use std::thread::sleep;
use std::time::Duration;

fn main() -> Result<()> {
env_logger::init();

let driver = NetworkDriver::open("192.168.1.248", 9100)?;
// let driver = ConsoleDriver::open(true);
// let driver = UsbDriver::open(0x0525, 0xa700, None)?;

loop {
Printer::new(driver.clone(), Protocol::default(), None)
.debug_mode(Some(DebugMode::Dec))
.real_time_status(RealTimeStatusRequest::Printer)?
.real_time_status(RealTimeStatusRequest::RollPaperSensor)?
.send_status()?;

// From Epson documentation: if this command must be transmitted continuously,
// it is possible to transmit up to 4 commands at once.
let mut buf = [0; 2];
driver.read(&mut buf)?;

// Online/Offline status
let status = RealTimeStatusResponse::parse(RealTimeStatusRequest::Printer, buf[0])?;
println!(
"Printer online: {}",
status.get(&RealTimeStatusResponse::Online).unwrap_or(&false)
);

// Roll paper near-end sensor status
let status = RealTimeStatusResponse::parse(RealTimeStatusRequest::RollPaperSensor, buf[1])?;
println!(
"Roll paper near-end sensor => paper adequate: {}",
status
.get(&RealTimeStatusResponse::RollPaperNearEndSensorPaperAdequate)
.unwrap_or(&false)
);

sleep(Duration::from_secs(10));
}
}
2 changes: 1 addition & 1 deletion examples/usb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fn main() -> Result<()> {
);
}

let driver = UsbDriver::open(0x0000, 0x0000, None)?;
let driver = UsbDriver::open(0x0525, 0xa700, None)?;
Printer::new(driver, Protocol::default(), None)
.debug_mode(Some(DebugMode::Dec))
.init()?
Expand Down
6 changes: 5 additions & 1 deletion src/domain/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

pub const _EOL: &str = "\n";
pub const NUL: u8 = 0x00; // Null
pub const EOT: u8 = 0x04; // End of transmission
pub const LF: u8 = 0x0A; // Line feed
pub const _VT: u8 = 0x0B; // Vertical tab
pub const _CR: u8 = 0x0D; // Carriage return
pub const DLE: u8 = 0x10; // Data link escape
pub const ESC: u8 = 0x1B;
pub const GS: u8 = 0x1D; // Group separator
pub const CAN: u8 = 0x18; // Cancel
Expand Down Expand Up @@ -63,6 +65,9 @@ pub const GS_TEXT_SIZE_SELECT: &[u8] = &[GS, b'!'];
pub const ESC_TEXT_UPSIDE_DOWN_OFF: &[u8] = &[ESC, b'{', 0];
pub const ESC_TEXT_UPSIDE_DOWN_ON: &[u8] = &[ESC, b'{', 1];

// Printer Status
pub const DLE_REAL_TIME_STATUS: &[u8] = &[DLE, EOT];

// Barcodes
#[cfg(feature = "barcodes")]
pub const GS_BARCODE_POSITION: &[u8] = &[GS, b'H'];
Expand Down Expand Up @@ -136,7 +141,6 @@ pub const GS_2D_AZTEC_CODE_CORRECTION_LEVEL: &[u8] = &[GS, b'(', b'k', 4, 0, 53,
pub const GS_2D_AZTEC_CODE_PRINT: &[u8] = &[GS, b'(', b'k', 3, 0, 53, 81, 48];

// Image

#[cfg(feature = "graphics")]
pub const GS_IMAGE_BITMAP_PREFIX: &[u8] = &[GS, b'v', b'0'];
#[cfg(feature = "graphics")]
Expand Down
2 changes: 2 additions & 0 deletions src/domain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod constants;
mod graphics;
mod page_codes;
mod protocol;
mod status;
mod types;

#[cfg(feature = "graphics")]
Expand All @@ -16,4 +17,5 @@ pub use constants::*;
#[cfg(feature = "graphics")]
pub use graphics::*;
pub use protocol::*;
pub use status::*;
pub use types::*;
55 changes: 52 additions & 3 deletions src/domain/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@

#[cfg(feature = "graphics")]
use super::bit_image::*;
use super::codes::*;
use super::{character::*, common::get_parameters_number_2, constants::*, types::*};
use crate::domain::page_codes::PageCodeTable;
use super::{character::*, codes::*, common::get_parameters_number_2, constants::*, types::*, RealTimeStatusRequest};
use crate::{
domain::page_codes::PageCodeTable,
errors::{PrinterError, Result},
io::encoder::Encoder,
};
Expand Down Expand Up @@ -217,6 +216,15 @@ impl Protocol {
cmd
}

/// Transmit real-time status
pub(crate) fn real_time_status(&self, status: RealTimeStatusRequest) -> Command {
let mut cmd = DLE_REAL_TIME_STATUS.to_vec();
let (n, a) = status.into();
cmd.push(n);
cmd.push(a);
cmd
}

#[cfg(feature = "barcodes")]
/// Set barcode font
fn barcode_font(&self, font: BarcodeFont) -> Command {
Expand Down Expand Up @@ -903,6 +911,47 @@ mod tests {
assert_eq!(protocol.motion_units(4, 122), vec![29, 80, 4, 122]);
}

#[test]
fn test_real_time_status() {
let protocol = Protocol::new(Encoder::default());
assert_eq!(
protocol.real_time_status(RealTimeStatusRequest::Printer),
vec![16, 4, 1, 0]
);
assert_eq!(
protocol.real_time_status(RealTimeStatusRequest::OfflineCause),
vec![16, 4, 2, 0]
);
assert_eq!(
protocol.real_time_status(RealTimeStatusRequest::ErrorCause),
vec![16, 4, 3, 0]
);
assert_eq!(
protocol.real_time_status(RealTimeStatusRequest::RollPaperSensor),
vec![16, 4, 4, 0]
);
assert_eq!(
protocol.real_time_status(RealTimeStatusRequest::InkA),
vec![16, 4, 7, 1]
);
assert_eq!(
protocol.real_time_status(RealTimeStatusRequest::InkB),
vec![16, 4, 7, 2]
);
assert_eq!(
protocol.real_time_status(RealTimeStatusRequest::Peeler),
vec![16, 4, 8, 3]
);
assert_eq!(
protocol.real_time_status(RealTimeStatusRequest::Interface),
vec![16, 4, 18, 1]
);
assert_eq!(
protocol.real_time_status(RealTimeStatusRequest::DMD),
vec![16, 4, 18, 2]
);
}

#[cfg(feature = "barcodes")]
#[test]
fn test_barcode_font() {
Expand Down

0 comments on commit a9ab3c9

Please sign in to comment.