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?