CreateFile function being called twice to open the same serial COM port

78 Views Asked by At

For context I have a HC-05 module connected to my Arduino Mega. The Arduino sends some data to the HC-05 module which gets transferred via bluetooth to my computer (after it's paired).

When pairing HC-05 with laptop, I noticed that the computer has two COM ports. One is incoming and other is outgoing and the outgoing one seems to be the port that gets the data.

I needed to programmatically find which COM port is the one receiving the data and read from it.

My idea was to send some initial flags from the Arduino and then check which of the COM ports of my computer are getting those flags and use that to determine which COM port is the "outgoing" one that receives any data sent from Arduino via bluetooth. I have been able to get this to work using the following code.

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


BOOL BTPortFound;
char* BTPortName;

void portFinder(HKEY hKey, LONG result, char*** portArray, int* portCount) 
{
    // Open the registry key for COM ports
    result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_READ, &hKey);

    if (result != ERROR_SUCCESS) 
    {
        fprintf(stderr, "Failed to open registry key\n");
        return;
    }

    // Enumerate through the values in the registry key
    DWORD index = 0;
    char valueName[256], valueData[256];
    DWORD valueNameSize = sizeof(valueName) / sizeof(valueName[0]);
    DWORD valueDataSize = sizeof(valueData) / sizeof(valueData[0]);

    // Allocate initial memory for portArray
    *portCount = 0;
    *portArray = NULL;

    while (RegEnumValue(hKey, index++, valueName, &valueNameSize, NULL, NULL, (LPBYTE)valueData, &valueDataSize) == ERROR_SUCCESS) 
    {
        // Reallocate memory for portArray
        *portArray = (char**)realloc(*portArray, (*portCount + 1) * sizeof(char*));

        if (*portArray == NULL) 
        {
            fprintf(stderr, "Memory allocation failed\n");

            // Clean up
            for (int i = 0; i < *portCount; i++) 
            {
                free((*portArray)[i]);
            }
            free(*portArray);

            // Close the registry key
            RegCloseKey(hKey);

            return;
        }

        // Allocate memory for the current port string
        (*portArray)[*portCount] = _strdup(valueData);
        if ((*portArray)[*portCount] == NULL) 
        {
            fprintf(stderr, "Memory allocation failed\n");

            // Clean up
            for (int i = 0; i < *portCount; i++) 
            {
                free((*portArray)[i]);
            }
            free(*portArray);

            // Close the registry key
            RegCloseKey(hKey);

            return;
        }

        (*portCount)++;
        valueNameSize = sizeof(valueName) / sizeof(valueName[0]);
        valueDataSize = sizeof(valueData) / sizeof(valueData[0]);
    }

    // Close the registry key
    RegCloseKey(hKey);
}

void portInfoChecker(char* portName)
{
    HANDLE hSerial;
    DCB dcbSerialParams = {0};
    COMMTIMEOUTS timeouts = {0};
    DWORD bytesRead;
    char buffer[1];

    // Open the serial port
    hSerial = CreateFile(portName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hSerial == INVALID_HANDLE_VALUE) 
    {
        printf("Error opening serial port %s\n", portName);
        return;
    }

    // Set serial port parameters
    dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
    if (!GetCommState(hSerial, &dcbSerialParams)) 
    {
        printf("Error getting serial port state\n");
        CloseHandle(hSerial); // Need to close the port before returning from the function since it was already open
        return;
    }

    dcbSerialParams.BaudRate = CBR_9600; // Set your desired baud rate
    dcbSerialParams.ByteSize = 8;
    dcbSerialParams.StopBits = ONESTOPBIT;
    dcbSerialParams.Parity = NOPARITY;

    if (!SetCommState(hSerial, &dcbSerialParams)) 
    {
        printf("Error setting serial port state\n");
        CloseHandle(hSerial); // Need to close the port before returning from the function since it was already open
        return;
    }

    // Set timeouts
    timeouts.ReadIntervalTimeout = 50;
    timeouts.ReadTotalTimeoutConstant = 50;
    timeouts.ReadTotalTimeoutMultiplier = 10;

    if (!SetCommTimeouts(hSerial, &timeouts)) 
    {
        printf("Error setting timeouts\n");
        CloseHandle(hSerial); // Need to close the port before returning from the function since it was already open
        return;
    }

    while(1) // While loop to keep reading the COM port
    {
        if (ReadFile(hSerial, buffer, sizeof(buffer), &bytesRead, NULL))
        {
            if (bytesRead > 0) // Port has picked up the flags sent from Arduino
            {
                if (!BTPortFound) // If BTPortFound = FALSE (meaning that bluetooth port hasn't been found yet)
                {
                    BTPortFound = TRUE; // Bluetooth port has been found
                    BTPortName = portName;
                }
            }
            else // Port hasn't picked up the flags from Arduino
            {
                break; // Break out of the while loop ---> go to check next port
            }
        }  
    }
    CloseHandle(hSerial);
    return;
}

