La séptima vida

...o el gato así lo espera/teme

Week 41: Well, it wasn't really working

So yes, it was a big deception. I worked a lot to modify Device::Modbus to parse messages as they arrive, and it does not work.

It seemed to me that waiting for the port to time-out waiting for characters that would not arrive was just wasting time. I thought that, because the Modbus protocol tells you how many bytes you need to read, I could simply go ahead and grab what was needed. But this approach is too slow in my old computer reading the serial port at 19200 baud (well, through the Arduino's FTDI chip) and the program was loosing bytes.

It was obvious that I needed to go back to reading a whole message before parsing it. With another quick test I verified that the reading timers were correct and capable of reading the test requests, and so I modified the serial port reading routine as follows:

sub read_port {
    my $self = shift;
    my ($bytes, $read) = $self->{port}->read(255);
    # say STDERR "> " . join '-', unpack 'C*', $read;
    $self->{buffer} = $read;
    return $read;
}

Pretty simple. What you don't see is that I took out the code that repeats the read until a certain time has elapsed, waiting for the whole 255 bytes. That code is recommended by Device::SerialPort but it seems that the two timers it provides are sufficient. They are calculated like this, according to the Modbus RTU specification:

    # char_time and read_char_time are given in milliseconds
    $self->{char_time} =
        1000*($self->{databits}+$self->{stopbits}+1)/ $self->{baudrate};

    $serial->read_char_time($self->{char_time});
    if ($self->{baudrate} < 19200) { 
        $serial->read_const_time(3.5 * $self->{char_time});
    }
    else {
        $serial->read_const_time(1.75);
    }

Finally, after these modifications my server demonstration program worked as expected.