How-to Read data from serial port up until a pair of chars appear?

Hi everyone,
first time working with rust and I need some help. (tl;dr is below the long version)

Situation and Setup:
I have an electronic cash register (ECR) and a thermal printer connected to it for printing receipts. These two devices are connected by a serial cable an communicate by RS-232. Now every receipt that gets printed gets a unique number (order ID) and I want to print the order ID also as a barcode on the receipt.
My idea was to use a raspberry pi as RS-232 proxy with serial to USB adapters (FTDI chip). So my rust application should read the data from one serial port, parse the terminal printer commands, find the order ID, append the order ID as barcode to the print commands and send them to the receipt printer that is connected to the other serial port.
After one receipt was printed, there is at least a 4 seconds delay until the next receipt is printed.

What does already work:
Receiving data, parsing that data, adding a barcode and send this data to the printer works.

My problem:
Reading the data from the serial port does not work very well.

When using the serial crate, it some how kills the driver of the serial to USB adapter and it reads nothing or an endless flow of 0s (zeros) and I have to disconnect an reconnect the adapter. That's why I switched to just read the serial device /dev/ttyUSB0 with a read_until from a BufReader like a file.

I do read up until the character 'V' appears, because this is part of the printer command for cutting the receipt of. Here some example:

let f = File::open("/dev/ttyUSB0").expect("oh no");
let mut f = BufReader::new(f);
let mut read_buffer: Vec<u8> = Vec::new();
f.read_until(b'V', &mut read_buffer).expect("reading from cursor won't fail");

For testing and coding every other functionality that worked just fine. But now I'm facing the problem that the 'V' can be part of a article description and so reading until the character 'V' does not work any more.
The cutting command for the printer is really the only thing that identifies the end of a receipt. The ECR does not send an EOF. In detail the cutting command consists of two chars: 0x1D followed by 0x56 (='V').

tl;dr / question:
Is there a way to read from a file/device until a specific pair of bytes is read ([0x1D, 0x56]) that does not require the read data to be valid utf8 (because the data from the ECR is send in CP-437)?

I'm not familiar with any standard library function, but give this a try:

/// Will read until the pair of bytes is found (or EOF).
/// The terminator is consumed from the stream, but not appended to the buffer.
fn read_until<R: BufRead>(mut read: R, out: &mut Vec<u8>, pair: (u8, u8)) -> Result<usize> {
    let mut bytes_read = 0;
    let mut got_possible_terminator = false;
    
    loop {
        let buf = read.fill_buf()?;
        if buf.len() == 0 { return Ok(bytes_read); } // EOF
        
        let mut seen = 0;
        
        for byte in buf.iter().copied() {
            seen += 1;
            if got_possible_terminator && byte == pair.1 {
                out.pop(); // remove first half of terminator
                read.consume(seen);
                return Ok(bytes_read + seen - 2);
            }
            out.push(byte);
            got_possible_terminator = byte == pair.0;
        }
        let len = buf.len();
        read.consume(len);
        bytes_read += len;
    }
}

playground (with a test)

1 Like

@alice Thank you very much, that works perfect

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.