Serial port programming using Win32 API (Windows)

Serial port programming

Sometimes we require to communicate with an external device like a printer, microcontroller board or any serial device using the serial port of a windows machine. There is a lot of serial application available like Hercules, HyperTerminal, Docklight, ..etc.

We can use any one of them for serial communication but sometimes we require to create our own custom serial application for communication. In windows, it is easy to create the custom serial application using the win32 API.

In this blog post, we will learn serial port programming using the Win32 API. In Windows, serial port programming is very easy, MSDN provide all the required win32 API information which require for the serial port programming.

You can also see the below articles that how to change the properties of com-port like baud rate, parity using the Windows API.

Finding COM port Number of a Device

In windows, the serial device will display in com port section of device manager with name as COM1, COM2, COM3, COM4.. etc. Generally, COM1 and COM2 refer to the hardware serial ports present in the PC and another com port number is export when any serial device or USB to serial device attached to PC. It also possible that com id could be virtual ( for example static virtual com port).

In my laptop, I have attached an Arduino board (Atmega 2560) and its COM id would be shown in com port section (Control Panel > Device Manager > Ports).

port class

 

 

Compilers and IDE’s used

Here I have used Visual Studio 2013. You can also use MinGW (Minimalist GNU for Windows) an Open Source programming tool.  Here I am assuming that you know how to create the console application using the visual studio and familiar with win32 API.

 

Opening a Serial Port

In Windows using the CreateFile(), we can open the serial port. The CreateFile() is a Win32 API that creates or opens a file or I/O device.

On success CreateFile() returns a handle that can be used to access the file or device depending on the flags and attributes specified.

HANDLE CreateFile(
    LPCTSTR lpFileName,          // pointer to name of the file
    DWORD dwDesiredAccess,       // access (read-write) mode
    DWORD dwShareMode,           // share mode
    LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    // pointer to security attributes
    DWORD dwCreationDisposition,  // how to create
    DWORD dwFlagsAndAttributes,  // file attributes
    HANDLE hTemplateFile         // handle to file with attributes to
    // copy
);

 

Closing a Serial Port

You should remember that after opening the com port using the CreateFile(), you have to close it by calling the CloseHandle() otherwise it will be unavailable to other applications.

BOOL CloseHandle(
  HANDLE hObject
);

 

On success CloseHandle() returns zero.

 

Serial port communication Application

Let see an example code where I am talking with a Mega2560 Arduino device. I have written a small code for mega2560 in which it receives serial data. If received data is “aticleworld” then it will send a message “Welcome to AticleWorld !” or otherwise it will send the message “Please Send Correct Message”.

Mega2560 serial code,

#include <SoftwareSerial.h>

void setup()
{
    // initialize serial ports
    Serial.begin(9600);    // USB serial port 0
}


void loop()
{
    String msg = "";
    // check for data byte on USB serial port
    if (Serial.available())
    {
        // get byte from USB serial port
        while(Serial.available())
        {
            msg = Serial.readString();// read the incoming data as string
        }
        if( msg == "aticleworld")
        {
            //Send data to usb serial port
            Serial.write(" Welcome to AticleWorld !");
        }
        else
        {
            //Send data to usb serial port
            Serial.write("Please Send Correct Message");
        }
    }
}

 

 

You can purchase Mega2560 from Amazon,  Click here

Serial Application for the console,

In the below console application, I am using the win32 API to open the com port and sending the data to the open COM port.  See the below video where I have downloaded the Arduino code in the Arduino board and communicating with this board using the console application.

#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>
#include <string.h>

