skip to Main Content

I’m working on an application where I use specific protocol to send packet over COM port to a hardware. I have the protocol specifications document available. I’m able to send the packets using WriteFile(). Going through the protocol specs, I see that I must sent a specific packet every 50ms. I thought I could do this by having another thread sending this packet every 50ms so that I can do other read/write operations in the main thread. However when I try this it did not work (no TXD on RS422 adapter and not printing anything, I added cout to see if line is executed). Only way I could send packets was if the application was singlethreaded.

Here is the code:

void sendBootSignal(HANDLE h_Serial, byte packetArr[], DWORD size)
{
    DWORD dwWrite = 11;
    if (!WriteFile(h_Serial, packetArr, (DWORD)size, &dwWrite, NULL))
    {
        std::cout << "error while WriteFile" << std::endl;
    }
    else
    {
        std::cout << "succesfully send boot packet" << std::endl;
    }
}

void bootThread(HANDLE h_Serial, byte packetArr[], DWORD size)
{
    while (true)
    {
        sendBootSignal(h_Serial, packetArr, size);
        std::this_thread::sleep_for(std::chrono::milliseconds(50));
    }
}
int main()
{
    h_Serial = CreateFile(L"COM3", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    if (h_Serial == INVALID_HANDLE_VALUE)
    {
        if (GetLastError() == ERROR_FILE_NOT_FOUND)
        {
            std::cout << "Serial Port not found" << std::endl;
        }
        std::cout << "error while establishing communication" << std::endl;
    }

    DCB dcbSerialParam = { 0 };
    dcbSerialParam.DCBlength = sizeof(dcbSerialParam);

    if (!GetCommState(h_Serial, &dcbSerialParam))
    {
        std::cout << "error when getting comm state" << std::endl;
    }

    dcbSerialParam.BaudRate = CBR_115200;
    dcbSerialParam.ByteSize = 8;
    dcbSerialParam.StopBits = ONESTOPBIT;
    dcbSerialParam.Parity = NOPARITY;
    dcbSerialParam.fDtrControl = DTR_CONTROL_DISABLE;

    if (!SetCommState(h_Serial, &dcbSerialParam))
    {
        std::cout << "error while setting comm state" << std::endl;
    }

    COMMTIMEOUTS timeout = { 0 };
    timeout.ReadIntervalTimeout = 60;
    timeout.ReadTotalTimeoutConstant = 60;
    timeout.ReadTotalTimeoutMultiplier = 15;
    timeout.WriteTotalTimeoutConstant = 60;
    timeout.WriteTotalTimeoutMultiplier = 8;

    if (!SetCommTimeouts(h_Serial, &timeout))
    {
        std::cout << "timeout " << std::endl;
    }

    byte bootPacket[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

    bootPacket[0] = 0x10;
    bootPacket[1] = 0x00;
    bootPacket[2] = 0x80;
    bootPacket[3] = 0x00;
    bootPacket[4] = 0x48;
    bootPacket[5] = 0x00;
    bootPacket[6] = 255;
    bootPacket[7] = 0x00;
    bootPacket[8] = 0x00;
    bootPacket[9] = 0x10;
    bootPacket[10] = 0x03;

    DWORD size = 11;
    sizeCalculator(bootPacket, size);
    checksumCalculator(bootPacket, size);
    std::thread t1(bootThread, h_Serial, bootPacket, size);


    while (true)
    {
        // do other things here
    }

    CloseHandle(h_Serial);
}

Is this approach incorrect? If yes, what would be a better approach to this problem?


EDIT 1

I found out that the reason why it was getting stuck was not because of WriteFile() rather the std::cout. The question at the end is still relevant. I’d appreciate if someone can comment on why cout in the else block of sentBootSignal() just stops the application.


EDIT 2

Restarting Visual Studio fixed the cout issue however I’m still wondering about the multithreading this problem. Appreciate the responses.

2

Answers


  1. I would expect your code to protect sending the data at least using a mutex.
    The example also shows some C++ style alternatives to using "C" style arrays for sending the data

    #include <array>
    #include <iostream>
    #include <mutex>
    #include <span>
    #include <Windows.h>
    
    
    class YourDevice
    {
    public:
        YourDevice() 
        {
            // open serial port etc...
        }
    
        ~YourDevice()
        {
            // close serial port etc...
        }
    
        // send a packet of N bytes
        // Note I use std::uint8_t instead of char to avoid confusion with strings (and signedness)
        // used when size is know at compile time
        template<DWORD N>
        void sendPacket(const std::uint8_t(&packet)[N])
        {
            // ensure only one thread can send a packet at a time
            std::scoped_lock lock(m_mtx);
            
            DWORD dwWrite = N;
            if (!WriteFile(m_serial, packet, N, &dwWrite, NULL))
            {
                std::cout << "error while WriteFile" << std::endl;
            }
            else
            {
                std::cout << "succesfully send boot packet" << std::endl;
            }
        }
        
        // used when size is only known at runtime
        void SendPacket(std::span<int> packet)
        {
             // ensure only one thread can send a packet at a time
            std::scoped_lock lock(m_mtx);
    
            DWORD dwWrite = packet.size();
            if (!WriteFile(m_serial, packet.data(), packet.size(), &dwWrite, NULL))
            {
                std::cout << "error while WriteFile" << std::endl;
            }
            else
            {
                std::cout << "succesfully send boot packet" << std::endl;
            }
        }
    
        void sendBootSignal()
        {
            // the packed is a sequence of 11 bytes initialized with the values provided within the curly braces
            sendPacket({0x10, 0x00, 0x80, 0x00, 0x48, 0x00, 0xFF, 0x00, 0x00, 0x10, 0x03});
        }
    
    private:
        std::mutex m_mtx;
        HANDLE m_serial;
    
    };
    
    int main()
    {
        YourDevice device;
        device.sendBootSignal();
    }
    
    Login or Signup to reply.
  2. The multithreading method is as follows:

    #include<windows.h>
    #include<cstdio>
    #include<iostream>
    #include<thread>
    HANDLE h_Serial; byte packetArr[]; DWORD size;
    void sendBootSignal(HANDLE h_Serial, byte packetArr[], DWORD size)
    {
        DWORD dwWrite = 11;
        if (!WriteFile(h_Serial, packetArr, (DWORD)size, &dwWrite, NULL))
        {
            std::cout << "error while WriteFile" << std::endl;
        }
        else
        {
            std::cout << "succesfully send boot packet" << std::endl;
        }
    }
    void bootThread(HANDLE h_Serial, byte packetArr[], DWORD size)
    {
        while (true)
        {
            sendBootSignal(h_Serial, packetArr, size);
            std::this_thread::sleep_for(std::chrono::milliseconds(50));
        }
    }
    DWORD WINAPI ThreadProc(LPVOID lpParameter)
    {
        while (true)
        {
            sendBootSignal(h_Serial, packetArr, size);
            std::this_thread::sleep_for(std::chrono::milliseconds(50));
        }
    
        return 1;
    }
    DWORD WINAPI ThreadProc2(LPVOID lpParameter)
    {
        h_Serial = CreateFile(L"COM3", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
        if (h_Serial == INVALID_HANDLE_VALUE)
        {
            if (GetLastError() == ERROR_FILE_NOT_FOUND)
            {
                std::cout << "Serial Port not found" << std::endl;
            }
            std::cout << "error while establishing communication" << std::endl;
        }
    
        DCB dcbSerialParam = { 0 };
        dcbSerialParam.DCBlength = sizeof(dcbSerialParam);
    
        if (!GetCommState(h_Serial, &dcbSerialParam))
        {
            std::cout << "error when getting comm state" << std::endl;
        }
    
        dcbSerialParam.BaudRate = CBR_115200;
        dcbSerialParam.ByteSize = 8;
        dcbSerialParam.StopBits = ONESTOPBIT;
        dcbSerialParam.Parity = NOPARITY;
        dcbSerialParam.fDtrControl = DTR_CONTROL_DISABLE;
    
        if (!SetCommState(h_Serial, &dcbSerialParam))
        {
            std::cout << "error while setting comm state" << std::endl;
        }
    
        COMMTIMEOUTS timeout = { 0 };
        timeout.ReadIntervalTimeout = 60;
        timeout.ReadTotalTimeoutConstant = 60;
        timeout.ReadTotalTimeoutMultiplier = 15;
        timeout.WriteTotalTimeoutConstant = 60;
        timeout.WriteTotalTimeoutMultiplier = 8;
    
        if (!SetCommTimeouts(h_Serial, &timeout))
        {
            std::cout << "timeout " << std::endl;
        }
    
        byte bootPacket[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    
        bootPacket[0] = 0x10;
        bootPacket[1] = 0x00;
        bootPacket[2] = 0x80;
        bootPacket[3] = 0x00;
        bootPacket[4] = 0x48;
        bootPacket[5] = 0x00;
        bootPacket[6] = 255;
        bootPacket[7] = 0x00;
        bootPacket[8] = 0x00;
        bootPacket[9] = 0x10;
        bootPacket[10] = 0x03;
    
        DWORD size = 11;
        sizeCalculator(bootPacket, size);
        checksumCalculator(bootPacket, size);
        std::thread t1(bootThread, h_Serial, bootPacket, size);
    
    
        while (true)
        {
            // do other things here
        }
    
        CloseHandle(h_Serial);
        return 0;
    }
    int main(int argc, char* argv[])
    {
        HANDLE hThread[2];
        DWORD dWResult1;
        DWORD dWResult2;
        hThread[0] = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
        hThread[1] = CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL);
    
    
        WaitForMultipleObjects(2, hThread, true, INFINITE);
        GetExitCodeThread(hThread[0], &dWResult1);
        GetExitCodeThread(hThread[1], &dWResult2);
        printf("Thread execution completed n");
        getchar();
        CloseHandle(hThread[0]);
        CloseHandle(hThread[1]);
    
    
        return 0;
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search