class io.Serial

Overview

This class provides high-level asynchronous interface for accessing hardware or virtual serial ports. More…

import "io_base.jncx"
import "io_Serial.jnc"

class Serial {
    // fields

    io.SerialEvents readonly volatile m_activeEvents;
    std.Error const* readonly volatile m_ioError;
    bool readonly m_isOpen;

    // properties

    uint_t autoget property m_baudRate;
    io.SerialFlowControl autoget property m_flowControl;
    uint_t autoget property m_dataBits;
    io.SerialStopBits autoget property m_stopBits;
    io.SerialParity autoget property m_parity;
    io.SerialStatusLines const property m_statusLines;
    bool autoget property m_dtr;
    bool autoget property m_rts;
    bool autoget property m_breakCondition;
    uint_t autoget property m_updateInterval;
    uint_t autoget property m_readInterval;
    uint_t autoget property m_readParallelism;
    size_t autoget property m_readBlockSize;
    size_t autoget property m_readBufferSize;
    size_t autoget property m_writeBufferSize;
    io.SerialOptions autoget property m_options;
    uintptr_t const property m_osHandle;

    // construction

    construct();
    destruct();

    // methods

    bool errorcode open(string_t name);
    void close();

    bool errorcode setupDevice(
        uint_t baudRate,
        uint_t dataBits = 8,
        io.SerialStopBits stopBits = SerialStopBits._1,
        io.SerialParity parity = SerialParity.None,
        io.SerialFlowControl flowControl = SerialFlowControl.None,
        uint_t readInterval = 0,
        bool dtr = false,
        bool rts = false
    );

    io.SerialLineErrors clearLineErrors();

    size_t errorcode read(
        void* p,
        size_t size
    );

    size_t errorcode write(
        void const* p,
        size_t size
    );

    long errorcode wait(
        io.SerialEvents eventMask,
        void function* handler(io.SerialEvents triggeredEvents)
    );

    bool errorcode cancelWait(long handle);

    io.SerialEvents blockingWait(
        io.SerialEvents eventMask,
        uint_t timeout = -1
    );

    io.SerialEvents async asyncWait(io.SerialEvents eventMask);

    // aliases

    alias dispose = close;
};

Detailed Documentation

This class provides high-level asynchronous interface for accessing hardware or virtual serial ports.

Serial ports are represented as files in most operating systems, so working with serial port looks pretty much the same as working with any other file stream. However, besides read and write operations serial port files also expose a set of serial-specific properties such as baud rate, status and control lines and so on.

A typical sequence of steps when working with a serial port usually looks like this:

  • Open a serial port with open method;

  • Assign IO event handler with wait method. You would probably also want to schedule your event handler to be run in particular environment(e.g., in a specific thread) and partially apply some syncrhonization ID to discard late events;

  • Configure serial port settings(m_baudRate, m_dataBits etc)

  • When io.SerialEvents.IncomingData event is fired, read from the serial port using read method;

  • Write to the serial port stream using write method;

  • Suspend writing if the return value of write is less than requested;

  • Resume writing on io.SerialEvents.WriteBufferReady event;

  • Close the serial port when no longer needed with close method.

Sample code:

class MyDialog {
    // ...

    io.Serial m_serial;
    uint_t m_syncId;

    void open(
        string_t portName,
        uint_t baudRate
    );

    void close();
    void waitSerialEvents();

    void onWriteButtonClicked();

    void onSerialEvent(
        uint_t syncId,
        io.SerialEvents triggeredEvents
    );
}

MyDialog.open(
    string_t portName,
    uint_t baudRate
) {
    m_serial.open(portName);
    m_serial.m_baudRate = baudRate;
    m_serial.m_dataBits = 8;
    m_serial.m_stopBits = io.SerialStopBits._1;
    // ...

    waitSerialEvents();

catch:
    // handle the error...
}

MyDialog.close() {
    m_serial.close();
    m_syncId++; // events past this point will be discarded as 'late'
}

MyDialog.waitSerialEvent() {
    io.SerialEvents eventMask =
        io.SerialEvents.IoError |
        io.SerialEvents.IncomingData;

    m_serial.wait(eventMask, onSerialEvent ~(++m_syncId) @
        g_mainThreadScheduler);
}

