Auto detect what port the arduino is connected to

I made up a hacked up code that attempts to detect what port the board is connected to using VC++.

THe problem is that it only works sometimes, or not at all. I'm thinking it's a synch issue, or Windows doesn't buffer the serial port the way I'm using it.

This is the VC++ code I made: Error handling will be implemented later:

      bool board_detected = 0;

      char ports[] = {'1','2','3','4','5','6','7'};
      int currentport = 0;
      int maxports = 7;
      HANDLE hSerial;
      char portname[5];
      strcpy(portname, "COM");

      for(; currentport < maxports; currentport++)
      {
            portname[3] = ports[currentport];
            portname[4] = '\0';

            
            
            hSerial = CreateFile( portname, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
            printf("\n\nTrying PORT: %s\n\n", portname);
            printf("\n\n%s\n\n", (LPCWSTR)portname);
            if(hSerial == INVALID_HANDLE_VALUE)
                        continue;
            else
            {
                  //FlushFileBuffers(hSerial);

                  DCB dcbSerialParams = {0};

                  dcbSerialParams.DCBlength = sizeof(dcbSerialParams);

                  if(!GetCommState(hSerial, &dcbSerialParams)) {
                        printf("\nError Getting Com Port State!\n\n");
                        // Error handling
                  }

                  dcbSerialParams.BaudRate = CBR_9600;
                  dcbSerialParams.ByteSize = 8;
                  dcbSerialParams.StopBits = ONESTOPBIT;
                  dcbSerialParams.Parity = NOPARITY;

                  if(!SetCommState(hSerial, &dcbSerialParams)) {
                        printf("\n\nError Setting Serial Port STATE!\n\n");
                        //Error Handling
                  }

                  // To Prevent Timing out the serial port Tell Windows not to wait up for us

                  COMMTIMEOUTS timeouts = {0};

                  timeouts.ReadIntervalTimeout = 50;
                  timeouts.ReadTotalTimeoutConstant = 50;
                  timeouts.ReadTotalTimeoutMultiplier = 10;
                  timeouts.WriteTotalTimeoutConstant = 50;
                  timeouts.WriteTotalTimeoutMultiplier = 10;

                  if(!SetCommTimeouts(hSerial, &timeouts)){
                        printf("\n\n Serious Timeout error occured!!\n\n");
                  }

                  char readystatus[8] = {0};
            DWORD testWrote = 0;
            WriteFile(hSerial, "~READY~", 8, &testWrote, NULL);
            Sleep(150);
            ReadFile(hSerial, readystatus, 8, &testWrote, NULL);
            readystatus[7] = '\0';
            printf("\n\nREADY STATUS: %s\n\n", readystatus);
            if(strstr(readystatus, "~READ1~") != NULL){

                  board_detected = 1;
                  break;
            }
            else
                  continue;

}

Now the Ardunio parses for ~ and ~ characters and whatever is between them is a string. If it parses ~READY~ it responds with ~READ1~ using Serial.Print.

Can anyone see how I can do this better? As I said it works sometimes, but it seems like hit or miss.

Well, I don't know if this is the actual cause, but I hope this helps.

When you call WriteFile, the OS buffers the data - and you actually have a lot of buffering going on - you have that com port buffer, the FTDI driver buffer, a USB chip buffer, whatever the USB chips are doing on the motherboard hubs, etc, and then the arduino's serial buffer to contend with before the arduino is going to bark back its response. (more or less)

You will want to flush the ouput buffers to ensure that the layers know when to write the data out. Check out FlushFileBuffers (FlushFileBuffers function (fileapi.h) - Win32 apps | Microsoft Learn) to use after WriteFile. That will ensure the data is at least on its way to the arduino before continuing.

The second part is that ReadFile may return before your read has completed. Check your testWrote variable to see if you got anything back, and specifically to ensure that you have the number of characters that you need.

I worked in the past with some RS485 FTDI USB Com port dongles, and they seemed to be quick to identify a byte or two of data on the line so programs could react (giving ReadFile a byte or two only) and then they would buffer the incoming data in the background. The second ReadFile would get the rest or all of the data if it was a small packet. We used to joke around the office that it was to show they were faster then their competition, when really - 920kbps can only be so fast. Later we surmised that it was to be more efficient and so that software can queue up reads while data was coming in, rather than waiting for all the data to all be in.

I have no idea if this driver behaves the same way, but it appears to be the same driver I was using back then...

Thanks for your reply it seemed to make it work a lot better! However, now sometimes when the arduino sends back a ~READ1~ reply, my program is receiving it out of order. Is there any way to prevent this?

Sometimes it's received as ~~READ1 or ~DEAR~ or ~R~EAD1

Data coming in backwards is usually not possible with serial. I would check the buffers and ensure that you weren't clobbering data somehow in your code.

I have seen that when a buffer was set too small though.

Can you try this:

DWORD Result;

char readystatus[8];
Result = ReadFile(hSerial, readystatus, 8, &testWrote, NULL);
if(Result==ERROR_SUCCESS)
{
      readystatus[testWrote] = '\0';
      printf("\n\nREADY STATUS: %s\n\n", readystatus);
}
else 
{
      printf("\n\nReadFile Failed with Error:  %8.08X\n\n", Result);
}

and see what happens?

Seems to have done the trick, I set up a loop that waits for ReadFile to actually receive 8 bytes before moving on, and now it works perfectly.

Thanks a lot for your help

Here's the test code:

#include <windows.h>
#include <stdio.h>

#define PORT_SIZE 5
#define maxports 7
int serial_params(HANDLE hSerial);
HANDLE Init_Serial(char *portname);
char* Gen_Port_Name(void);
int Wait_Ready(HANDLE hSerial);


char* Gen_Port_Name(void)
{
      /* Return ERROR if Max Ports Reached */

      char ports[] = {'1','2','3','4','5','6','7'};
      static int currentport = 0;
      char portname[PORT_SIZE];
      strcpy(portname, "COM");
      portname[3] = ports[currentport];
      portname[4] = '\0';
      
      if(currentport > maxports)
            return ("ERROR"); // No more ports to try
      else
            currentport++; // Update to next port

      return( portname );
}

HANDLE Init_Serial(char *portname)
{
      static HANDLE temp;
      printf("\n\nPortName: %s\n\n", portname);
      temp = CreateFile( portname, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, 0);

      if(temp == INVALID_HANDLE_VALUE)
            {
                        FlushFileBuffers(temp); // flush buffers to tell Ardunio to bark it's data 
                        CloseHandle(temp); // Prevent Handle leaks
                        return INVALID_HANDLE_VALUE; // Move on to next port since this one doesn't exist
            }
      else
      {
            int attempt = serial_params(temp);

            if( attempt != (-1) )
                  return temp;
            else
                  return INVALID_HANDLE_VALUE;
      }
}


int serial_params(HANDLE hSerial)
{
      DWORD testWrote = 0;
      DWORD testRead = 0;
      DCB dcbSerialParams = {0};
      COMMTIMEOUTS timeouts = {0};

                  dcbSerialParams.DCBlength = sizeof(dcbSerialParams);

                  if(!GetCommState(hSerial, &dcbSerialParams)) {
                        printf("\nError Getting Com Port State!\n\n");
                        return (-1);
                  }

                  dcbSerialParams.BaudRate = CBR_9600;
                  dcbSerialParams.ByteSize = 8;
                  dcbSerialParams.StopBits = ONESTOPBIT;
                  dcbSerialParams.Parity = NOPARITY;

                  if(!SetCommState(hSerial, &dcbSerialParams)) {
                        printf("\n\nError Setting Serial Port STATE!\n\n");
                        return(-1);
                  }

                  // To Prevent Timing out the serial port Tell Windows not to wait up for us

                  

                  timeouts.ReadIntervalTimeout = 50;
                  timeouts.ReadTotalTimeoutConstant = 50;
                  timeouts.ReadTotalTimeoutMultiplier = 10;
                  timeouts.WriteTotalTimeoutConstant = 50;
                  timeouts.WriteTotalTimeoutMultiplier = 10;

                  if(!SetCommTimeouts(hSerial, &timeouts)){
                        printf("\n\n Serious Timeout error occured!!\n\n");
                        return(-1);
                  }

}

int Wait_Ready(HANDLE hSerial)
{
      /* This function waits for the ready Status of the Arduino Board */
      // TODO ERROR HANDLING FOR WRITE AND READFILE FUNCTIONS

            int Read_Timeout = 0;
            DWORD testRead = 0;
            DWORD testWrote = 0;
            char readystatus[8] = {0};
            
            WriteFile(hSerial, "~READY~", sizeof(readystatus), &testWrote, NULL);

            FlushFileBuffers(hSerial); // flush buffers to tell Ardunio to bark it's data 

            do{
            Sleep(1);
            printf("\nWrote %ld BYTES\n", testWrote);
            }while (testWrote < 8);

            do
            {      
                  ReadFile(hSerial, readystatus, 8, &testRead, NULL);
                  Sleep(1);
                  Read_Timeout++;
            }while((testRead < 8) && (Read_Timeout <= 100));

                  readystatus[7] = '\0';
                  printf("\n\nREADY STATUS: %s\n\n", readystatus);
                  if(strstr(readystatus, "READ1") != NULL)
                        return 1; // Board Detected
                  else
                        return 0; // Board not here
}

That's in it's own header file:

#include "serialfunc.h"
#include <windows.h>
#include <stdio.h>

int main(void)
{
      char use_port[PORT_SIZE];
      int i = 0;
      bool board_detected = 0;
      HANDLE hSerial = 0;
      for(; (i < maxports) && (board_detected != 1); i++)
      {
            strcpy(use_port, Gen_Port_Name());
            
            hSerial = Init_Serial(use_port);

            if (hSerial != INVALID_HANDLE_VALUE)
                  board_detected = Wait_Ready(hSerial);
            else
                  continue;
      }
      
      if(board_detected == 1)
            printf("\nBoard Found on Port: %s\n\n", use_port);
      else
            printf("\nBoard could not be found!!!\n\n");
      

      Sleep(10000); //debugging


      return 0;
}

Cool, glad to hear I could help;

One thing I can offer with this code:

do
            {
                  ReadFile(hSerial, readystatus, 8, &testRead, NULL);
                  Sleep(1);
                  Read_Timeout++;
            }while((testRead < 8) && (Read_Timeout <= 100));

...if the first read gets 1 byte, you are going to clobber that with another read of 7 to the same place, and never exit the loop until timeout.

For example if you get the tilde as the data buffer during the first read,
then READ1~ will turn into READ1~ because readystatus[0] was overwritten with the second call.

You may want to try something like:

char *pCurrentBuf = readystatus;
int totalIn =0;
int iWanted =8;
DWORD testRead;
DWORD result;
do{
     result = ReadFile(hSerial, pCurrentBuf, iWanted, &testRead, NULL);
     if(result!=ERROR_SUCCESS)
     {
          // ok, do some good ol' error checking here.
     }
     pCurrentBuf+=testRead;
     iWanted-=testRead;
     Sleep(1);
     Read_Timeout++;

} while((iWanted>0) &&  Read_Timeout <=100));
*pCurrentBuf=NULL;

This way, if you read 1 byte, then 2, then 4, then 1 - they all get assembled in your readyStatus buffer properly.

Not that 8 characters should need this, but if you expand your message size, you most certainly will need it.