escpos_rw/printer.rs
1use crate::Error;
2use rusb::{Context, DeviceHandle, Direction, TransferType, UsbContext};
3use std::{thread, time::Duration};
4
5const OP_DELAY: u64 = 10;
6
7struct PrinterConnection {
8 /// Bulk write endpoint
9 endpoint: u8,
10 /// Bulk read endpoint
11 endpoint_r: u8,
12 /// Device handle
13 dh: DeviceHandle<Context>,
14 /// Time to wait before giving up writing to the bulk endpoint
15 timeout: std::time::Duration,
16}
17
18struct UsbConnectionData {
19 /// Vendor id for the printer
20 pub vendor_id: u16,
21 /// product id for the printer
22 pub product_id: u16,
23 /// Endpoint where the usb data is meant to be written to
24 pub endpoint_w: Option<u8>,
25 /// Endpoint where the usb data is meant to be read from
26 pub endpoint_r: Option<u8>,
27 /// Timeout for bulk write operations
28 pub timeout: std::time::Duration,
29}
30
31/// The printer object represents the thermal printer.
32pub struct Printer {
33 /// Actual connection to the printer
34 printer_connection: PrinterConnection,
35}
36
37impl Printer {
38 /// Creates the printer with the given VID/PID
39 /// ```rust,no_run
40 /// use escpos_rw::{Error, Printer};
41 /// # fn main() -> Result<(), Error> {
42 /// let Some(printer) = Printer::new(0x04b8, 0x0202)? else {
43 /// return Err(escpos_rw::Error::PrinterError(
44 /// "No printer found !".to_string(),
45 /// ));
46 /// };
47 /// # Ok(())}
48 /// ```
49 pub fn new(vendor_id: u16, product_id: u16) -> Result<Option<Printer>, Error> {
50 let printer_connection_data = UsbConnectionData {
51 vendor_id,
52 product_id,
53 endpoint_w: None,
54 endpoint_r: None,
55 timeout: std::time::Duration::from_secs(2),
56 };
57
58 // Quick check for the profile containing at least one font
59 let context = Context::new().map_err(Error::UsbError)?;
60
61 let devices = context.devices().map_err(Error::UsbError)?;
62 for device in devices.iter() {
63 let s = device.device_descriptor().map_err(Error::UsbError)?;
64 if s.vendor_id() == printer_connection_data.vendor_id
65 && s.product_id() == printer_connection_data.product_id
66 {
67 // Before opening the device, we must find the bulk endpoint
68 let config_descriptor =
69 device.active_config_descriptor().map_err(Error::UsbError)?;
70 let actual_endpoint = if let Some(endpoint_w) = printer_connection_data.endpoint_w {
71 endpoint_w
72 } else {
73 let mut detected_endpoint: Option<u8> = None;
74 // Horrible to have 3 nested for, but so be it
75 for interface in config_descriptor.interfaces() {
76 for descriptor in interface.descriptors() {
77 for endpoint in descriptor.endpoint_descriptors() {
78 if let (TransferType::Bulk, Direction::Out) =
79 (endpoint.transfer_type(), endpoint.direction())
80 {
81 detected_endpoint = Some(endpoint.address());
82 }
83 }
84 }
85 }
86
87 if let Some(detected_endpoint) = detected_endpoint {
88 detected_endpoint
89 } else {
90 return Err(Error::NoBulkEndpoint);
91 }
92 };
93
94 let actual_endpoint_r = if let Some(endpoint_r) = printer_connection_data.endpoint_r
95 {
96 endpoint_r
97 } else {
98 let mut detected_endpoint_r: Option<u8> = None;
99 // Horrible to have 3 nested for, but so be it
100 for interface in config_descriptor.interfaces() {
101 for descriptor in interface.descriptors() {
102 for endpoint in descriptor.endpoint_descriptors() {
103 if let (TransferType::Bulk, Direction::In) =
104 (endpoint.transfer_type(), endpoint.direction())
105 {
106 detected_endpoint_r = Some(endpoint.address());
107 }
108 }
109 }
110 }
111
112 if let Some(detected_endpoint_r) = detected_endpoint_r {
113 detected_endpoint_r
114 } else {
115 return Err(Error::NoBulkEndpoint);
116 }
117 };
118
119 // Now we continue opening the device
120
121 match device.open() {
122 Ok(dh) => {
123 if let Ok(active) = dh.kernel_driver_active(0) {
124 if active {
125 // The kernel is active, we have to detach it
126 match dh.detach_kernel_driver(0) {
127 Ok(_) => (),
128 Err(e) => return Err(Error::UsbError(e)),
129 };
130 }
131 } else {
132 println!("Could not find out if kernel driver is active, might encounter a problem soon.");
133 };
134 // Now we claim the interface
135 match dh.claim_interface(0) {
136 Ok(_) => (),
137 Err(e) => return Err(Error::UsbError(e)),
138 }
139 let timeout = printer_connection_data.timeout;
140 return Ok(Some(Printer {
141 printer_connection: PrinterConnection {
142 endpoint: actual_endpoint,
143 endpoint_r: actual_endpoint_r,
144 dh,
145 timeout,
146 },
147 }));
148 }
149 Err(e) => return Err(Error::UsbError(e)),
150 };
151 }
152 }
153 // No printer was found with such vid and pid
154 Ok(None)
155 }
156
157 /// Sends bytes to the printer
158 /// ```rust,no_run
159 /// # use escpos_rw::{Error, Printer};
160 /// # fn main() -> Result<(), Error> {
161 /// # use escpos_rw::{Error, Printer};
162 /// # let Some(printer) = Printer::new(0x04b8, 0x0202)? else {
163 /// # return Err(escpos_rw::Error::PrinterError(
164 /// # "No printer found !".to_string(),
165 /// # ));
166 /// # };
167 /// // Open the cash drawer
168 /// printer.write_raw([0x1B, 0x70, 0x00, 0x7E, 0x7E])?;
169 /// # Ok(())
170 /// # }
171 /// ```
172 pub fn write_raw<A: AsRef<[u8]>>(&self, bytes: A) -> Result<(), Error> {
173 let PrinterConnection {
174 endpoint,
175 endpoint_r: _,
176 dh,
177 timeout,
178 } = &self.printer_connection;
179 {
180 dh.write_bulk(*endpoint, bytes.as_ref(), *timeout)
181 .map_err(Error::UsbError)?;
182 thread::sleep(Duration::from_millis(OP_DELAY));
183 Ok(())
184 }
185 }
186
187 /// Reads bytes from the printer
188 /// ```rust,no_run
189 /// # use escpos_rw::{Error, Printer};
190 /// # fn main() -> Result<(), Error> {
191 /// # use escpos_rw::{Error, Printer};
192 /// # let Some(printer) = Printer::new(0x04b8, 0x0202)? else {
193 /// # return Err(escpos_rw::Error::PrinterError(
194 /// # "No printer found !".to_string(),
195 /// # ));
196 /// # };
197 /// // Reads data from printer output buffer
198 /// printer.read_raw()?;
199 /// # Ok(())
200 /// # }
201 /// ```
202 pub fn read_raw(&self) -> Result<[u8; 128], Error> {
203 let PrinterConnection {
204 endpoint: _,
205 endpoint_r,
206 dh,
207 timeout,
208 } = &self.printer_connection;
209 {
210 let mut buffer: [u8; 128] = [0; 128];
211 dh.read_bulk(*endpoint_r, &mut buffer, *timeout)
212 .map_err(Error::UsbError)?;
213 Ok(buffer)
214 }
215 }
216}