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}