void dataLoggingToFile(FILE* file, char* BTPortName)
{
    HANDLE BTSerial;
    DCB dcbSerialParams = {0};
    COMMTIMEOUTS timeouts = {0};
    DWORD bytesRead;
    char binaryStringBuffer[1024];

    // Open the serial port
    BTSerial = CreateFile(BTPortName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (BTSerial == INVALID_HANDLE_VALUE) 
    {
        printf("Error opening serial port %s\n", BTPortName);
        return;
    }

    // Set serial port parameters
    dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
    if (!GetCommState(BTSerial, &dcbSerialParams)) 
    {
        printf("Error getting serial port state\n");
        CloseHandle(BTSerial); // Need to close the port before returning from the function since it was already open
        return;
    }

    dcbSerialParams.BaudRate = CBR_9600; // Set your desired baud rate
    dcbSerialParams.ByteSize = 8;
    dcbSerialParams.StopBits = ONESTOPBIT;
    dcbSerialParams.Parity = NOPARITY;

    if (!SetCommState(BTSerial, &dcbSerialParams)) 
    {
        printf("Error setting serial port state\n");
        CloseHandle(BTSerial); // Need to close the port before returning from the function since it was already open
        return;
    }

    // Set timeouts
    timeouts.ReadIntervalTimeout = 50;
    timeouts.ReadTotalTimeoutConstant = 50;
    timeouts.ReadTotalTimeoutMultiplier = 10;

    if (!SetCommTimeouts(BTSerial, &timeouts)) 
    {
        printf("Error setting timeouts\n");
        CloseHandle(BTSerial); // Need to close the port before returning from the function since it was already open
        return;
    }

    while(1)
    {
        if (ReadFile(BTSerial, binaryStringBuffer, sizeof(binaryStringBuffer), &bytesRead, NULL))
        {
            if (bytesRead > 0)
            {
                fwrite(binaryStringBuffer, 1, bytesRead, file);
                fflush(file);
            }
        }
    }
}

int main() 
{
    HKEY hKey;
    LONG result;
    char** portArray; // Array of COM Ports
    int portCount; // Number of COM Ports
    char fileName[50];

    BTPortFound = FALSE; // Flag used to check if BT port is found

    printf("\n");
    printf("|--------- Beginning Application ---------|\n\n\n");
    printf("Finding COM ports on computer .... \n");

    portFinder(hKey, result, &portArray, &portCount);
    printf("Found %d COM ports on computer: \n\n", portCount);

    if (portCount > 0)
    {
        // Iterating through all the ports on computer to determine which one is receiving the data via bluetooth
        for (int i = 0; i < portCount; i++)
        {
            char *portName = portArray[i];
            if (!BTPortFound) // Call portInfoChecker if BTPortFound = FALSE (meaning that bluetooth port hasn't been found yet)
            {
                portInfoChecker(portName);
            }
        }

        for (int i = 0; i < portCount; i++)
        {
            if (portArray[i] == BTPortName)
            {
                printf("\t%s:\tBluetooth Receiving Port\n", portArray[i]);
                if (i == portCount - 1)
                {
                    printf("\n");
                }
            }
            else
            {
                printf("\t%s\n", portArray[i]);
                if (i == portCount - 1)
                {
                    printf("\n");
                }
            }
        }

        time_t t;
        struct tm* tm_info;

        time(&t);
        tm_info = localtime(&t);

        if (tm_info != NULL) 
        {
            // Format the filename based on local time
            snprintf(fileName, sizeof(fileName), "log_%04d-%02d-%02d_%02d-%02d-%02d.txt",
            tm_info->tm_year + 1900, tm_info->tm_mon + 1, tm_info->tm_mday,
            tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec);
        }

        // Open the file for writing
        FILE* file = fopen(fileName, "w");
        if (file == NULL) 
        {
            printf("Error opening file %s\n", fileName);
        }
        printf("Start writing to text file .... \n");
        dataLoggingToFile(file, BTPortName);
    }

    else
    {
        printf("No COM ports found ... Please reconnect bluetooth\n");
    }

    return 0;
}

The problem I have is when I want to open the same serial port (the one receiving the flags) again in the dataLoggingToFile() function to read from it, it is giving an error. I used the GetLastError() function to obtain the error code which was 1168 ERROR_NOT_FOUND.

So my question is, can I not open the same port twice?

0

There are 0 best solutions below