MyDialog.onWriteButtonClicked() {
    static char data[] = "a quick brown fox jumps over the lazy dog";
    m_serial.write(data, sizeof(data));
}

MyDialog.onSerialEvent(
    uint_t syncId,
    io.SerialEvents triggeredEvents
) {
    if (syncId != m_syncId) // late event
        return;

    if (triggeredEvents & io.SerialEvents.IoError) {
        string_t errorString = m_fileStream.m_ioError.m_description;
        // ...
    }

    if (triggeredEvents & io.SerialEvents.IncomingData) {
        char buffer[256];
        size_t size = m_fileStream.read(buffer, sizeof(buffer));
        // ...
    }

    waitSerialEvents(); // restart wait
}

See also:

io.SerialEvents, io.File, io.FileStream

Fields

bool readonly m_isOpen

Holds the open status for serial port, i.e. true if opened; false otherwise.

Properties

uint_t autoget property m_baudRate

This property is used for getting and setting [1] baud rate, i.e. the number of signal changes per second, which directly affects the speed of data transmission.

Different hardware serial ports may or may not support some particular baud rates. Unfortunately, there is no reliable cross-platform method of building a list of supported baud rates, so the only way is to probe.

io.SerialFlowControl autoget property m_flowControl

This property is used for getting and setting [1] flow control, i.e. the protocol of attempted prevention of data loss during serial transmission which may happen when transmission side sends data faster than the receiving side is able to process it.

See io.SerialFlowControl enumeration for the list of valid values.

uint_t autoget property m_dataBits

This property is used for getting and setting [1] byte size, i.e. the number of bits in each character transmitted over the serial line. Valid values range from 5 to 8; normally 8 data bits are used.

io.SerialStopBits autoget property m_stopBits

This property is used for getting and setting [1] the number of serial stop bits, i.e. auxillary(non-data) bits sent at the end of each character to re-synchronize the receiver; normally 1 stop bit is used (designated by io.SerialStopBits._1)

See io.SerialStopBits enumeration for the list of valid values.

io.SerialParity autoget property m_parity

This property is used for getting and setting [1] serial parity control, i.e. the protocol of attempted error detection during serial transmission. The idea is to send an extra parity bit so that the total number of one’s including data bits and this parity bit is always odd or always even.

This, however, is a rather weak error detection method; usually it’s much better to employ other checksums/hash checks after the data has been transmitted.

See io.SerialParity for the list of valid values.

io.SerialStatusLines const property m_statusLines

This property is used for getting the values of serial status lines (DSR, CTS, DCD, RING).

The returned value is a combination of io.SerialStatusLines enumeration members, showing which status lines are high(or 0 if all are low).

bool autoget property m_dtr

This property is used for getting and setting [1] DTR (data-terminal-ready) serial control line.

bool autoget property m_rts

This property is used for getting and setting [1] RTS (ready-to-send) serial control line.

bool autoget property m_breakCondition

This property is used for getting and setting [1] RTS (ready-to-send) serial control line.

Methods

bool errorcode open(string_t name)

Opens a serial port.

The function accepts a single name argument, which specifies the name of the serial port device.

Returns true on success. If serial port could not be opened, IO error supplied by operating system is set and then the function returns false [2].

void close()

Closes a previously opened serial port, does nothing if the port is not opened. This function always succeeds.

Sometimes it may be convenient to use disposable pattern to ensure timely invokation of close [3].

size_t errorcode read(
    void* p,
    size_t size
)

Attempts to read up to size bytes from the serial port into the buffer pointed to by p.

Returns the actual amount of bytes read on success. If read operation is unsuccessful, IO error supplied by the operating system is set and then the function returns -1 [2].

Normally you would call this function from within your event handler for IncomingData event. If this function is called when there is no incoming data, it blocks until either the data arrives, or the serial port is closed.

size_t errorcode write(
    void const* p,
    size_t size
)

Attempts to write size bytes from the buffer pointed to by p to the serial port.

Returns the actual amount of bytes written on success. If write operation is unsuccessful, IO error supplied by the operating system is set and then the function returns -1 [2].

If the return value shows less bytes than specified by size argument, you should suspend further transmission until the file stream is ready to accept more data. When this happens, WriteBufferReady event is fired and transmission could be resumed.

Aliases

alias dispose = close

Effectively makes io.Serial a disposable class [3].


Footnotes