int main(void)
{

    HANDLE hComm;  // Handle to the Serial port
    BOOL   Status; // Status
    DCB dcbSerialParams = { 0 };  // Initializing DCB structure
    COMMTIMEOUTS timeouts = { 0 };  //Initializing timeouts structure
    char SerialBuffer[64] = { 0 }; //Buffer to send and receive data
    DWORD BytesWritten = 0;          // No of bytes written to the port
    DWORD dwEventMask;     // Event mask to trigger
    char  ReadData;        //temperory Character
    DWORD NoBytesRead;     // Bytes read by ReadFile()
    unsigned char loop = 0;
    wchar_t pszPortName[10] = { 0 }; //com port id
    wchar_t PortNo[20] = { 0 }; //contain friendly name



    //Enter the com port id
    printf_s("Enter the Com Port: ");
    wscanf_s(L"%s", pszPortName, (unsigned)_countof(pszPortName));
    swprintf_s(PortNo, 20, L"\\\\.\\%s", pszPortName);

    //Open the serial com port
    hComm = CreateFile(PortNo, //friendly name
                       GENERIC_READ | GENERIC_WRITE,      // Read/Write Access
                       0,                                 // No Sharing, ports cant be shared
                       NULL,                              // No Security
                       OPEN_EXISTING,                     // Open existing port only
                       0,                                 // Non Overlapped I/O
                       NULL);                             // Null for Comm Devices

    if (hComm == INVALID_HANDLE_VALUE)
    {
        printf_s("\n Port can't be opened\n\n");
        goto Exit2;
    }

    //Setting the Parameters for the SerialPort
    dcbSerialParams.DCBlength = sizeof(dcbSerialParams);

    Status = GetCommState(hComm, &dcbSerialParams); //retreives  the current settings
    if (Status == FALSE)
    {
        printf_s("\nError to Get the Com state\n\n");
        goto Exit1;
    }

    dcbSerialParams.BaudRate = CBR_9600;      //BaudRate = 9600
    dcbSerialParams.ByteSize = 8;             //ByteSize = 8
    dcbSerialParams.StopBits = ONESTOPBIT;    //StopBits = 1
    dcbSerialParams.Parity = NOPARITY;      //Parity = None

    Status = SetCommState(hComm, &dcbSerialParams);
    if (Status == FALSE)
    {
        printf_s("\nError to Setting DCB Structure\n\n");
        goto Exit1;
    }

    //Setting Timeouts
    timeouts.ReadIntervalTimeout = 50;
    timeouts.ReadTotalTimeoutConstant = 50;
    timeouts.ReadTotalTimeoutMultiplier = 10;
    timeouts.WriteTotalTimeoutConstant = 50;
    timeouts.WriteTotalTimeoutMultiplier = 10;
    if (SetCommTimeouts(hComm, &timeouts) == FALSE)
    {
        printf_s("\nError to Setting Time outs");
        goto Exit1;
    }


    printf_s("\n\nEnter your message: ");
    scanf_s("%s", SerialBuffer, (unsigned)_countof(SerialBuffer));


    //Writing data to Serial Port
    Status = WriteFile(hComm,// Handle to the Serialport
                       SerialBuffer,            // Data to be written to the port
                       sizeof(SerialBuffer),   // No of bytes to write into the port
                       &BytesWritten,  // No of bytes written to the port
                       NULL);
    if (Status == FALSE)
    {
        printf_s("\nFail to Written");
        goto Exit1;
    }

    //print numbers of byte written to the serial port
    printf_s("\nNumber of bytes written to the serail port = %d\n\n", BytesWritten);

    //Setting Receive Mask
    Status = SetCommMask(hComm, EV_RXCHAR);

    if (Status == FALSE)
    {
        printf_s("\nError to in Setting CommMask\n\n");
        goto Exit1;
    }


    //Setting WaitComm() Event
    Status = WaitCommEvent(hComm, &dwEventMask, NULL); //Wait for the character to be received
    if (Status == FALSE)
    {
        printf_s("\nError! in Setting WaitCommEvent()\n\n");
        goto Exit1;
    }

    //Read data and store in a buffer
    do
    {
        Status = ReadFile(hComm, &ReadData, sizeof(ReadData), &NoBytesRead, NULL);
        SerialBuffer[loop] = ReadData;
        ++loop;
    }
    while (NoBytesRead > 0);

    --loop; //Get Actual length of received data

    printf_s("\nNumber of bytes received = %d\n\n", loop);

    //print receive data on console
    printf_s("\n\n");
    int index = 0;
    for (index = 0; index < loop; ++index)
    {
        printf_s("%c", SerialBuffer[index]);
    }

    printf_s("\n\n");

Exit1:
    CloseHandle(hComm);//Closing the Serial Port
Exit2:
    system("pause");
    return 0;
}

 

 

Recommended Posts for you:



Reference: https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-createfilea

3 comments

  1. Very nice article, thank you! I notice you are an embedded C software engineer, would you happen to have any recommendations on embedded books for beginners (win or linux)? Thank you for any feedback.

Leave a Reply

Your email address will not be published. Required fields are marked *