Simple Serial Port Windows Interface












0












$begingroup$


I implemented a very simple blocking uart interface. Most of the serial instruments I interface with are master/slave and I have to wait on data to proceed forward. I am contemplating add some template code to accept any container iterator, but for now I will probably use array or vector. Also, I open the serial port immediately in the constructor and only close it in the destructor. If I don't keep it open some of the other calls will not perform their intended function.



Things I implemented that I'm working on understanding better




  1. Pimpl Idiom

  2. Exception Handling

  3. RAII

  4. Constructor defaults


SerialPort.h



#pragma once
#include <memory>
#include <vector>
#include <string>
#include <chrono>

namespace Lite
{
class SerialPort
{
public:
enum class PARITY { NONE, ODD, EVEN, MARK, SPACE };
enum class STOPBITS { ONE, ONEPT5, TWO };
enum class FLOWCONTROL { SW, HW, NONE };

SerialPort(const std::string port = "COM1",
const uint32_t baudRate = 9600,
const PARITY parity = PARITY::NONE,
const STOPBITS stopBit = STOPBITS::ONE,
const FLOWCONTROL flowControl = FLOWCONTROL::NONE,
const uint8_t databits = 8);
~SerialPort();

SerialPort(const SerialPort&) = delete;
SerialPort(SerialPort&&) = delete;

SerialPort& operator=(const SerialPort&) = delete;
SerialPort& operator=(SerialPort&&) = delete;

size_t ReadBytes(uint8_t* data, const size_t bytesToRead);
size_t SendBytes(const uint8_t* data, const size_t bytesToSend);

// thinking of adding a container template version of read and send

void SetRxTimeouts(const std::chrono::milliseconds& timeOut,
const std::chrono::milliseconds& readIntervalTimeout);

void FlushRx();
void FlushTx();

size_t CurrentRxQueueSize();
size_t CurrentTxQueueSize();

size_t CurrentRxInQueue();
size_t CurrentTxInQueue();

private:
struct Impl;
std::unique_ptr<Impl> m_impl;
};

class SerialPortRuntimeException
: public std::runtime_error
{
public:
SerialPortRuntimeException(const char *message)
: std::runtime_error(message) {}
};

class SerialPortRangeError
: public std::range_error
{
public:
SerialPortRangeError(const char *message)
: std::range_error(message) {}
};
}


SerialPort.cpp



#if defined(_WIN32)

#include <Windows.h>
#include <CommCtrl.h>
#include <sstream>
#include <string>
#include <stdexcept>
#include "SerialPort.h"

struct Lite::SerialPort::Impl
{
BYTE ParityValue(const SerialPort::PARITY parity);
BYTE StopBitsValue(const SerialPort::STOPBITS stopBits);
DCB& FlowControlValue(const SerialPort::FLOWCONTROL, DCB& dcb);

std::string GetCommError();
HANDLE m_hCommPort;
};

std::string Lite::SerialPort::Impl::GetCommError()
{
LPSTR lpMsgBuf;
DWORD errCode = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
errCode,
0, // Default language
(LPTSTR)&lpMsgBuf,
0,
NULL
);

std::stringstream temp;
temp << lpMsgBuf;
return temp.str();
}

Lite::SerialPort::SerialPort(const std::string name, const uint32_t baudRate,
const PARITY parity, const STOPBITS stopBit, const FLOWCONTROL flowControl,
const uint8_t databits)
: m_impl(std::make_unique <Impl>())
{
m_impl->m_hCommPort = CreateFile(name.c_str(),
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
0,
0
);
if(m_impl->m_hCommPort == INVALID_HANDLE_VALUE) {
throw SerialPortRuntimeException(m_impl->GetCommError().c_str());
}

DCB dcb;
dcb.DCBlength = sizeof(DCB);

if(!GetCommState(m_impl->m_hCommPort, &dcb))
throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

dcb.BaudRate = baudRate;
dcb.ByteSize = databits;
dcb.Parity = m_impl->ParityValue(parity);
dcb.StopBits = m_impl->StopBitsValue(stopBit);
m_impl->FlowControlValue(flowControl, dcb);

if (!SetCommState(m_impl->m_hCommPort, &dcb))
throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

return;
}

Lite::SerialPort::~SerialPort()
{
CloseHandle(m_impl->m_hCommPort);

return;
}

size_t Lite::SerialPort::ReadBytes(uint8_t* data, const size_t bytesToRead)
{
auto tempBytesToRead = bytesToRead;

if (tempBytesToRead > MAXDWORD)
tempBytesToRead = MAXDWORD;

DWORD bytesRead;

if (!ReadFile(m_impl->m_hCommPort, &data,
tempBytesToRead, &bytesRead, NULL))
throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

return bytesRead;
}

size_t Lite::SerialPort::SendBytes(const uint8_t* data, const size_t bytesToSend)
{
auto tempBytesToSend = bytesToSend;

if (tempBytesToSend > MAXDWORD)
tempBytesToSend = MAXDWORD;

DWORD sentSize;
if(!WriteFile(m_impl->m_hCommPort, &data,
tempBytesToSend, &sentSize, NULL))
throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

return sentSize;
}

void Lite::SerialPort::SetRxTimeouts(const std::chrono::milliseconds& timeOut,
const std::chrono::milliseconds& readIntervalTimeout)
{
COMMTIMEOUTS to;

if (!GetCommTimeouts(m_impl->m_hCommPort, &to))
throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

to.ReadIntervalTimeout = readIntervalTimeout.count();
to.ReadTotalTimeoutMultiplier = 0;
to.ReadTotalTimeoutConstant = timeOut.count();

if(!SetCommTimeouts(m_impl->m_hCommPort, &to))
throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

return;
}

