Search code examples
cfile-ioserial-port

Interacting with a serial port using the C functions fopen, fread, and fwrite?


I have been attempting to communicate with a device of mine via an RS232 serial port(in my case it is COM6). My code is supposed to write a string of ascii values to the device and then read the response, however I cannot seem to get any response. The program seems to work relatively fine when I have it write and read to a file in my computer, but not for when I designate COM6. Here is the latest edition of my code:

using namespace std;

const char ASCII[ ]= "0123456789ABCDEF";
char *Checksum (char *buffer)
{
static char Hex[10];
static int a1, a2;
register unsigned int i;
int sum;
printf("%s \n", buffer);
sum = 256;
for ( i=0 ; i<strlen(buffer) ; i++ )
{
sum-=buffer[i];
if ( sum<0 )
sum+= 256;
}
a1 = (sum & 0xF0) >> 4;
a2 = sum & 0x0F;
Hex[0] = ASCII[a1];
Hex[1] = ASCII[a2];
Hex[2] = 0;
printf("the checksum is %s \n",Hex);
return(Hex);
}


int main()
{
char data[80], input[80], *data2;
char *response;
DCB dcb;
bool retVal;
DWORD dwBytesTransferred;
DWORD byteswritten;
printf("the variable response is initially: %d\n", response);

dcb.BaudRate = CBR_19200; //19200 Baud
dcb.ByteSize = 8; //8 data bits
dcb.Parity = NOPARITY; //no parity
dcb.StopBits = ONESTOPBIT; //1 stop

//New open port area
HANDLE        hPort;
  if ((hPort = CreateFile ( "\\\\.\\COM6",
      GENERIC_READ | GENERIC_WRITE,
      0,              // exclusive access
      NULL,           // no security attrs
      OPEN_EXISTING,
      FILE_ATTRIBUTE_NORMAL,
      NULL)) != INVALID_HANDLE_VALUE)
  {
      printf("SUCCESS opening the port\n");// success
  } 
//GetCommState
DCB Dcb;
  GetCommState (hPort, &Dcb);
  Dcb.BaudRate        = CBR_19200;
  Dcb.StopBits        = ONESTOPBIT;
  Dcb.ByteSize        = 8;
  Dcb.Parity          = NOPARITY;
  Dcb.fParity         = 0;
  Dcb.fOutxCtsFlow    = 0;
  Dcb.fOutxDsrFlow    = 0;
  Dcb.fDsrSensitivity = 0;
  Dcb.fTXContinueOnXoff = TRUE;
  Dcb.fOutX           = 0;
  Dcb.fInX            = 0;
  Dcb.fNull           = 0;
  Dcb.fErrorChar      = 0;
  Dcb.fAbortOnError   = 0;
  Dcb.fRtsControl     = RTS_CONTROL_DISABLE;
  Dcb.fDtrControl     = DTR_CONTROL_DISABLE;

//Flushing

FlushFileBuffers( hPort );
  PurgeComm (hPort, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
  COMSTAT     comStat;
  DWORD       dwErrorFlags;
  ClearCommError ( hPort, &dwErrorFlags, &comStat );

//NEW commtimeouts area
COMMTIMEOUTS CommTimeOuts;
 DWORD dwTimeout = 3000;  // <- set timeout in milliseconds
     if(!dwTimeout)
     {   // Don't use timeout -> Read the bytes already in input buffer and return immediately
         CommTimeOuts.ReadIntervalTimeout      = MAXDWORD;
         CommTimeOuts.ReadTotalTimeoutConstant = 0;
     } else
     {   // Use given timeout, wait until the requested number of bytes are read - or timeout
         CommTimeOuts.ReadIntervalTimeout         = 0;
         CommTimeOuts.ReadTotalTimeoutConstant    = dwTimeout;
     }
     CommTimeOuts.ReadTotalTimeoutMultiplier  = 0;
     CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
     CommTimeOuts.WriteTotalTimeoutConstant   = 0;
     SetCommTimeouts (hPort, &CommTimeOuts);
printf("insert ASCII code string you wish to send:");
scanf("%s", input);
strcpy(data, "{0x02}");
strcat(data, input);
printf("%s \n", data);
data2=Checksum(data);
strcat(data, data2);
strcat(data, "{0x03}");
printf("the final sent message will be: %s \n",data);
retVal = WriteFile(hPort,data, strlen(data), &byteswritten, NULL);
printf("Number of bytes written: %d\n",  byteswritten);
printf("Write Success? %d\n", retVal);
retVal=ReadFile (hPort, &response, 20, &dwBytesTransferred, NULL);
printf("Read Success? %d\n", retVal);
printf("Port Response: %d\n", response);
free(response);
return 0;
}

Summary of latest discoveries: Using the Free Serial Port Monitor that Habi suggested I now know for sure that WriteFile is functioning correctly and COM6 is in receiving the message. I'm still looking for a crossover cable to double check that the message is being transferred across the line. I figure while I try to figure that out if someone could look at this new edition and tell me if there is anything wrong, particularly in relation to the ReadFile function, it would be much appreciated. It bothers me that the Free Serial Port software is only showing the data passed from my computer and not a response from the device at all. =\


Solution

  • Instead of

    "COM6"
    

    try

    "\\\\.\\COM6"
    

    And I would recommend to use CreateFile(), ReadFile(), WriteFile().

    To open the COM port try this:

    HANDLE        hComDev;
    
    if ((hComDev = CreateFile ( "\\\\.\\COM6",
        GENERIC_READ | GENERIC_WRITE,
        0,              // exclusive access
        NULL,           // no security attrs
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL)) != INVALID_HANDLE_VALUE)
    {
          // success
    }
    

    It seems that calling GetCommState() is missing in your code. Try this to configure the COM port:

    DCB Dcb;
    
    GetCommState (hComDev, &Dcb); 
    
    Dcb.BaudRate        = CBR_19200;
    Dcb.StopBits        = ONESTOPBIT;
    Dcb.ByteSize        = 8;
    Dcb.Parity          = NOPARITY;
    Dcb.fParity         = 0;
    Dcb.fOutxCtsFlow    = 0;
    Dcb.fOutxDsrFlow    = 0;
    Dcb.fDsrSensitivity = 0;
    Dcb.fTXContinueOnXoff = TRUE;
    Dcb.fOutX           = 0;
    Dcb.fInX            = 0;
    Dcb.fNull           = 0;
    Dcb.fErrorChar      = 0;
    Dcb.fAbortOnError   = 0;
    Dcb.fRtsControl     = RTS_CONTROL_DISABLE;
    Dcb.fDtrControl     = DTR_CONTROL_DISABLE;
    

    And to initially clear the COM port, I would do a reset like this before starting to send and receive bytes:

    FlushFileBuffers( hComDev );
    
    PurgeComm (hComDev, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
    
    COMSTAT     comStat;
    DWORD       dwErrorFlags;
    ClearCommError ( hComDev, &dwErrorFlags, &comStat );
    

    You ask for timeouts? To configure time outs try this:

    COMMTIMEOUTS CommTimeOuts;
    DWORD dwTimeout = ....  // <- set timeout in milliseconds
    
    if(!dwTimeout)
    {   // Don't use timeout -> Read the bytes already in input buffer and return immediately
        CommTimeOuts.ReadIntervalTimeout      = MAXDWORD;
        CommTimeOuts.ReadTotalTimeoutConstant = 0;
    }
    else
    {   // Use given timeout, wait until the requested number of bytes are read - or timeout
        CommTimeOuts.ReadIntervalTimeout         = 0;
        CommTimeOuts.ReadTotalTimeoutConstant    = dwTimeout;
    }
    CommTimeOuts.ReadTotalTimeoutMultiplier  = 0;
    CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
    CommTimeOuts.WriteTotalTimeoutConstant   = 0;
    
    SetCommTimeouts (hComDev, &CommTimeOuts);
    

    My code is supposed to write a string of ascii values to the device and then read the response, however I cannot seem to get any response.

    Are you sure that the bytes are really sent to the device? If you have the option, take a oscilloscope and monitor the PC's Tx line. Send some bytes and check baudrate and start/stop bits. If you have no hardware to monitor this signal, take a software based serial monitor, e.g. Free Serial Port Monitor. I have no experience with that kind of software tools but they should show you at least that the Windows drivers try to send something via the COM port of your choice.

    Greetings Habi