www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - RS232 / USB read/write?

reply Brian Hay <bhay construct3d.com> writes:
I'm a bit green with D. I've done some basic D1 + Tango + Derelict stuff 
but am keen to learn more about D2 + Phobos.

As a learning project I thought I'd like to try reading/writing data 
streams from/to RS232 and/or USB devices, something I've never done 
before in any language.

Can anyone give me some pointers and online references? Are there any D 
libraries for this?
Jan 31 2010
next sibling parent Mike James <foo bar.com> writes:
Brian Hay Wrote:

 I'm a bit green with D. I've done some basic D1 + Tango + Derelict stuff 
 but am keen to learn more about D2 + Phobos.
 
 As a learning project I thought I'd like to try reading/writing data 
 streams from/to RS232 and/or USB devices, something I've never done 
 before in any language.
 
 Can anyone give me some pointers and online references? Are there any D 
 libraries for this?

This something I wrote a while ago for D1 + Tango, it will probably need a bit of tweeking to get it to the latest compiler version. Sorry for having to include it in the body text... ============================================== module comms; private import tango.sys.Common, tango.time.Time, tango.stdc.stdint, tango.io.Stdout, StdC = tango.stdc.String, Integer = tango.text.convert.Integer, Float = tango.text.convert.Float, tango.text.Util, tango.stdc.Stringz, tango.core.Thread, tango.io.device.ThreadConduit; private import ascii; public class Comms { /* * Constants */ private const uint RX_CHAR_COUNT = 1; private const char[] ALT_NAME_PREFIX = r"\\.\"; private const char[] PORT_NOT_OPEN_EXCEPTION = "Serial Port not Open"; public const uint INFINITE_TIMEOUT = uint.max; /* * Enumerations */ public enum Parity : ubyte { None = cast(ubyte)PARITY_NONE, Odd = cast(ubyte)PARITY_ODD, Even = cast(ubyte)PARITY_EVEN, Mark = cast(ubyte)PARITY_MARK, Space = cast(ubyte)PARITY_SPACE } public enum StopBits : ubyte { One = cast(ubyte)ONESTOPBIT, OnePointFive = cast(ubyte)ONE5STOPBITS, Two = cast(ubyte)TWOSTOPBITS } public enum Handshake : int { None, Hardware, Software } private enum SigState : int { Low, High, NotUsed } private enum WaitFor { ThreadTerminate, Event, MaxSize } private HANDLE hComm = null; private HANDLE hRxThreadStarted = null; private HANDLE hRxThreadDone = null; private HANDLE hReadLine = null; private Thread receiveThread; private ThreadConduit tcRxConduit; private bool isOpen_ = false; private char[] portName_ = "COM1"; private uint baudrate_ = 9600; private int dataBits_ = 8; private StopBits stopBits_ = StopBits.One; private Parity parity_ = Parity.None; private Handshake handshake_ = Handshake.None; private char xonChar_ = ASCII.DC1; private char xoffChar_ = ASCII.DC3; private char errorChar_ = ASCII.NUL; private uint rxQueue_ = 0; private uint txQueue_ = 0; private SigState rtsState_ = SigState.NotUsed; private SigState dtrState_ = SigState.NotUsed; private SigState breakState_ = SigState.NotUsed; private uint rxTimeout_ = INFINITE_TIMEOUT; private uint txTimeoutConst_ = 0; private uint txTimeoutMult_ = 0; private short xonLowLevel_ = 0; private short xoffHighLevel_ = 0; private bool discardNull_ = false; /* * Delegates */ private void delegate(uint, char[]) dgDataEvent; private void delegate() dgTxEmptyEvent; private void delegate() dgBreakEvent; private void delegate(ModemStatus, ModemStatus) dgStatusChangeEvent; private void delegate(ErrorStatus) dgErrorEvent; this() { commonInit(); } this(char[] portName, uint baudrate, int dataBits, StopBits stopBits, Parity parity) { portName_ = portName; baudrate_ = baudrate; dataBits_ = dataBits; stopBits_ = stopBits; parity_ = parity; commonInit(); } this(char[] portName, uint baudrate) { portName_ = portName; baudrate_ = baudrate; commonInit(); } ~this() { close(); } private void commonInit() { tcRxConduit = new ThreadConduit(); } /************************************************************* * Read property * Returns: * Current state of the serial port. */ public bool isOpen() { return isOpen_; } /************************************************************* * Write property * Params: * value = 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 57600, 115200, 128000, 256000, default 9600. * Returns: * Current Baudrate setting. */ public uint baudrate(uint value) { return baudrate_ = value; } /************************************************************* * Read property * Returns: * Baudrate. */ public uint baudrate() { return baudrate_; } /************************************************************* * Write property * Params: * value = 5, 6, 7 or 8, default 8. * Returns: * Current data bits setting. */ public int dataBits(int value) { return dataBits_ = value; } /************************************************************* * Read property * Returns: * Current data bits setting. */ public int dataBits() { return dataBits_; } /************************************************************* * Write property * Params: * value = One, OnePointFive or Two, default One. * Returns: * Current stop bits setting. */ public StopBits stopBits(StopBits value) { return stopBits_ = value; } /************************************************************* * Read property * Returns: * Current stop bits setting. */ public StopBits stopBits() { return stopBits_; } /************************************************************* * Write property * Params: * value = None, Odd, Even, Mark or Space, default None. * Returns: * Current parity setting. */ public Parity parity(Parity value) { return parity_ = value; } /************************************************************* * Read property * Returns: * Current parity setting. */ public Parity parity() { return parity_; } /************************************************************* * Write property * Params: * value = port name i.e. COM1, default COM1. * Returns: * Current port name setting. */ public char[] portName(char[] value) { return portName_ = value; } /************************************************************* * Read property * Returns: * Current port name setting. */ public char[] portName() { return portName_; } /************************************************************* * Write property * Params: * value = None, Hardware or Software, default None. * Returns: * Current handshake setting. */ public Handshake handshake(Handshake value) { return handshake_ = value; } /************************************************************* * Read property * Returns: * Current handshake setting. */ public Handshake handshake() { return handshake_; } /************************************************************* * Write property * Params: * value = XON char. * Returns: * Current XON char. */ public char xonChar(char value) { return xonChar_ = value; } /************************************************************* * Read property * Returns: * Current XON char. */ public char xonChar() { return xonChar_; } /************************************************************* * Write property * Params: * value = XOFF char. * Returns: * Current XOFF char. */ public char xoffChar(char value) { return xoffChar_ = value; } /************************************************************* * Read property * Returns: * Current XOFF char. */ public char xoffChar() { return xoffChar_; } /************************************************************* * Write property * Params: * value = size of RX queue. * Returns: * Current size of RX queue. */ public uint rxQueue(uint value) { return rxQueue_ = value; } /************************************************************* * Read property * Returns: * Current size of RX queue. */ public uint rxQueue() { return rxQueue_; } /************************************************************* * Write property * Params: * value = size of TX queue. * Returns: * Current size of TX queue. */ public uint txQueue(uint value) { return txQueue_ = value; } /************************************************************* * Read property * Returns: * Current size of TX queue. */ public uint txQueue() { return txQueue_; } /************************************************************* * Write property * Params: * value = required RTS state. * Returns: * Current RTS state. */ public bool rts(bool value) { if (rtsState_ > SigState.High) { return false; } if (value) { if (EscapeCommFunction(hComm, SETRTS)) { rtsState_ = SigState.High; } else { throw new CommException("EscapeCommFunction [RTS]"); } } else { if (EscapeCommFunction(hComm, CLRRTS)) { rtsState_ = SigState.Low; } else { throw new CommException("EscapeCommFunction [RTS]"); } } return cast(bool)(rtsState_ == SigState.High); } /************************************************************* * Read property * Returns: * Current RTS state. */ public bool rts() { return cast(bool)(rtsState_ == SigState.High); } /************************************************************* * Write property * Params: * value = required DTR state. * Returns: * Current DTR state. */ public bool dtr(bool value) { if (dtrState_ > SigState.High) { return false; } if (value) { if (EscapeCommFunction(hComm, SETDTR)) { dtrState_ = SigState.High; } else { throw new CommException("EscapeCommFunction [DTR]"); } } else { if (EscapeCommFunction(hComm, CLRDTR)) { dtrState_ = SigState.Low; } else { throw new CommException("EscapeCommFunction [DTR]"); } } return cast(bool)(dtrState_ == SigState.High); } /************************************************************* * Read property * Returns: * Current DTR state. */ public bool dtr() { return cast(bool)(dtrState_ == SigState.High); } /************************************************************* * Write property * Params: * value = required BREAK state. * Returns: * Current BREAK state. */ public bool brk(bool value) { if (breakState_ > SigState.High) { return false; } if (value) { if (EscapeCommFunction(hComm, SETBREAK)) { breakState_ = SigState.High; } else { throw new CommException("EscapeCommFunction [BREAK]"); } } else { if (EscapeCommFunction(hComm, CLRBREAK)) { breakState_ = SigState.Low; } else { throw new CommException("EscapeCommFunction [BREAK]"); } } return cast(bool)(breakState_ == SigState.High); } /************************************************************* * Read property * Returns: * Current BREAK state. */ public bool brk() { return cast(bool)(breakState_ == SigState.High); } /************************************************************* * Write property * Params: * value = size of readLine timeout in ms. * Returns: * Current size of readLine timeout in ms. */ public uint rxTimeout(uint value) { return rxTimeout_ = value; } /************************************************************* * Read property * Returns: * Current size of readLine timeout in ms. */ public uint rxTimeout() { return rxTimeout_; } /************************************************************* * Write property * Params: * value = size of WriteTotalTimeoutMultiplier in ms. * Returns: * Current size of WriteTotalTimeoutMultiplier in ms. */ public uint txTimeoutMult(uint value) { return txTimeoutMult_ = value; } /************************************************************* * Read property * Returns: * Current size of WriteTotalTimeoutMultiplier in ms. */ public uint txTimeoutMult() { return txTimeoutMult_; } /************************************************************* * Write property * Params: * value = size of WriteTotalTimeoutConstant in ms. * Returns: * Current size of WriteTotalTimeoutConstant in ms. */ public uint txTimeoutConst(uint value) { return txTimeoutConst_ = value; } /************************************************************* * Read property * Returns: * Current size of WriteTotalTimeoutConstant in ms. */ public uint txTimeoutConst() { return txTimeoutConst_; } /************************************************************* * Write property * Params: * value = minimum number of bytes in input buffer before XON char is sent. * Returns: * Current size of XON low level. */ public short xonLowLevel(short value) { return xonLowLevel_ = value; } /************************************************************* * Read property * Returns: * Current size of XON low level. */ public short xonLowLevel() { return xonLowLevel_; } /************************************************************* * Write property * Params: * value = maximum number of bytes in input buffer before XOFF char is sent. * Returns: * Current size of XOFF high level. */ public short xoffHighLevel(short value) { return xoffHighLevel_ = value; } /************************************************************* * Read property * Returns: * Current size of XOFF high level. */ public short xoffHighLevel() { return xoffHighLevel_; } /************************************************************* * Write property * Params: * value = true or false, default false. * Returns: * Current discard null setting. */ public bool discardNull(bool value) { return discardNull_ = value; } /************************************************************* * Read property * Returns: * Current discard null setting. */ public bool discardNull() { return discardNull_; } /************************************************************* * Write property * Params: * value = required error char, default NULL. * Returns: * Current error char. */ public char errorChar(char value) { return errorChar_ = value; } /************************************************************* * Read property * Returns: * Current error char. */ public char errorChar() { return errorChar_; } /************************************************************* * Read property * Returns: * number of bytes in receive buffer. */ public uint bytesToRead() { return tcRxConduit.remaining; } /************************************************************* * Read property * Returns: * modem status. */ public ModemStatus modemStatus() { if (isOpen_) { uint scStatus = 0; if (!GetCommModemStatus(hComm, &scStatus)) { throw new CommException("GetCommModemStatus"); } return new ModemStatus(scStatus); } else { throw new CommException(PORT_NOT_OPEN_EXCEPTION, false); } } /************************************************************* * Method: opens the serial port. * Returns: * none. */ public void open() { if (hComm !is null) { throw new CommException("Port " ~ portName_ ~ " already open"); } if ((hComm = CreateFileA(toStringz(portName_), GENERIC_READ | GENERIC_WRITE, 0, null, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, null)) == INVALID_HANDLE_VALUE) { // try with alternate naming if ((hComm = CreateFileA(toStringz(ALT_NAME_PREFIX ~ portName_), GENERIC_READ | GENERIC_WRITE, 0, null, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, null)) == INVALID_HANDLE_VALUE) { throw new CommException("Failed to open " ~ portName_); } } if (GetFileType(hComm) != FILE_TYPE_CHAR) { throw new CommException(portName_ ~ " file handle is not valid"); } DCB dcb = {0}; dcb.DCBlength = dcb.sizeof; if (GetCommState(hComm, &dcb) == 0) { throw new CommException("GetCommState"); } dcb.BaudRate = baudrate_; dcb.ByteSize = dataBits_; dcb.Parity = parity_; dcb.StopBits = stopBits_; dcb.XonChar = xonChar_; dcb.XoffChar = xoffChar_; // flag settings dcb.flag0 = bm_DCB_fBinary; // must always be set to 1 // handshake flags switch (handshake_) { case Handshake.None: // dcb.fOutxCtsFlow = dcb.fOutxDsrFlow = dcb.fOutX = dcb.fInX = false; dcb.flag0 |= (RTS_CONTROL_DISABLE << bp_DCB_fRtsControl); dcb.flag0 |= (DTR_CONTROL_DISABLE << bp_DCB_fDtrControl); break; case Handshake.Hardware: dcb.flag0 |= (1 << bp_DCB_fOutxCtsFlow); dcb.flag0 |= (1 << bp_DCB_fOutxDsrFlow); // dcb.fOutX = dcb.fInX = false; dcb.flag0 |= (RTS_CONTROL_HANDSHAKE << bp_DCB_fRtsControl); dcb.flag0 |= (DTR_CONTROL_HANDSHAKE << bp_DCB_fDtrControl); rtsState_ = SigState.High; dtrState_ = SigState.High; break; case Handshake.Software: // dcb.fOutxCtsFlow = dcb.fOutxDsrFlow = false; dcb.flag0 |= (1 << bp_DCB_fOutX); dcb.flag0 |= (1 << bp_DCB_fInX); dcb.flag0 |= (RTS_CONTROL_DISABLE << bp_DCB_fRtsControl); dcb.flag0 |= (DTR_CONTROL_DISABLE << bp_DCB_fDtrControl); break; default: throw new CommException("Handshake"); break; } if ((xonLowLevel == 0) || (xoffHighLevel == 0)) { COMMPROP cp; if (!GetCommProperties(hComm, &cp)) { throw new CommException("GetCommProperties"); } const int XON_XOFF_LIMITS_PERCENT = 10; const short XON_XOFF_DEFAULT_LIMITS = 8; if (cp.dwCurrentRxQueue > 0) { dcb.XoffLim = dcb.XonLim = cast(short)(cp.dwCurrentRxQueue / XON_XOFF_LIMITS_PERCENT); } else { dcb.XoffLim = dcb.XonLim = XON_XOFF_DEFAULT_LIMITS; } } else { dcb.XoffLim = xoffHighLevel; dcb.XonLim = xonLowLevel; } if (discardNull_) { dcb.flag0 |= (1 << bp_DCB_fNull); } // set the parity flag if parity-checking set if ((parity_ == Parity.Odd) || (parity_ == Parity.Even)) { dcb.flag0 |= (1 << bp_DCB_fParity); } dcb.ErrorChar = errorChar_; if (SetCommState(hComm, &dcb) == 0) { throw new CommException("SetCommState"); } if ((rxQueue != 0) || (txQueue != 0)) { if (!SetupComm(hComm, rxQueue, txQueue)) { throw new CommException("SetupComm"); } } COMMTIMEOUTS commTimeouts; commTimeouts.ReadIntervalTimeout = INFINITE_TIMEOUT; commTimeouts.ReadTotalTimeoutMultiplier = 0; commTimeouts.ReadTotalTimeoutConstant = 0; commTimeouts.WriteTotalTimeoutMultiplier = txTimeoutMult_; commTimeouts.WriteTotalTimeoutConstant = txTimeoutConst_; if (SetCommTimeouts(hComm, &commTimeouts) == 0) { throw new CommException("SetCommTimeouts"); } hRxThreadStarted = CreateEventA(null, false, false, null); hRxThreadDone = CreateEventA(null, false, false, null); hReadLine = CreateEventA(null, true, false, null); receiveThread = new Thread(&receiveHandler); receiveThread.start(); uint waitForThreadStart = WaitForSingleObject(hRxThreadStarted, INFINITE); CloseHandle(hRxThreadStarted); hRxThreadStarted = INVALID_HANDLE_VALUE; isOpen_ = true; } /************************************************************* * Method: closes the serial port. * Returns: * none. */ public void close() { if (isOpen_) { flush(); tcRxConduit.close(); SetEvent(hRxThreadDone); receiveThread.join(); CloseHandle(hComm); hComm = INVALID_HANDLE_VALUE; } } /************************************************************* * Delegate: called when data is available. * Returns: * none. */ public void onData(void delegate(uint, char[]) dg) { dgDataEvent = dg; } /************************************************************* * Delegate: called when transmit buffer is empty. * Returns: * none. */ public void onTxEmpty(void delegate() dg) { dgTxEmptyEvent = dg; } /************************************************************* * Delegate: called when a break is detected. * Returns: * none. */ public void onBreak(void delegate() dg) { dgBreakEvent = dg; } /************************************************************* * Delegate: called when line status change is detected. * Returns: * none. */ public void onStatusChange(void delegate(ModemStatus, ModemStatus) dg) { dgStatusChangeEvent = dg; } /************************************************************* * Delegate: called when error event is detected. * Returns: * none. */ public void onError(void delegate(ErrorStatus) dg) { dgErrorEvent = dg; } /************************************************************* * Method: gets a char from the receive buffer. * Returns: * int - char if available or -1 if buffer empty. */ public int readChar() { char[1] ch; if (tcRxConduit.remaining > 0) { tcRxConduit.input.read(ch); } else { // return -1 if end of stream return -1; } return cast(int)ch[0]; } /************************************************************* * Method: gets a byte from the receive buffer. * Returns: * int - byte if available or -1 if buffer empty. */ public int readByte() { return readChar(); } /************************************************************* * Method: gets all chars from the receive buffer. * Returns: * char string if available or empty string if not. */ public char[] readExisting() { if (tcRxConduit.remaining > 0) { char[] str = new char[tcRxConduit.remaining]; tcRxConduit.input.read(str); return str; } else { return ""; } } /************************************************************* * Method: gets a string terminated with a newline from the receive buffer. * Returns: * int - char string if available or CommTimeoutException if timedout. */ public char[] readLine() { char[] str; char[1] ch; uint n = 0; str.length = 256; for (;;) { switch (WaitForSingleObject(hReadLine, rxTimeout_)) { ResetEvent(hReadLine); case WAIT_OBJECT_0: tcRxConduit.input.read(ch); switch (ch[0]) { case '\r': str.length = n; return str; break; case '\n': break; default: if (n == str.length) { str.length = str.length + 64; } str[n++] = ch[0]; break; } break; case WAIT_TIMEOUT: throw new CommTimeoutException("Comms Timeout"); break; case WAIT_FAILED: throw new CommException("Comms ReadLine Timeout"); break; } } } /************************************************************* * Method: transmits a char ch immediately - ignores the transmit buffer. * Returns: * none. */ public void writeImmediate(char ch) { if (isOpen_) { if (!TransmitCommChar(hComm, ch)) { throw new CommException("TransmitCommChar"); } } else { throw new CommException(PORT_NOT_OPEN_EXCEPTION, false); } } /************************************************************* * Method: transmits the string str. * Returns: * none. */ public void write(char[] str) { writeStr(str); } /************************************************************* * Method: transmits the string str appended with a CR/LF. * Returns: * none. */ public void writeln(char[] str) { writeStr(str ~ "\r\n"); } /************************************************************* * Method: transmits the char ch. * Returns: * none. */ public void write(char ch) { char[1] str; str[0] = ch; writeStr(str); } private void writeStr(char[] str) { if (isOpen_) { uint numBytesWritten = 0; OVERLAPPED ovTransmit = {0}; ovTransmit.hEvent = CreateEventA(null, true, false, null); if (!WriteFile(hComm, cast(void*)str, str.length, &numBytesWritten, &ovTransmit)) { if (SysError.lastCode() == ERROR_IO_PENDING) { switch(WaitForSingleObject(ovTransmit.hEvent, INFINITE_TIMEOUT)) { case WAIT_OBJECT_0: if (GetOverlappedResult(hComm, &ovTransmit, &numBytesWritten, false) == 0) { throw new CommException("GetOverlappedResult"); } else { if (str.length != numBytesWritten) { throw new CommException("Write Failed", false); } } break; case WAIT_FAILED: throw new CommException("WaitforSingleObject"); break; } } else { throw new CommException("WriteFile"); } } CloseHandle(ovTransmit.hEvent); } else { throw new CommException(PORT_NOT_OPEN_EXCEPTION, false); } } /************************************************************* * Method: flushes the transmit buffer. * Returns: * none. */ public void flush() { if (isOpen_) { if (FlushFileBuffers(hComm) == 0) { throw new CommException("FlushFileBuffers"); } } else { throw new CommException(PORT_NOT_OPEN_EXCEPTION, false); } } private void receiveHandler() { bool receiveThreadActive = true; bool firstTime = true; uint evMask = 0; uint nBytesRead; byte[] localRxBuffer = new byte[RX_CHAR_COUNT]; HANDLE[WaitFor.MaxSize] hWaitfor; OVERLAPPED ovReceive; StdC.memset(&ovReceive, 0x00, ovReceive.sizeof); ovReceive.hEvent = CreateEventA(null, true, false, null); hWaitfor[WaitFor.ThreadTerminate] = hRxThreadDone; try { while (receiveThreadActive) { if (!SetCommMask(hComm, EV_RXCHAR | EV_TXEMPTY | EV_ERR | EV_BREAK | EV_CTS | EV_DSR | EV_RLSD | EV_RING)) { throw new CommException("SetCommMask"); } if (firstTime) { SetEvent(hRxThreadStarted); firstTime = false; } evMask = 0; if (!WaitCommEvent(hComm, &evMask, &ovReceive)) { if (SysError.lastCode() != ERROR_IO_PENDING) { throw new CommException("WaitCommEvent"); } } hWaitfor[WaitFor.Event] = ovReceive.hEvent; switch (WaitForMultipleObjects(WaitFor.MaxSize, cast(HANDLE*)hWaitfor, false, INFINITE)) { case WAIT_OBJECT_0 + WaitFor.ThreadTerminate: receiveThreadActive = false; isOpen_ = false; break; case WAIT_OBJECT_0 + WaitFor.Event: if ((evMask & EV_ERR) == EV_ERR) { uint errors = 0; if (ClearCommError(hComm, &errors, null)) { bool isError = false; if (((errors & CE_FRAME) == CE_FRAME) || ((errors & CE_IOE) == CE_IOE) || ((errors & CE_OVERRUN) == CE_OVERRUN) || ((errors & CE_RXOVER) == CE_RXOVER) || ((errors & CE_RXPARITY) == CE_RXPARITY) || ((errors & CE_TXFULL) == CE_TXFULL)) { isError = true; } if (isError) { if (dgErrorEvent !is null) { dgErrorEvent(new ErrorStatus(errors)); } } else { if (errors == CE_BREAK) { evMask |= EV_BREAK; } else { throw new CommException("Break Error", false); } } } else { throw new CommException("ClearCommError"); } } if ((evMask & EV_RXCHAR) == EV_RXCHAR) { do { if (!ReadFile(hComm, cast(void*)localRxBuffer, RX_CHAR_COUNT, &nBytesRead, &ovReceive)) { throw new CommException("ReadFile"); } if (nBytesRead == RX_CHAR_COUNT) { tcRxConduit.output.write(localRxBuffer); } } while(nBytesRead > 0); if (dgDataEvent !is null) { uint len = tcRxConduit.remaining; char[] str = new char[len]; tcRxConduit.input.read(str); dgDataEvent(len, str); } SetEvent(hReadLine); } if ((evMask & EV_TXEMPTY) == EV_TXEMPTY) { if (dgTxEmptyEvent !is null) { dgTxEmptyEvent(); } } if ((evMask & EV_BREAK) == EV_BREAK) { if (dgBreakEvent !is null) { dgBreakEvent(); } } if (dgStatusChangeEvent !is null) { uint scMask = 0; if ((evMask & EV_CTS) == EV_CTS) { scMask |= MS_CTS_ON; } if ((evMask & EV_DSR) == EV_DSR) { scMask |= MS_DSR_ON; } if ((evMask & EV_RLSD) == EV_RLSD) { scMask |= MS_RLSD_ON; } if ((evMask & EV_RING) == EV_RING) { scMask |= MS_RING_ON; } if (scMask != 0) { uint scStatus = 0; if (!GetCommModemStatus(hComm, &scStatus)) { throw new CommException("GetCommModemStatus"); } dgStatusChangeEvent(new ModemStatus(scMask), new ModemStatus(scStatus)); } } break; case WAIT_FAILED: throw new CommException("WaitForMultipleObjects"); break; } ResetEvent(ovReceive.hEvent); } } catch (CommException e) { Stdout.format("Comms Exception: {0}", e.toString); } } /* * Exceptions */ private class CommException : Exception { this(char[] msg, bool showSysErr = true) { if (showSysErr) { msg ~= " - " ~ SysError.lastMsg(); } super(msg); } } private class CommTimeoutException : Exception { this(char[] msg) { super(msg); } } } public class ModemStatus { private uint status; this(uint s) { status = s; } public bool cts() { return cast(bool)((status & MS_CTS_ON) == MS_CTS_ON); } public bool dsr() { return cast(bool)((status & MS_DSR_ON) == MS_DSR_ON); } public bool rlsd() { return cast(bool)((status & MS_RLSD_ON) == MS_RLSD_ON); } public bool ri() { return cast(bool)((status & MS_RING_ON) == MS_RING_ON); } } public class ErrorStatus { private uint error; this(uint err) { error = err; } public bool frame() { return cast(bool)((error & CE_FRAME) == CE_FRAME); } public bool io() { return cast(bool)((error & CE_IOE) == CE_IOE); } public bool overrun() { return cast(bool)((error & CE_OVERRUN) == CE_OVERRUN); } public bool rxOverflow() { return cast(bool)((error & CE_RXOVER) == CE_RXOVER); } public bool rxParity() { return cast(bool)((error & CE_RXPARITY) == CE_RXPARITY); } public bool txFull() { return cast(bool)((error & CE_TXFULL) == CE_TXFULL); } } debug (COMMS) { private import tango.io.Console, tango.io.stream.TextFileStream; int main() { Comms comms = new Comms("COM1", 38400, 8, Comms.StopBits.One, Comms.Parity.None); void textIn(uint count, char[] str) { /* char ch; while (comms.bytesToRead > 0) { switch (ch = cast(char)comms.readChar()) { case '\r': Stdout.newline; break; default: Stdout.format("{0}", ch).flush; break; } } */ for (uint n = 0; n < count; ++n) { switch (str[n]) { case '\r': Stdout.newline; break; default: Stdout.format("{0}", str[n]).flush; break; } } } void txEmptyDetected() { Stdout.formatln("TX EMPTY"); } void breakDetected() { Stdout.formatln("BREAK"); } void statusChange(ModemStatus mask, ModemStatus status) { Stdout.formatln("CTS : {0}, {1}", mask.cts, status.cts); Stdout.formatln("DSR : {0}, {1}", mask.dsr, status.dsr); Stdout.formatln("RLSD : {0}, {1}", mask.rlsd, status.rlsd); Stdout.formatln("RI : {0}, {1}", mask.ri, status.ri); Stdout.newline; } void errorsDetected(ErrorStatus es) { Stdout.formatln("Frame : {0}", es.frame); Stdout.formatln("IO : {0}", es.io); Stdout.formatln("Overrun : {0}", es.overrun); Stdout.formatln("RX Overflow : {0}", es.rxOverflow); Stdout.formatln("RX Parity : {0}", es.rxParity); Stdout.formatln("TX Full : {0}", es.txFull); Stdout.newline; } comms.handshake = Comms.Handshake.None; comms.onData = &textIn; //comms.onTxEmpty = &txEmptyDetected; comms.onBreak = &breakDetected; comms.onStatusChange = &statusChange; comms.onError = &errorsDetected; comms.rxTimeout = 1000; comms.open(); Stdout.formatln("Modem Status : {0}", comms.modemStatus.cts); char[] str = new char[10]; comms.writeln("Hello World"); comms.writeln("the quick brown fox jumped over the lazy dog"); comms.writeImmediate('!'); //auto input = new TextFileInput("comms.d"); //foreach (line; input) { // comms.writeln(line); //} //input.close; /* while (1) { if (comms.bytesToRead > 0) { try { Stdout.formatln("{0}", comms.readLine()); } catch (Comms.CommTimeoutException e) { Stdout.formatln(" "); } } } */ while ((str = Cin.get) != "q") { comms.write(str); } comms.close(); return 0; } } ============================================== regards, -=mike=-
Feb 01 2010
prev sibling parent "Nick Sabalausky" <a a.a> writes:
"Brian Hay" <bhay construct3d.com> wrote in message 
news:hk51h7$1t39$1 digitalmars.com...
 I'm a bit green with D. I've done some basic D1 + Tango + Derelict stuff 
 but am keen to learn more about D2 + Phobos.

 As a learning project I thought I'd like to try reading/writing data 
 streams from/to RS232 and/or USB devices, something I've never done before 
 in any language.

 Can anyone give me some pointers and online references? Are there any D 
 libraries for this?

If you're doing it on Windows, there's the "inpout32.dll" ( http://logix4u.net/Legacy_Ports/Parallel_Port/Inpout32.dll_for_Window _98/2000/NT/XP.html ) that's commonly used for basic hardware I/O (or at least for the old standard serial/parallel ports). I've used it in a C program before to drive a homemade parallel-port EEPROM burner. It works well (although my homemade burner didn't work quite as well ;) ). It is a C thing, but it should be easy to make a D binding for it (just look up accessing C libs from D on the digital mars site or the D wiki). If you have any timing-critical portions, just make sure to either disable the GC and/or avoid any hidden (or non-hidden) allocations during those portions.
Feb 01 2010