void Lite::SerialPort::FlushRx()
{
if (!PurgeComm(m_impl->m_hCommPort, PURGE_RXCLEAR))
throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

return;
}

void Lite::SerialPort::FlushTx()
{
if (!PurgeComm(m_impl->m_hCommPort, PURGE_TXCLEAR))
throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

return;
}

size_t Lite::SerialPort::CurrentRxQueueSize()
{
COMMPROP cp;

if (!GetCommProperties(m_impl->m_hCommPort, &cp))
throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

return cp.dwCurrentRxQueue;
}

size_t Lite::SerialPort::CurrentTxQueueSize()
{
COMMPROP cp;

if (!GetCommProperties(m_impl->m_hCommPort, &cp))
throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

return cp.dwCurrentTxQueue;
}

size_t Lite::SerialPort::CurrentRxInQueue()
{
COMSTAT cs;
DWORD error;

if (!ClearCommError(m_impl->m_hCommPort, &error, &cs))
throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

return cs.cbInQue;
}

size_t Lite::SerialPort::CurrentTxInQueue()
{
COMSTAT cs;
DWORD error;

if (!ClearCommError(m_impl->m_hCommPort, &error, &cs))
throw std::runtime_error(m_impl->GetCommError().c_str());

return cs.cbOutQue;
}

BYTE Lite::SerialPort::Impl::ParityValue(const SerialPort::PARITY parity)
{
BYTE temp;

switch (parity)
{
case SerialPort::PARITY::NONE:
temp = NOPARITY;
break;
case SerialPort::PARITY::ODD:
temp = ODDPARITY;
break;
case SerialPort::PARITY::EVEN:
temp = EVENPARITY;
break;
case SerialPort::PARITY::MARK:
temp = MARKPARITY;
break;
case SerialPort::PARITY::SPACE:
temp = SPACEPARITY;
break;
default:
throw SerialPortRangeError("Parity Enum out of Range");
break;
}

return temp;
}

BYTE Lite::SerialPort::Impl::StopBitsValue(const SerialPort::STOPBITS stopBits)
{
BYTE temp;
switch (stopBits)
{
case SerialPort::STOPBITS::ONE:
temp = ONESTOPBIT;
break;
case SerialPort::STOPBITS::ONEPT5:
temp = ONE5STOPBITS;
break;
case SerialPort::STOPBITS::TWO:
temp = TWOSTOPBITS;
break;
default:
throw SerialPortRangeError("Stop Bits Enum out of range");
break;
}

return temp;
}

DCB& Lite::SerialPort::Impl::FlowControlValue(const SerialPort::FLOWCONTROL flowcontrol,
DCB& dcb)
{
switch (flowcontrol)
{
case Lite::SerialPort::FLOWCONTROL::SW:
dcb.fOutxCtsFlow = false;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
dcb.fOutX = true;
dcb.fInX = true;
break;
case Lite::SerialPort::FLOWCONTROL::HW:
dcb.fOutxCtsFlow = true;
dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
dcb.fOutX = false;
dcb.fInX = false;
break;
case Lite::SerialPort::FLOWCONTROL::NONE:
dcb.fOutxCtsFlow = false;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
dcb.fOutX = false;
dcb.fInX = false;
break;
default:
throw SerialPortRangeError("Flow Control Enum out of range");
break;
}

return dcb;
}

#endif


main.cpp



#include <iostream>
#include "SerialPort.h"

void HandleException()
{
try { throw; }
catch (const Lite::SerialPortRuntimeException &e)
{
std::cerr << e.what() << 'n'; // catches standard error message
}
catch (const Lite::SerialPortRangeError &e)
{
std::cerr << e.what() << 'n';; // catches standard error message
}
return;
}

int main()
{
try
{
Lite::SerialPort sp{ "COM5", 4800 };

sp.FlushRx();
sp.FlushTx();

std::cout << "rx queue size: " << sp.CurrentRxQueueSize() << 'n';
std::cout << "rx in queue size: " << sp.CurrentRxInQueue() << 'n';

sp.SetRxTimeouts(std::chrono::milliseconds(1000),
std::chrono::milliseconds(1000));

std::vector<uint8_t> writeBuf(100);
sp.SendBytes(writeBuf.data(), writeBuf.size());

std::vector<uint8_t> test(sp.CurrentRxQueueSize());
test.resize(sp.ReadBytes(test.data(), 100));

sp.FlushRx();
sp.FlushTx();
}
catch (...)
{
HandleException();
}
}









share|improve this question











