Author image Narbey Derbekyan


Device::Velleman::PPS10 - Read data from Velleman PPS10 oscilloscope


    use Device::Velleman::PPS10;
    my $pps10 = Device::Velleman::PPS10->new;

    while (1) {
        # Read data from serial port and append to buffer in $pps10 object.

        # Get 0 or more packets that may exist in buffer.
        while (my $packet = $pps10->get_next_packet) {
            # Do stuff with packet of a single frame of the signal trace.


The Velleman PPS10 oscilloscope sends each frame of the signal displayed on its LCD screen as a series of packets over the serial port. Each packet contains 256 voltage samples, which if traced graphically, replicates the signal the LCD screen. The data in the packet is more detailed than can be drawn on the 128x64 LCD screen.

Device::Velleman::PPS10 allows for reading of the raw data from the serial port and parses for packets containing the frames. Currently, the module only uses the packet type (BA) containing a complete frame of a signal trace. The scope sends two other packet types (BR and BS), which contain 1 or just a few samples.

This module relies on Device::SerialPort. The user may need to handle permission issues when reading from the serial port device on their system.

This module has not been tested on a Win32 system.

The Raw Data

The data sent by the oscilloscope over the serial device is binary. There are three packet types and each begins with a delimiter of 2 to 4 byte values. The delimiter is then followed by 6 header values containing oscilloscope settings such as volts/division. This version of the module only processes the BA type packets. These packets contain 256 sample values following the headers. One byte per sample.

Parsed Packet Data Structure

The final product of parsing the raw data from the serial device is a hash containing the unscaled and scaled voltage samples, and oscilloscope setting values contained in each packet. The unscaled sample values of the signal are scaled to actual voltage values given the display settings reported in the packet.

    $packet = {
        packet_type      => 'BA' | 'BR' | 'BS',
        raw_packet       => <binary data>, # packet data from serial port to be parsed
        processed_packet => $packet,       # actual raw packet that was parsed
        header           => [ ... ],       # array of header values in decimal
        time_per_div     => '0.002',       # time between division marks on screen
        time_per_point   => '0.0002',      # time between each sample point
        volts_per_div    => '0.01',        # volts between division marks on screen
        volts_per_point  => '0.0003125',   # volts between each sample point
        volts_acdc       => 'ac' | 'dc',
        volts_10x        => 0 | 1,
        time             => [ ... ],       # sample counts 0 .. 255
        trace            => [ ... ],       # the sample values in decimal
        x_max            => 0.0512',       # end of x-axis in seconds from 0
        y_max            => 0.04',         # height of y-axis in volts above the 0V baseline
        time_scaled      => [ ... ],       # x-axis values in seconds
        trace_scaled     => [ ... ],       # y-axis values in volts

The time values are simply integers 0 to 255 and represent the 256 samples. They are scaled to units of seconds using time_per_point and stored in time_scaled. The trace values are scaled using volts_per_point and stored in trace_scaled. The original trace values are 8-bit samples (0 .. 255) where 127 is the 0V baseline. Plotting time_scaled values on the x-axis and trace_scaled on the y-axis will give a nice graph of the frame.


There are no exportable functions or symbols.

The following methods are provided.

$pps10 = Device::Velleman::PPS10->new;

Create a new object to read from a single serial port. All parameters to the constructor are optional.

    port        Serial port to read from. Default: '/dev/ttyS0'.
    read_bytes  Number of bytes to read from port. Default: 255.
    verbose     Report actions being taken to STDERR. 0|1. Default: 0.
    debug       Enable dev actions. 0|1. Default: 0.

The returned object is a hash-ref with the following structure:

    $self = {
        serial_port  => $serial_port, # Device::SerialPort object
        port         => '/dev/ttyS0',
        read_bytes   => 255,
        verbose      => 0|1,
        debug        => 0|1,

        read_data        => <last data read from serial port>,
        read_total_bytes => <int>,    # Total bytes read from serial port over life of object
        read_count       => <int>,    # count of serial port readings
        read_buffer      => <buffer of serial port data, holding candidate packets>

        packet_count   => <int>,      # number of parsed packets over the life of the object
        current_packet => { ... },    # hash ref of last parsed packet
        first_ba_seen  => 0,

        raw_out_fh     => undef|<filehandle>, # file handle of the raw data file
$pps10->read(<read byte length>);
($length_read, $data_read) = $pps10->read;

Read data from serial port and append to the rasw data buffer in the object. Returns the length of the read request and the string read in array context. These are the same values returned by the read() method of a Device::SerialPort object.

The length of the data to be read is defined by read_bytes value passed to the constructor. Optional parameter to ->read() overrides the default read length, for that call of the method. This should not be necessary since the default value of 255 works well.


Fetches first packet of data from the raw data buffer, parses and scales it. Returns undef if no complete packet is found in the buffer. This method should be called repeatedly between each ->read(), until no packets are returned. See "Parsed Packet Data Structure".

$pps10->save_raw_data_to_file('/file/path', [0|1]);

Start saving the raw data read from the serial port to specified file. Data is written to file with each ->read() after this method is called. This will essentially save the binary stream from the serial port to a file. Only one file is opened. If a file is already open for saving, additional calls to this method are ignored.

If the second parameter is true, the filehandle is unbuffered. This allows the data to be immediately written to disk after each serial port read. The filehandle is buffered by default.

Returns 0 if a file is currently open, 1 on success and die()s if there is an error open()ing the file.


Stop saving raw data by closing the data dump file. Does nothing if no file is open. No return value.

Misc Notes

BR type packets appear in the stream at 20ms/div and longer. The parsed packets may not be useful at intervals greater than 20ms/div.


Death, taxes and software bugs. This is the first release of this module.



Can't read without it.

The forum for questions related to Velleman products. The protocol used to send the packets over the serial device are discussed on that site. Searching for "pps10 serial port" or "serial port protocol" should return relevant threads.


Narbey Derbekyan


Copyright (C) 2010 by Narbey Derbekyan

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.10.0 or, at your option, any later version of Perl 5 you may have available.