ASCII TCP protocols in Windows ANSI C or C++


My recent project work included applications with a servo drive controller that offers a simple text-based protocol via TCP. We many times explain this to our clients as the fastest way to integrate the drive, especially if their part is a standalone Windows software project that does not already have any fieldbus communications up and running. But to our own surprise, discussions went frequently like this:

“We recommend the direct TCP interface - this is so much easier than adding extra Ethercat or Profinet equipment for this purpose.“


“Yes, but how does TCP work? ”


“Easy. Just open a TCP client connection to the drive controller, and send these simple ASCII commands.”


“Yes, but I have no idea about TCP connections. Don’t you have a simple DLL for this?”


So we decided to help out and make a DLL indeed. Not one that implements the servo controller interface, but a simplistic and generic way of processing text-based protocols and dealing with the TCP socket connection. And I felt this might be interesting to other people.


So here it is - download the precompiled Windows 32-bit DLL and the source from


http://kickdrive.de/sw/FullmoCommLib.zip

and documentation

http://kickdrive.de/doc/FullmoCommLib_DevDoc.pdf

And this is the most simplistic ANSI C++ TCP demo I could come up with:


#include <iostream> 
using namespace std; 
 
int _tmain(int argc, _TCHAR* argv[]) 
{        
        cout << "FullmoCommLib C++ Style Demo" << endl; 
        cout << "Enter remote ip adress and TCP port in format <ipadress>:<port>, e.g. 
192.168.2.100:10001" << endl; 
        cout << "<ipadress>:<port> = ";  
        char channelName[80]; 
        cin.getline(channelName, 80, '\n'); 
        if (strlen(channelName) == 0) { 
                return 1; 
        } 
 
        FCommChannel fComm; 
 
        cout << "Opening " << channelName << " ... " << endl; 
        if (!fComm.open(channelName)) { 
                cout << "Failed to connect" << endl; 
        } 
        else { 
                cout << "Connected" << endl; 
 
                cout << "Sending: Hello World!" << endl; 
 
                int retVal = fComm.sendMsg("Hello World!\r", "ok\r", "error\r"); 
                if (retVal == 0) { 
                        printf("send ok, but no answer\n"); 
                } 
                else if (retVal == 1) { 
                        printf("'ok' received\n"); 
                } 
                else if (retVal == 2) { 
                        printf("'error' received\n"); 
                } 
                else { 
                        printf("send failed\n"); 
                } 
 
                // read first line of RX data 
                char answerBuf[256]; 
                const int bufSize = 256;         
                int answerSize = fComm.readMsg(answerBuf, bufSize, "\r"); 
                cout << "first RX line: " << answerBuf << endl; 
 
                // done 
                fComm.close(); 
                cout << "Socket closed" << endl; 
        }  
        cout << "Press [ENTER]..." << endl; 
        cin.get();       
        return 0;  
}

The same thing in ANSI C, using a default Visual Studio 2008 console application template:


#include "fullmocommlibc.h" #include <string.h> 
 
int _tmain(int argc, _TCHAR* argv[]) 
{        
        printf("FullmoCommLib C-Style Demo\n"); 
        printf("Enter remote ip adress and TCP port in format <ipadress>:<port>, e.g.
192.168.2.100:10001\n"); 
        printf("<ipadress>:<port> = "); 
        char channelName[80]; 
        scanf_s("%s", channelName, 80); 
        fgetc(stdin); // Discard ENTER  
        if (strlen(channelName) == 0) { 
                return 1; 
        } 
 
        printf("Opening %s ...\n", channelName); 
        if (!fCommOpen(channelName)) { 
                printf("Failed to connect\n"); 
                // never forget to clean up in C! 
                fCommClose(); 
        } 
        else { 
                printf("Connected\n"); 
                printf("Sending: Hello World!\n"); 
                 
                // clear all previous communication before sending the new command 
                fCommClearMsgBuffer(); 
 
                int retVal = fCommSendMsg("Hello World!\r", "ok\r", "error\r"); 
                if (retVal == 0) { 
                        printf("send ok, but no answer\n"); 
                } 
                else if (retVal == 1) { 
                        printf("'ok' received\n"); 
                } 
                else if (retVal == 2) { 
                        printf("'error' received\n"); 
                } 
                else { 
                        printf("send failed\n"); 
                } 
 
                // read first line of RX data 
                char answerBuf[256]; 
                const int bufSize = 256;         
                int answerSize = fCommReadMsg(answerBuf, bufSize, "\r"); 
                printf("first RX line: %s\n", answerBuf); 
 
                // done 
                fCommClose(); 
                printf("Socket closed\n"); 
        }  
 
        printf("Library cleanup...\n"); 
        fCommLibCleanup(); 
        printf("Press [ENTER]...\n"); 
        getchar();  
        return 0;  
}


Feel free to use this as you please, just keep in mind this is not a maintained and fully developed library. Errors and omissions expected, really. Again, the links: http://kickdrive.de/sw/FullmoCommLib.zip and http://kickdrive.de/doc/FullmoCommLib_DevDoc.pdf . 

The DLL for the FCommChannel functionality uses Qt 4.8 core and networking libraries, please refer to the documentation on the necessary files for distribution. I will soon move this to Qt 5, and add Qt 5’s QtSerialPort interface for accessing COM ports to it. So you will be able to do things like


fComm.open("COM7:9600,none,8,1")
 
Any suggestions or comments welcome!


Comments

Popular posts from this blog

Python syntax highlighting for QTextEdit C++/Qt

Windows DLL for Microchip PIC32 firmware update / UBL bootloader

On CRCs and CRC Reverse Engineering