$endgroup$

















    0












    $begingroup$


    I implemented a very simple blocking uart interface. Most of the serial instruments I interface with are master/slave and I have to wait on data to proceed forward. I am contemplating add some template code to accept any container iterator, but for now I will probably use array or vector. Also, I open the serial port immediately in the constructor and only close it in the destructor. If I don't keep it open some of the other calls will not perform their intended function.



    Things I implemented that I'm working on understanding better




    1. Pimpl Idiom

    2. Exception Handling

    3. RAII

    4. Constructor defaults


    SerialPort.h



    #pragma once
    #include <memory>
    #include <vector>
    #include <string>
    #include <chrono>

    namespace Lite
    {
    class SerialPort
    {
    public:
    enum class PARITY { NONE, ODD, EVEN, MARK, SPACE };
    enum class STOPBITS { ONE, ONEPT5, TWO };
    enum class FLOWCONTROL { SW, HW, NONE };

    SerialPort(const std::string port = "COM1",
    const uint32_t baudRate = 9600,
    const PARITY parity = PARITY::NONE,
    const STOPBITS stopBit = STOPBITS::ONE,
    const FLOWCONTROL flowControl = FLOWCONTROL::NONE,
    const uint8_t databits = 8);
    ~SerialPort();

    SerialPort(const SerialPort&) = delete;
    SerialPort(SerialPort&&) = delete;

    SerialPort& operator=(const SerialPort&) = delete;
    SerialPort& operator=(SerialPort&&) = delete;

    size_t ReadBytes(uint8_t* data, const size_t bytesToRead);
    size_t SendBytes(const uint8_t* data, const size_t bytesToSend);

    // thinking of adding a container template version of read and send

    void SetRxTimeouts(const std::chrono::milliseconds& timeOut,
    const std::chrono::milliseconds& readIntervalTimeout);

    void FlushRx();
    void FlushTx();

    size_t CurrentRxQueueSize();
    size_t CurrentTxQueueSize();

    size_t CurrentRxInQueue();
    size_t CurrentTxInQueue();

    private:
    struct Impl;
    std::unique_ptr<Impl> m_impl;
    };

    class SerialPortRuntimeException
    : public std::runtime_error
    {
    public:
    SerialPortRuntimeException(const char *message)
    : std::runtime_error(message) {}
    };

    class SerialPortRangeError
    : public std::range_error
    {
    public:
    SerialPortRangeError(const char *message)
    : std::range_error(message) {}
    };
    }


    SerialPort.cpp



    #if defined(_WIN32)

    #include <Windows.h>
    #include <CommCtrl.h>
    #include <sstream>
    #include <string>
    #include <stdexcept>
    #include "SerialPort.h"

    struct Lite::SerialPort::Impl
    {
    BYTE ParityValue(const SerialPort::PARITY parity);
    BYTE StopBitsValue(const SerialPort::STOPBITS stopBits);
    DCB& FlowControlValue(const SerialPort::FLOWCONTROL, DCB& dcb);

    std::string GetCommError();
    HANDLE m_hCommPort;
    };

    std::string Lite::SerialPort::Impl::GetCommError()
    {
    LPSTR lpMsgBuf;
    DWORD errCode = GetLastError();
    FormatMessage(
    FORMAT_MESSAGE_ALLOCATE_BUFFER |
    FORMAT_MESSAGE_FROM_SYSTEM |
    FORMAT_MESSAGE_IGNORE_INSERTS,
    NULL,
    errCode,
    0, // Default language
    (LPTSTR)&lpMsgBuf,
    0,
    NULL
    );

    std::stringstream temp;
    temp << lpMsgBuf;
    return temp.str();
    }

    Lite::SerialPort::SerialPort(const std::string name, const uint32_t baudRate,
    const PARITY parity, const STOPBITS stopBit, const FLOWCONTROL flowControl,
    const uint8_t databits)
    : m_impl(std::make_unique <Impl>())
    {
    m_impl->m_hCommPort = CreateFile(name.c_str(),
    GENERIC_READ | GENERIC_WRITE,
    0,
    0,
    OPEN_EXISTING,
    0,
    0
    );
    if(m_impl->m_hCommPort == INVALID_HANDLE_VALUE) {
    throw SerialPortRuntimeException(m_impl->GetCommError().c_str());
    }

    DCB dcb;
    dcb.DCBlength = sizeof(DCB);

    if(!GetCommState(m_impl->m_hCommPort, &dcb))
    throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

    dcb.BaudRate = baudRate;
    dcb.ByteSize = databits;
    dcb.Parity = m_impl->ParityValue(parity);
    dcb.StopBits = m_impl->StopBitsValue(stopBit);
    m_impl->FlowControlValue(flowControl, dcb);

    if (!SetCommState(m_impl->m_hCommPort, &dcb))
    throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

    return;
    }

    Lite::SerialPort::~SerialPort()
    {
    CloseHandle(m_impl->m_hCommPort);

    return;
    }

    size_t Lite::SerialPort::ReadBytes(uint8_t* data, const size_t bytesToRead)
    {
    auto tempBytesToRead = bytesToRead;

    if (tempBytesToRead > MAXDWORD)
    tempBytesToRead = MAXDWORD;

    DWORD bytesRead;

    if (!ReadFile(m_impl->m_hCommPort, &data,
    tempBytesToRead, &bytesRead, NULL))
    throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

    return bytesRead;
    }

    size_t Lite::SerialPort::SendBytes(const uint8_t* data, const size_t bytesToSend)
    {
    auto tempBytesToSend = bytesToSend;

    if (tempBytesToSend > MAXDWORD)
    tempBytesToSend = MAXDWORD;

    DWORD sentSize;
    if(!WriteFile(m_impl->m_hCommPort, &data,
    tempBytesToSend, &sentSize, NULL))
    throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

    return sentSize;
    }

    void Lite::SerialPort::SetRxTimeouts(const std::chrono::milliseconds& timeOut,
    const std::chrono::milliseconds& readIntervalTimeout)
    {
    COMMTIMEOUTS to;

    if (!GetCommTimeouts(m_impl->m_hCommPort, &to))
    throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

    to.ReadIntervalTimeout = readIntervalTimeout.count();
    to.ReadTotalTimeoutMultiplier = 0;
    to.ReadTotalTimeoutConstant = timeOut.count();

    if(!SetCommTimeouts(m_impl->m_hCommPort, &to))
    throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

    return;
    }

    void Lite::SerialPort::FlushRx()
    {
    if (!PurgeComm(m_impl->m_hCommPort, PURGE_RXCLEAR))
    throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

    return;
    }

    void Lite::SerialPort::FlushTx()
    {
    if (!PurgeComm(m_impl->m_hCommPort, PURGE_TXCLEAR))
    throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

    return;
    }

    size_t Lite::SerialPort::CurrentRxQueueSize()
    {
    COMMPROP cp;

    if (!GetCommProperties(m_impl->m_hCommPort, &cp))
    throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

    return cp.dwCurrentRxQueue;
    }

    size_t Lite::SerialPort::CurrentTxQueueSize()
    {
    COMMPROP cp;

    if (!GetCommProperties(m_impl->m_hCommPort, &cp))
    throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

    return cp.dwCurrentTxQueue;
    }

    size_t Lite::SerialPort::CurrentRxInQueue()
    {
    COMSTAT cs;
    DWORD error;

    if (!ClearCommError(m_impl->m_hCommPort, &error, &cs))
    throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

    return cs.cbInQue;
    }

    size_t Lite::SerialPort::CurrentTxInQueue()
    {
    COMSTAT cs;
    DWORD error;

    if (!ClearCommError(m_impl->m_hCommPort, &error, &cs))
    throw std::runtime_error(m_impl->GetCommError().c_str());

    return cs.cbOutQue;
    }

    BYTE Lite::SerialPort::Impl::ParityValue(const SerialPort::PARITY parity)
    {
    BYTE temp;

    switch (parity)
    {
    case SerialPort::PARITY::NONE:
    temp = NOPARITY;
    break;
    case SerialPort::PARITY::ODD:
    temp = ODDPARITY;
    break;
    case SerialPort::PARITY::EVEN:
    temp = EVENPARITY;
    break;
    case SerialPort::PARITY::MARK:
    temp = MARKPARITY;
    break;
    case SerialPort::PARITY::SPACE:
    temp = SPACEPARITY;
    break;
    default:
    throw SerialPortRangeError("Parity Enum out of Range");
    break;
    }

    return temp;
    }

    BYTE Lite::SerialPort::Impl::StopBitsValue(const SerialPort::STOPBITS stopBits)
    {
    BYTE temp;
    switch (stopBits)
    {
    case SerialPort::STOPBITS::ONE:
    temp = ONESTOPBIT;
    break;
    case SerialPort::STOPBITS::ONEPT5:
    temp = ONE5STOPBITS;
    break;
    case SerialPort::STOPBITS::TWO:
    temp = TWOSTOPBITS;
    break;
    default:
    throw SerialPortRangeError("Stop Bits Enum out of range");
    break;
    }

    return temp;
    }

    DCB& Lite::SerialPort::Impl::FlowControlValue(const SerialPort::FLOWCONTROL flowcontrol,
    DCB& dcb)
    {
    switch (flowcontrol)
    {
    case Lite::SerialPort::FLOWCONTROL::SW:
    dcb.fOutxCtsFlow = false;
    dcb.fRtsControl = RTS_CONTROL_DISABLE;
    dcb.fOutX = true;
    dcb.fInX = true;
    break;
    case Lite::SerialPort::FLOWCONTROL::HW:
    dcb.fOutxCtsFlow = true;
    dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
    dcb.fOutX = false;
    dcb.fInX = false;
    break;
    case Lite::SerialPort::FLOWCONTROL::NONE:
    dcb.fOutxCtsFlow = false;
    dcb.fRtsControl = RTS_CONTROL_DISABLE;
    dcb.fOutX = false;
    dcb.fInX = false;
    break;
    default:
    throw SerialPortRangeError("Flow Control Enum out of range");
    break;
    }

    return dcb;
    }

    #endif


    main.cpp



    #include <iostream>
    #include "SerialPort.h"

    void HandleException()
    {
    try { throw; }
    catch (const Lite::SerialPortRuntimeException &e)
    {
    std::cerr << e.what() << 'n'; // catches standard error message
    }
    catch (const Lite::SerialPortRangeError &e)
    {
    std::cerr << e.what() << 'n';; // catches standard error message
    }
    return;
    }

    int main()
    {
    try
    {
    Lite::SerialPort sp{ "COM5", 4800 };

    sp.FlushRx();
    sp.FlushTx();

    std::cout << "rx queue size: " << sp.CurrentRxQueueSize() << 'n';
    std::cout << "rx in queue size: " << sp.CurrentRxInQueue() << 'n';

    sp.SetRxTimeouts(std::chrono::milliseconds(1000),
    std::chrono::milliseconds(1000));

    std::vector<uint8_t> writeBuf(100);
    sp.SendBytes(writeBuf.data(), writeBuf.size());

    std::vector<uint8_t> test(sp.CurrentRxQueueSize());
    test.resize(sp.ReadBytes(test.data(), 100));

    sp.FlushRx();
    sp.FlushTx();
    }
    catch (...)
    {
    HandleException();
    }
    }









    share|improve this question











    $endgroup$















      0












      0








      0





      $begingroup$


      I implemented a very simple blocking uart interface. Most of the serial instruments I interface with are master/slave and I have to wait on data to proceed forward. I am contemplating add some template code to accept any container iterator, but for now I will probably use array or vector. Also, I open the serial port immediately in the constructor and only close it in the destructor. If I don't keep it open some of the other calls will not perform their intended function.



      Things I implemented that I'm working on understanding better




      1. Pimpl Idiom

      2. Exception Handling

      3. RAII

      4. Constructor defaults


      SerialPort.h



      #pragma once
      #include <memory>
      #include <vector>
      #include <string>
      #include <chrono>

      namespace Lite
      {
      class SerialPort
      {
      public:
      enum class PARITY { NONE, ODD, EVEN, MARK, SPACE };
      enum class STOPBITS { ONE, ONEPT5, TWO };
      enum class FLOWCONTROL { SW, HW, NONE };

      SerialPort(const std::string port = "COM1",
      const uint32_t baudRate = 9600,
      const PARITY parity = PARITY::NONE,
      const STOPBITS stopBit = STOPBITS::ONE,
      const FLOWCONTROL flowControl = FLOWCONTROL::NONE,
      const uint8_t databits = 8);
      ~SerialPort();

      SerialPort(const SerialPort&) = delete;
      SerialPort(SerialPort&&) = delete;

      SerialPort& operator=(const SerialPort&) = delete;
      SerialPort& operator=(SerialPort&&) = delete;

      size_t ReadBytes(uint8_t* data, const size_t bytesToRead);
      size_t SendBytes(const uint8_t* data, const size_t bytesToSend);

      // thinking of adding a container template version of read and send

      void SetRxTimeouts(const std::chrono::milliseconds& timeOut,
      const std::chrono::milliseconds& readIntervalTimeout);

      void FlushRx();
      void FlushTx();

      size_t CurrentRxQueueSize();
      size_t CurrentTxQueueSize();

      size_t CurrentRxInQueue();
      size_t CurrentTxInQueue();

      private:
      struct Impl;
      std::unique_ptr<Impl> m_impl;
      };

      class SerialPortRuntimeException
      : public std::runtime_error
      {
      public:
      SerialPortRuntimeException(const char *message)
      : std::runtime_error(message) {}
      };

      class SerialPortRangeError
      : public std::range_error
      {
      public:
      SerialPortRangeError(const char *message)
      : std::range_error(message) {}
      };
      }


      SerialPort.cpp



      #if defined(_WIN32)

      #include <Windows.h>
      #include <CommCtrl.h>
      #include <sstream>
      #include <string>
      #include <stdexcept>
      #include "SerialPort.h"

      struct Lite::SerialPort::Impl
      {
      BYTE ParityValue(const SerialPort::PARITY parity);
      BYTE StopBitsValue(const SerialPort::STOPBITS stopBits);
      DCB& FlowControlValue(const SerialPort::FLOWCONTROL, DCB& dcb);

      std::string GetCommError();
      HANDLE m_hCommPort;
      };

      std::string Lite::SerialPort::Impl::GetCommError()
      {
      LPSTR lpMsgBuf;
      DWORD errCode = GetLastError();
      FormatMessage(
      FORMAT_MESSAGE_ALLOCATE_BUFFER |
      FORMAT_MESSAGE_FROM_SYSTEM |
      FORMAT_MESSAGE_IGNORE_INSERTS,
      NULL,
      errCode,
      0, // Default language
      (LPTSTR)&lpMsgBuf,
      0,
      NULL
      );

      std::stringstream temp;
      temp << lpMsgBuf;
      return temp.str();
      }

      Lite::SerialPort::SerialPort(const std::string name, const uint32_t baudRate,
      const PARITY parity, const STOPBITS stopBit, const FLOWCONTROL flowControl,
      const uint8_t databits)
      : m_impl(std::make_unique <Impl>())
      {
      m_impl->m_hCommPort = CreateFile(name.c_str(),
      GENERIC_READ | GENERIC_WRITE,
      0,
      0,
      OPEN_EXISTING,
      0,
      0
      );
      if(m_impl->m_hCommPort == INVALID_HANDLE_VALUE) {
      throw SerialPortRuntimeException(m_impl->GetCommError().c_str());
      }

      DCB dcb;
      dcb.DCBlength = sizeof(DCB);

      if(!GetCommState(m_impl->m_hCommPort, &dcb))
      throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

      dcb.BaudRate = baudRate;
      dcb.ByteSize = databits;
      dcb.Parity = m_impl->ParityValue(parity);
      dcb.StopBits = m_impl->StopBitsValue(stopBit);
      m_impl->FlowControlValue(flowControl, dcb);

      if (!SetCommState(m_impl->m_hCommPort, &dcb))
      throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

      return;
      }

      Lite::SerialPort::~SerialPort()
      {
      CloseHandle(m_impl->m_hCommPort);

      return;
      }

      size_t Lite::SerialPort::ReadBytes(uint8_t* data, const size_t bytesToRead)
      {
      auto tempBytesToRead = bytesToRead;

      if (tempBytesToRead > MAXDWORD)
      tempBytesToRead = MAXDWORD;

      DWORD bytesRead;

      if (!ReadFile(m_impl->m_hCommPort, &data,
      tempBytesToRead, &bytesRead, NULL))
      throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

      return bytesRead;
      }

      size_t Lite::SerialPort::SendBytes(const uint8_t* data, const size_t bytesToSend)
      {
      auto tempBytesToSend = bytesToSend;

      if (tempBytesToSend > MAXDWORD)
      tempBytesToSend = MAXDWORD;

      DWORD sentSize;
      if(!WriteFile(m_impl->m_hCommPort, &data,
      tempBytesToSend, &sentSize, NULL))
      throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

      return sentSize;
      }

      void Lite::SerialPort::SetRxTimeouts(const std::chrono::milliseconds& timeOut,
      const std::chrono::milliseconds& readIntervalTimeout)
      {
      COMMTIMEOUTS to;

      if (!GetCommTimeouts(m_impl->m_hCommPort, &to))
      throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

      to.ReadIntervalTimeout = readIntervalTimeout.count();
      to.ReadTotalTimeoutMultiplier = 0;
      to.ReadTotalTimeoutConstant = timeOut.count();

      if(!SetCommTimeouts(m_impl->m_hCommPort, &to))
      throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

      return;
      }

      void Lite::SerialPort::FlushRx()
      {
      if (!PurgeComm(m_impl->m_hCommPort, PURGE_RXCLEAR))
      throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

      return;
      }

      void Lite::SerialPort::FlushTx()
      {
      if (!PurgeComm(m_impl->m_hCommPort, PURGE_TXCLEAR))
      throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

      return;
      }

      size_t Lite::SerialPort::CurrentRxQueueSize()
      {
      COMMPROP cp;

      if (!GetCommProperties(m_impl->m_hCommPort, &cp))
      throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

      return cp.dwCurrentRxQueue;
      }

      size_t Lite::SerialPort::CurrentTxQueueSize()
      {
      COMMPROP cp;

      if (!GetCommProperties(m_impl->m_hCommPort, &cp))
      throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

      return cp.dwCurrentTxQueue;
      }

      size_t Lite::SerialPort::CurrentRxInQueue()
      {
      COMSTAT cs;
      DWORD error;

      if (!ClearCommError(m_impl->m_hCommPort, &error, &cs))
      throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

      return cs.cbInQue;
      }

      size_t Lite::SerialPort::CurrentTxInQueue()
      {
      COMSTAT cs;
      DWORD error;

      if (!ClearCommError(m_impl->m_hCommPort, &error, &cs))
      throw std::runtime_error(m_impl->GetCommError().c_str());

      return cs.cbOutQue;
      }

      BYTE Lite::SerialPort::Impl::ParityValue(const SerialPort::PARITY parity)
      {
      BYTE temp;

      switch (parity)
      {
      case SerialPort::PARITY::NONE:
      temp = NOPARITY;
      break;
      case SerialPort::PARITY::ODD:
      temp = ODDPARITY;
      break;
      case SerialPort::PARITY::EVEN:
      temp = EVENPARITY;
      break;
      case SerialPort::PARITY::MARK:
      temp = MARKPARITY;
      break;
      case SerialPort::PARITY::SPACE:
      temp = SPACEPARITY;
      break;
      default:
      throw SerialPortRangeError("Parity Enum out of Range");
      break;
      }

      return temp;
      }

      BYTE Lite::SerialPort::Impl::StopBitsValue(const SerialPort::STOPBITS stopBits)
      {
      BYTE temp;
      switch (stopBits)
      {
      case SerialPort::STOPBITS::ONE:
      temp = ONESTOPBIT;
      break;
      case SerialPort::STOPBITS::ONEPT5:
      temp = ONE5STOPBITS;
      break;
      case SerialPort::STOPBITS::TWO:
      temp = TWOSTOPBITS;
      break;
      default:
      throw SerialPortRangeError("Stop Bits Enum out of range");
      break;
      }

      return temp;
      }

      DCB& Lite::SerialPort::Impl::FlowControlValue(const SerialPort::FLOWCONTROL flowcontrol,
      DCB& dcb)
      {
      switch (flowcontrol)
      {
      case Lite::SerialPort::FLOWCONTROL::SW:
      dcb.fOutxCtsFlow = false;
      dcb.fRtsControl = RTS_CONTROL_DISABLE;
      dcb.fOutX = true;
      dcb.fInX = true;
      break;
      case Lite::SerialPort::FLOWCONTROL::HW:
      dcb.fOutxCtsFlow = true;
      dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
      dcb.fOutX = false;
      dcb.fInX = false;
      break;
      case Lite::SerialPort::FLOWCONTROL::NONE:
      dcb.fOutxCtsFlow = false;
      dcb.fRtsControl = RTS_CONTROL_DISABLE;
      dcb.fOutX = false;
      dcb.fInX = false;
      break;
      default:
      throw SerialPortRangeError("Flow Control Enum out of range");
      break;
      }

      return dcb;
      }

      #endif


      main.cpp



      #include <iostream>
      #include "SerialPort.h"

      void HandleException()
      {
      try { throw; }
      catch (const Lite::SerialPortRuntimeException &e)
      {
      std::cerr << e.what() << 'n'; // catches standard error message
      }
      catch (const Lite::SerialPortRangeError &e)
      {
      std::cerr << e.what() << 'n';; // catches standard error message
      }
      return;
      }

      int main()
      {
      try
      {
      Lite::SerialPort sp{ "COM5", 4800 };

      sp.FlushRx();
      sp.FlushTx();

      std::cout << "rx queue size: " << sp.CurrentRxQueueSize() << 'n';
      std::cout << "rx in queue size: " << sp.CurrentRxInQueue() << 'n';

      sp.SetRxTimeouts(std::chrono::milliseconds(1000),
      std::chrono::milliseconds(1000));

      std::vector<uint8_t> writeBuf(100);
      sp.SendBytes(writeBuf.data(), writeBuf.size());

      std::vector<uint8_t> test(sp.CurrentRxQueueSize());
      test.resize(sp.ReadBytes(test.data(), 100));

      sp.FlushRx();
      sp.FlushTx();
      }
      catch (...)
      {
      HandleException();
      }
      }









      share|improve this question











      $endgroup$




      I implemented a very simple blocking uart interface. Most of the serial instruments I interface with are master/slave and I have to wait on data to proceed forward. I am contemplating add some template code to accept any container iterator, but for now I will probably use array or vector. Also, I open the serial port immediately in the constructor and only close it in the destructor. If I don't keep it open some of the other calls will not perform their intended function.



      Things I implemented that I'm working on understanding better




      1. Pimpl Idiom

      2. Exception Handling

      3. RAII

      4. Constructor defaults


      SerialPort.h



      #pragma once
      #include <memory>
      #include <vector>
      #include <string>
      #include <chrono>

      namespace Lite
      {
      class SerialPort
      {
      public:
      enum class PARITY { NONE, ODD, EVEN, MARK, SPACE };
      enum class STOPBITS { ONE, ONEPT5, TWO };
      enum class FLOWCONTROL { SW, HW, NONE };

      SerialPort(const std::string port = "COM1",
      const uint32_t baudRate = 9600,
      const PARITY parity = PARITY::NONE,
      const STOPBITS stopBit = STOPBITS::ONE,
      const FLOWCONTROL flowControl = FLOWCONTROL::NONE,
      const uint8_t databits = 8);
      ~SerialPort();

      SerialPort(const SerialPort&) = delete;
      SerialPort(SerialPort&&) = delete;

      SerialPort& operator=(const SerialPort&) = delete;
      SerialPort& operator=(SerialPort&&) = delete;

      size_t ReadBytes(uint8_t* data, const size_t bytesToRead);
      size_t SendBytes(const uint8_t* data, const size_t bytesToSend);

      // thinking of adding a container template version of read and send

      void SetRxTimeouts(const std::chrono::milliseconds& timeOut,
      const std::chrono::milliseconds& readIntervalTimeout);

      void FlushRx();
      void FlushTx();

      size_t CurrentRxQueueSize();
      size_t CurrentTxQueueSize();

      size_t CurrentRxInQueue();
      size_t CurrentTxInQueue();

      private:
      struct Impl;
      std::unique_ptr<Impl> m_impl;
      };

      class SerialPortRuntimeException
      : public std::runtime_error
      {
      public:
      SerialPortRuntimeException(const char *message)
      : std::runtime_error(message) {}
      };

      class SerialPortRangeError
      : public std::range_error
      {
      public:
      SerialPortRangeError(const char *message)
      : std::range_error(message) {}
      };
      }


      SerialPort.cpp



      #if defined(_WIN32)

      #include <Windows.h>
      #include <CommCtrl.h>
      #include <sstream>
      #include <string>
      #include <stdexcept>
      #include "SerialPort.h"

      struct Lite::SerialPort::Impl
      {
      BYTE ParityValue(const SerialPort::PARITY parity);
      BYTE StopBitsValue(const SerialPort::STOPBITS stopBits);
      DCB& FlowControlValue(const SerialPort::FLOWCONTROL, DCB& dcb);

      std::string GetCommError();
      HANDLE m_hCommPort;
      };

      std::string Lite::SerialPort::Impl::GetCommError()
      {
      LPSTR lpMsgBuf;
      DWORD errCode = GetLastError();
      FormatMessage(
      FORMAT_MESSAGE_ALLOCATE_BUFFER |
      FORMAT_MESSAGE_FROM_SYSTEM |
      FORMAT_MESSAGE_IGNORE_INSERTS,
      NULL,
      errCode,
      0, // Default language
      (LPTSTR)&lpMsgBuf,
      0,
      NULL
      );

      std::stringstream temp;
      temp << lpMsgBuf;
      return temp.str();
      }

      Lite::SerialPort::SerialPort(const std::string name, const uint32_t baudRate,
      const PARITY parity, const STOPBITS stopBit, const FLOWCONTROL flowControl,
      const uint8_t databits)
      : m_impl(std::make_unique <Impl>())
      {
      m_impl->m_hCommPort = CreateFile(name.c_str(),
      GENERIC_READ | GENERIC_WRITE,
      0,
      0,
      OPEN_EXISTING,
      0,
      0
      );
      if(m_impl->m_hCommPort == INVALID_HANDLE_VALUE) {
      throw SerialPortRuntimeException(m_impl->GetCommError().c_str());
      }

      DCB dcb;
      dcb.DCBlength = sizeof(DCB);

      if(!GetCommState(m_impl->m_hCommPort, &dcb))
      throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

      dcb.BaudRate = baudRate;
      dcb.ByteSize = databits;
      dcb.Parity = m_impl->ParityValue(parity);
      dcb.StopBits = m_impl->StopBitsValue(stopBit);
      m_impl->FlowControlValue(flowControl, dcb);

      if (!SetCommState(m_impl->m_hCommPort, &dcb))
      throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

      return;
      }

      Lite::SerialPort::~SerialPort()
      {
      CloseHandle(m_impl->m_hCommPort);

      return;
      }

      size_t Lite::SerialPort::ReadBytes(uint8_t* data, const size_t bytesToRead)
      {
      auto tempBytesToRead = bytesToRead;

      if (tempBytesToRead > MAXDWORD)
      tempBytesToRead = MAXDWORD;

      DWORD bytesRead;

      if (!ReadFile(m_impl->m_hCommPort, &data,
      tempBytesToRead, &bytesRead, NULL))
      throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

      return bytesRead;
      }

      size_t Lite::SerialPort::SendBytes(const uint8_t* data, const size_t bytesToSend)
      {
      auto tempBytesToSend = bytesToSend;

      if (tempBytesToSend > MAXDWORD)
      tempBytesToSend = MAXDWORD;

      DWORD sentSize;
      if(!WriteFile(m_impl->m_hCommPort, &data,
      tempBytesToSend, &sentSize, NULL))
      throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

      return sentSize;
      }

      void Lite::SerialPort::SetRxTimeouts(const std::chrono::milliseconds& timeOut,
      const std::chrono::milliseconds& readIntervalTimeout)
      {
      COMMTIMEOUTS to;

      if (!GetCommTimeouts(m_impl->m_hCommPort, &to))
      throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

      to.ReadIntervalTimeout = readIntervalTimeout.count();
      to.ReadTotalTimeoutMultiplier = 0;
      to.ReadTotalTimeoutConstant = timeOut.count();

      if(!SetCommTimeouts(m_impl->m_hCommPort, &to))
      throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

      return;
      }

      void Lite::SerialPort::FlushRx()
      {
      if (!PurgeComm(m_impl->m_hCommPort, PURGE_RXCLEAR))
      throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

      return;
      }

      void Lite::SerialPort::FlushTx()
      {
      if (!PurgeComm(m_impl->m_hCommPort, PURGE_TXCLEAR))
      throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

      return;
      }

      size_t Lite::SerialPort::CurrentRxQueueSize()
      {
      COMMPROP cp;

      if (!GetCommProperties(m_impl->m_hCommPort, &cp))
      throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

      return cp.dwCurrentRxQueue;
      }

      size_t Lite::SerialPort::CurrentTxQueueSize()
      {
      COMMPROP cp;

      if (!GetCommProperties(m_impl->m_hCommPort, &cp))
      throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

      return cp.dwCurrentTxQueue;
      }

      size_t Lite::SerialPort::CurrentRxInQueue()
      {
      COMSTAT cs;
      DWORD error;

      if (!ClearCommError(m_impl->m_hCommPort, &error, &cs))
      throw SerialPortRuntimeException(m_impl->GetCommError().c_str());

      return cs.cbInQue;
      }

      size_t Lite::SerialPort::CurrentTxInQueue()
      {
      COMSTAT cs;
      DWORD error;

      if (!ClearCommError(m_impl->m_hCommPort, &error, &cs))
      throw std::runtime_error(m_impl->GetCommError().c_str());

      return cs.cbOutQue;
      }

      BYTE Lite::SerialPort::Impl::ParityValue(const SerialPort::PARITY parity)
      {
      BYTE temp;

      switch (parity)
      {
      case SerialPort::PARITY::NONE:
      temp = NOPARITY;
      break;
      case SerialPort::PARITY::ODD:
      temp = ODDPARITY;
      break;
      case SerialPort::PARITY::EVEN:
      temp = EVENPARITY;
      break;
      case SerialPort::PARITY::MARK:
      temp = MARKPARITY;
      break;
      case SerialPort::PARITY::SPACE:
      temp = SPACEPARITY;
      break;
      default:
      throw SerialPortRangeError("Parity Enum out of Range");
      break;
      }

      return temp;
      }

      BYTE Lite::SerialPort::Impl::StopBitsValue(const SerialPort::STOPBITS stopBits)
      {
      BYTE temp;
      switch (stopBits)
      {
      case SerialPort::STOPBITS::ONE:
      temp = ONESTOPBIT;
      break;
      case SerialPort::STOPBITS::ONEPT5:
      temp = ONE5STOPBITS;
      break;
      case SerialPort::STOPBITS::TWO:
      temp = TWOSTOPBITS;
      break;
      default:
      throw SerialPortRangeError("Stop Bits Enum out of range");
      break;
      }

      return temp;
      }

      DCB& Lite::SerialPort::Impl::FlowControlValue(const SerialPort::FLOWCONTROL flowcontrol,
      DCB& dcb)
      {
      switch (flowcontrol)
      {
      case Lite::SerialPort::FLOWCONTROL::SW:
      dcb.fOutxCtsFlow = false;
      dcb.fRtsControl = RTS_CONTROL_DISABLE;
      dcb.fOutX = true;
      dcb.fInX = true;
      break;
      case Lite::SerialPort::FLOWCONTROL::HW:
      dcb.fOutxCtsFlow = true;
      dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
      dcb.fOutX = false;
      dcb.fInX = false;
      break;
      case Lite::SerialPort::FLOWCONTROL::NONE:
      dcb.fOutxCtsFlow = false;
      dcb.fRtsControl = RTS_CONTROL_DISABLE;
      dcb.fOutX = false;
      dcb.fInX = false;
      break;
      default:
      throw SerialPortRangeError("Flow Control Enum out of range");
      break;
      }

      return dcb;
      }

      #endif


      main.cpp



      #include <iostream>
      #include "SerialPort.h"

      void HandleException()
      {
      try { throw; }
      catch (const Lite::SerialPortRuntimeException &e)
      {
      std::cerr << e.what() << 'n'; // catches standard error message
      }
      catch (const Lite::SerialPortRangeError &e)
      {
      std::cerr << e.what() << 'n';; // catches standard error message
      }
      return;
      }

      int main()
      {
      try
      {
      Lite::SerialPort sp{ "COM5", 4800 };

      sp.FlushRx();
      sp.FlushTx();

      std::cout << "rx queue size: " << sp.CurrentRxQueueSize() << 'n';
      std::cout << "rx in queue size: " << sp.CurrentRxInQueue() << 'n';

      sp.SetRxTimeouts(std::chrono::milliseconds(1000),
      std::chrono::milliseconds(1000));

      std::vector<uint8_t> writeBuf(100);
      sp.SendBytes(writeBuf.data(), writeBuf.size());

      std::vector<uint8_t> test(sp.CurrentRxQueueSize());
      test.resize(sp.ReadBytes(test.data(), 100));

      sp.FlushRx();
      sp.FlushTx();
      }
      catch (...)
      {
      HandleException();
      }
      }






      c++ object-oriented windows interface serial-port






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited 1 hour ago









      Jamal

      30.3k11116226




      30.3k11116226










      asked 1 hour ago









      Eddie C.Eddie C.

      124




      124






















          0






          active

          oldest

          votes











          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f211971%2fsimple-serial-port-windows-interface%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          0






          active

          oldest

          votes








          0






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes
















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Code Review Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          Use MathJax to format equations. MathJax reference.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f211971%2fsimple-serial-port-windows-interface%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Сан-Квентин

          8-я гвардейская общевойсковая армия

          Алькесар