ComPrinter

Image link to download ComPrinter.exeComPrinter is a console application (i.e. a command line program) that opens a serial port and displays incoming text characters in the console. It features several useful options, including the following:

  • ComPrinter supports a user-specified COM port number.
  • ComPrinter supports a user-specified baud rate.
  • By default, ComPrinter opens the highest available COM port (especially useful if you’re using a USB-to-serial converter).
  • ComPrinter provides a facility to simulate a keystroke for each incoming character. In combination with a USB-to-serial converter, this provides a very simple way of turning a microcontroller system with serial output into a communication device.
  • It provides an ideal free alternative to HyperTerminal in many applications.

Support ComPrinter Development

ComPrinter is completely free to download and use. You can support ComPrinter development by letting me know if you find it useful (just leave a comment below).

Why use ComPrinter?

I do a lot of microcontroller development and I regularly use a serial connection to send text from a microcontroller to a PC to be displayed on screen. I previously used HyperTerminal to open the serial port and display the incoming text. However, as of Windows 7, that application is no longer installed with the operating system, so I needed a convenient free HyperTerminal alternative. As it turns out, ComPrinter is actually more convenient than HyperTerminal for most of the things I do.

My personal favourite ComPrinter feature is that by default it opens the highest available COM port. I sometimes use a low-cost USB-to-serial converter to communicate between a microcontroller circuit and my PC. However, each time I plug in my USB-to-serial converter, it is assigned a different (and usually quite high) COM port number. So far, ComPrinter has always automatically found and opened it without difficulty.

Can I use / modify / sell / do [whatever] with ComPrinter without paying?

Short answer, yes. You can basically do whatever you want with it. If you want to modify it, go ahead. If you want to include it (or part of it) in something you’re selling, that’s fine. All I ask is that you don’t pretend that you (or somebody else) wrote what I wrote. Realistically though, even if you did that I probably wouldn’t know, and even if I did know I probably wouldn’t do anything about it. So, in short, just go ahead and use it however you want.

Running ComPrinter

There are several options when you run ComPrinter:

  • /devnum DEVICE_NUMBER : Use this to specify a COM port.
  • /baudrate BITS_PER_SECOND : Use this to specify the baud rate.
  • /keystrokes : Use this to simulate a keystroke for each incoming character, for example to type text into an application.
  • /debug : Use this to display additional information when opening the COM port.
  • /quiet : Use this to supress the welcome message text and other information. Only text received via the COM port will be displayed.

Example Commands

To open the highest available COM port at the default baud rate (2400 bits per second) and display all incoming text:

ComPrinter.exe

To open COM22 at 38400 baud and display all incoming text:

ComPrinter.exe /devnum 22 /baudrate 38400

To open the highest available COM port at 38400 baud and simulate a keystroke for each incoming character as well as printing it in the console window:

ComPrinter.exe /baudrate 38400 /keystrokes

Source Code

ComPrinter is a Win32 console application, written in C. I compiled it with the MinGW version of gcc. The complete source code is below.

If you want to compile it with Visual Studio C++, check out Bill Richman’s port on github. Thanks Bill!

//
// ComPrinter.c - This program reads text from a
//   serial port and prints it to the console.
//
// Written by Ted Burke - last updated 3-5-2012
//
// To compile with MinGW:
//
//      gcc -o ComPrinter.exe ComPrinter.c
//
// Example commands:
//
//      ComPrinter /devnum 22 /baudrate 38400
//      ComPrinter /id 12 /quiet
//      ComPrinter /baudrate 38400 /keystrokes
//
// To stop the program, press Ctrl-C
//
 
#define WINVER 0x0500
 
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
 
#define MESSAGE_LENGTH 100
 
// Declare variables and structures
HANDLE hSerial = INVALID_HANDLE_VALUE;
DCB dcbSerialParams = {0};
COMMTIMEOUTS timeouts = {0};
DWORD dwBytesWritten = 0;
char dev_name[MAX_PATH] = "";
int dev_number = -1;
int baudrate = 2400;
int scan_max = 30;
int scan_min = 1;
int simulate_keystrokes = 0;
int debug = 1; // print some info by default
int id = -1;
 
void CloseSerialPort()
{
    if (hSerial != INVALID_HANDLE_VALUE)
    {
        // Close serial port
        fprintf(stderr, "\nClosing serial port...");
        if (CloseHandle(hSerial) == 0)
            fprintf(stderr, "Error\n");
        else fprintf(stderr, "OK\n");
    }
}
 
void exit_message(const char* error_message, int error)
{
    // Print an error message
    fprintf(stderr, error_message);
    fprintf(stderr, "\n");
 
    // Exit the program
    exit(error);
}
 
void simulate_keystroke(char c)
{
    // This structure will be used to create the keyboard
    // input event.
    INPUT ip;
 
    // Set up a generic keyboard event.
    ip.type = INPUT_KEYBOARD;
    ip.ki.wScan = 0; // hardware scan code for key
    ip.ki.time = 0;
    ip.ki.dwExtraInfo = 0;
 
    // Press the key
    // Currently only alphabet lettes, spaces, commas and full stops.
    if (c >= 0x61 && c <= 0x7A) c -= 0x20;
    if (c >= 0x30 && c <= 0x39) ip.ki.wVk = c;
    else if (c >= 0x41 && c <= 0x5A) ip.ki.wVk = c;
    else if (c == ' ') ip.ki.wVk = VK_SPACE;
    else if (c == ',') ip.ki.wVk = VK_OEM_COMMA;
    else if (c == '.') ip.ki.wVk = VK_OEM_PERIOD;
    else if (c == '\b') ip.ki.wVk = VK_BACK;
    else if (c == '\t') ip.ki.wVk = VK_TAB;
    else if (c == '\n') ip.ki.wVk = VK_RETURN;
    else return;
 
    ip.ki.dwFlags = 0; // 0 for key press
    SendInput(1, &ip, sizeof(INPUT));
 
    // Release the key
    ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release
    SendInput(1, &ip, sizeof(INPUT));
}
 
int main(int argc, char *argv[])
{
    // Parse command line arguments. Available options:
    //
    // /devnum DEVICE_NUMBER
    // /baudrate BITS_PER_SECOND
    // /id ROBOT_ID_NUMBER
    // /keystrokes
    // /debug
    // /quiet
    //
    int n = 1;
    while (n < argc)
    {
        // Process next command line argument
        if (strcmp(argv[n], "/devnum") == 0)
        {
            if (++n >= argc) exit_message("Error: no device number specified", 1);
 
            // Set device number to specified value
            dev_number = atoi(argv[n]);
        }
        else if (strcmp(argv[n], "/baudrate") == 0)
        {
            if (++n >= argc) exit_message("Error: no baudrate value specified", 1);
 
            // Set baudrate to specified value
            baudrate = atoi(argv[n]);
        }
        else if (strcmp(argv[n], "/id") == 0)
        {
            if (++n >= argc) exit_message("Error: no id number specified", 1);
 
            // Set id to specified value
            id = atoi(argv[n]);
        }
        else if (strcmp(argv[n], "/keystrokes") == 0)
        {
            simulate_keystrokes = 1;
        }
        else if (strcmp(argv[n], "/debug") == 0)
        {
            debug = 2;
        }
        else if (strcmp(argv[n], "/quiet") == 0)
        {
            debug = 0;
        }
        else
        {
            // Unknown command line argument
            if (debug) fprintf(stderr, "Unrecognised option: %s\n", argv[n]);
        }
 
        n++; // Increment command line argument counter
    }
 
    // Welcome message
    if (debug)
    {
        fprintf(stderr, "\nComPrinter.exe - written by Ted Burke\n");
        fprintf(stderr, "https://batchloaf.wordpress.com\n");
        fprintf(stderr, "This version: 3-5-2012\n\n");
    }
 
    // Debug messages
    if (debug > 1) fprintf(stderr, "dev_number = %d\n", dev_number);
    if (debug > 1) fprintf(stderr, "baudrate = %d\n\n", baudrate);
 
    // Register function to close serial port at exit time
    atexit(CloseSerialPort);
 
    if (dev_number != -1)
    {
        // Limit scan range to specified COM port number
        scan_max = dev_number;
        scan_min = dev_number;
    }
 
    // Scan for an available COM port in _descending_ order
    for (n = scan_max ; n >= scan_min ; --n)
    {
        // Try next device
        sprintf(dev_name, "\\\\.\\COM%d", n);
        if (debug > 1) fprintf(stderr, "Trying %s...", dev_name);
        hSerial = CreateFile(dev_name, GENERIC_READ|GENERIC_WRITE, 0, 0,
                                OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
        if (hSerial!=INVALID_HANDLE_VALUE)
        {
            if (debug > 1) fprintf(stderr, "OK\n");
            dev_number = n;
            break; // stop scanning COM ports
        }
        else if (debug > 1) fprintf(stderr, "FAILED\n");
    }
 
    // Check in case no serial port could be opened
    if (hSerial==INVALID_HANDLE_VALUE)
        exit_message("Error: could not open serial port", 1);
 
    // If we get this far, a serial port was successfully opened
    if (debug) fprintf(stderr, "Opening COM%d at %d baud\n\n", dev_number, baudrate);
 
    // Set device parameters:
    //  baudrate (default 2400), 1 start bit, 1 stop bit, no parity
    dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
    if (GetCommState(hSerial, &dcbSerialParams) == 0)
        exit_message("Error getting device state", 1);
    dcbSerialParams.BaudRate = baudrate;
    dcbSerialParams.ByteSize = 8;
    dcbSerialParams.StopBits = ONESTOPBIT;
    dcbSerialParams.Parity = NOPARITY;
    if(SetCommState(hSerial, &dcbSerialParams) == 0)
        exit_message("Error setting device parameters", 1);
 
    // Set COM port timeout settings
    timeouts.ReadIntervalTimeout = 50;
    timeouts.ReadTotalTimeoutConstant = 50;
    timeouts.ReadTotalTimeoutMultiplier = 10;
    timeouts.WriteTotalTimeoutConstant = 50;
    timeouts.WriteTotalTimeoutMultiplier = 10;
    if(SetCommTimeouts(hSerial, &timeouts) == 0)
        exit_message("Error setting timeouts", 1);
 
    // Read text and print to console (and maybe simulate keystrokes)
    int state = 1;
    //int i;
    char c;
    char message_buffer[MESSAGE_LENGTH];
    DWORD bytes_read;
 
    // Depending on whether a robot id has been specified, either
    // print all incoming characters to the console or filter by
    // the specified id number
    if (id == -1)
    {
        // No robot id specified - print everything to console
        while(1)
        {
            ReadFile(hSerial, &c, 1, &bytes_read, NULL);
            if (bytes_read == 1)
            {
                printf("%c", c);
                if (simulate_keystrokes == 1) simulate_keystroke(c);
            }
        }
    }
    else
    {
        // A robot id was specified - use a state machine to parse
        // incoming text and display only messages in the correct
        // format matching the specified id number
        while(1)
        {
            // Read next character
            ReadFile(hSerial, &c, 1, &bytes_read, NULL);
            if (bytes_read != 1) continue;
 
            if (state == 1)
            {
                // State 1: Waiting for '<' character to start message
                n = 0;
                strcpy(message_buffer, "");
                if (c == '<') state = 2;
            }
            else if (state == 2)
            {
                // State 2: Reading robot id one digit at a time
                if (c >= '0' && c <= '9') n = 10*n + (c-48);
                else if (c == '@') state = 3;
                else state = 1;
            }
            else if (state == 3)
            {
                // Got a '@' character, so now reading actual message
                if (n != id) state = 1;
                else if (c != '>') strncat(message_buffer, &c, 1);
                else if (c == '>') state = 4;
                if (strlen(message_buffer) >= MESSAGE_LENGTH-1) state = 4;
            }
            else if (state == 4)
            {
                // State 4: Display complete message
                fprintf(stdout, "%s\n", message_buffer);
                state = 1;
            }
            else state = 1;
        }
    }
 
    // We should never get to this point because when the user
    // presses Ctrl-C, the atexit function will be called instead.
    return 0;
}

106 Responses to ComPrinter

  1. Abdullah Tahir says:

    Sir I’m facing some problem. I have been writing code on VC++ 6.0. But I couldn’t get desired data (and even getting data more than 1 byte). I also downloaded your COM Printer software but it is showing: Opening COM2 at baud 2400 and then does nothing. Can you guide me to fix these two problems. Thanks.

  2. batchloaf says:

    Hi Abdullah,

    Do you know if COM 2 is the correct serial port device? Also, is 2400 the correct baudrate? If one of these is wrong, you’ll need to specify the desired device and/or baudrate on the command line. For example, the following command would open COM22 to receive data at 38400 baud (i.e. 38.4 kbps) and display the incoming bytes in the command window:

    ComPrinter.exe /devnum 22 /baudrate 38400
    
    Ted
  3. Anne M. says:

    Thanks for this application, it’s really handy… !! However, is it possible to run it while the COM port is already opened and used by another program ? I would like to use ComPrinter, let’s say on COM 5, but without turning off a software that needs to constantly read the output of this COM5…

    • batchloaf says:

      Hi Anne,

      Unfortunately, I don’t think that’s possible. A single program can certainly read from and write to a serial port device at the same time, but I don’t think it’s possible for two separate programs to open the same device simultaneously, even if one is reading and one is writing. There’s probably some sort of virtual serial port software out there somewhere that can open a single serial port, then provide two or more virtual serial ports for other programs to access, but I haven’t ever looked for one.

      As a matter of interest, what is the software that is reading continuously from COM5? If you can explain a little bit more about the application, perhaps I can suggest a workaround.

      Ted

  4. Ramesh says:

    can u send a source code for write bytes to a serial port

    • batchloaf says:

      Hi Ramesh,

      Sorry, I haven’t had a chance to do this yet, but I’ll do my best to post a simple serial port reading/writing example in C later on today if I can.

      Ted

      • Ramesh says:

        Hmmm thanks Ted….send me as soon as possible

      • Ramesh says:

        Hi Ted ,
        I know its not possible to read and write a serial port in c language simultaneously
        i think we can do it in C threads…. can u jst help me to do read/write serial port in C threads

  5. Ramesh says:

    …IS IT POSSIBLE TO WRITE A PROGRAM FOR SERIAL PORT WRITE/READ AS A SINGLE PROGRAM USING THREADS…..

  6. Ken says:

    Along the lines of Ramesh’s request… I am looking to start the program with an argument for the com port number, send a single character “+”, and get back the text from a barcode read. Posted to STDOUT so it can be piped to a file. The program needs to die after doing this.

  7. Ken says:

    ComBCread.exe /devnum 5 /baudrate 9600 /data 8N1 /char + /file bc_result.txt

  8. tim says:

    Just starting programming in C. I’ve been programming the 8051 in assembly for several years now, and since GW-BASIC stopped being supported, have been looking for a way to write my own programs for the P.C. to interface with my 8051 projects. I’m using Pelles C for Windows, which I can only assume works as well as any other free compiler.

    http://www.christian-heffner.de/index.php?page=download&lang=en&version=5.00.1

    Your serial programming example(s) are the only examples I’ve found I could get to work…thank you. So now I have a working example to breakdown and learn from.

    As for a terminal program, have you tried Brays Terminal. It’s truly awesome replacement for Hyper Terminal. Works with Windows 8 as long as your USB to 232 cable is compatible with Windows 8 (I had to buy a new cable when I got my new computer). Try it…you’ll like it.

    https://sites.google.com/site/terminalbpp/

    • batchloaf says:

      Hi Tim,

      Thanks for your comment. I’m glad to hear you found my examples useful, especially since it sounds like you’re working on applications which are in some ways similar to a lot of what I do. I’ve used several different microcontrollers over the years (PIC, MSP430, AVR) but I’ve actually never used 8051. When I started programming microcontrollers back in the 1990s, the development tools for 8051 were just way too expensive, so I cut my teeth on PICs.

      I’ve heard of the Pelles C compiler previously, but I haven’t tried it. Partly because I work on Windows and Linux alternately, I use GCC as my regular C compiler for PC programs. On Windows, I use MinGW which includes GCC and a number of other GNU development tools. Sometimes, I’m forced to use Visual C++ for a specific project, but I’m a lot happier using GCC, which makes it easier to strip things back to the bare essentials – no bloated project files and directories, just a plain text editor, my C files and a simple build script!

      I’m interested to hear that you’re a GW-BASIC refugee. I come across people all the time who loved using it and found it incredibly empowering. Several of my colleagues were bereft when it was retired. For what it’s worth, I urge you to take a look at Python. Firstly, it seems to tick a lot of the boxes for GW-BASIC aficionados – it just shares some of the same “spirit”. Secondly, with your specific applications in mind, the PySerial module is absolutely the best thing I’ve used for automating and experimenting with serial port interaction. In a very few short lines, you can open a serial port and send and receive arbitrary data. Incoming data can be really easily parsed and, for example, plotted on a graph, written to a file, or streamed over TCP/IP.

      Personally, I really enjoy programming in Python. After C (or alongside C), it’s my favourite language. Also, when I have students working on serial port interaction with microcontroller, I almost always suggest that they use Python on the PC end to get things working fast and they usually become firm fans of it and start using it for all sorts of other things.

      Anyway, thanks again for the interesting message, and especially for the Bray Terminal suggestion – it looks brilliant and is just the kind of thing I would find useful!

      Ted

      EDIT: Here’s a link to some really short examples that show how easy it is to open and use a serial port in Python:

      http://pyserial.sourceforge.net/shortintro.html

      • tim says:

        GW-Basic was the bomb for our type of programming. However, it was slow because it had to run within it’s own interpreter until IBM came out with a ‘compiler’ that converted basic to stand alone .exe programs. Then it went from the ‘bomb’ to the A-bomb! There were several other ‘basics’ that came out with code similar to what I’m finding in C.

        I chose C because of all the available information. However, I’m finding the information is a bit misleading because most of it is too advanced for my level right now and it’s hard to get a handle on the basics. Im reading the ‘C’ book right now and doing the newbie examples but I’ll try Python… I don’t know anything about any of them right now so I guess one is as good as the other. I just need to find something I can use….

  9. tim says:

    I am proud to be the first to click your ‘This button has literally never been clicked’ button…

  10. tim says:

    Glad to contribute…

    I took your suggestion and am trying Python. I’m having trouble getting the PYSerial examples you recommended to work. I’m thinking it’s because I’m using version 3.4 and the examples are written for an earlier version (2.x…??). Do you have any insight on this? Should I install 2.7 instead of 3.4 or try running both or??

    Thanks again

    • batchloaf says:

      Hi Tim,

      As it happens, I’m still running Python 2.7, so I haven’t actually tried using PySerial with Python 3.x. However, the PySerial installation page refers to Python 3.x as if they are compatible with each other. For example, see here in the installation section:

      http://pyserial.sourceforge.net/pyserial.html#from-source-tar-gz-or-checkout

      Once you have installed Python, there are a few different methods of installing the PySerial add-on. If you haven’t already done so, I suggest using the MS Windows installer for PySerial, which is available from here:

      https://pypi.python.org/pypi/pyserial

      Make sure you select the installer version for Python 3.x.

      If PySerial still doesn’t work after running that installer, I have two further suggestions you could consider:

      • You could try Python 2.7 instead. There are some minor differences between Python versions 2.x and 3.x, but for your purposes it won’t make much (any?) difference to step back to 2.7, so if that gets it working then great. You can install version 2.7 alongside version 3.x, but you’ll need to install PySerial separately for Python 2.7 as well. Also, you’ll need to be careful which version you’re running each time you start it up!
      • Another thing you could consider (either with 3.x or 2.7) is to install win32all, which is an add-on package for Python which provides Python programs with access to Microsoft’s full Win32 API. It seems plausible (to me) that this might be required to get PySerial running, but I’m just guessing. I already have win32all installed, so it’s impossible for me to compare, but if I didn’t maybe I would also see an error?

      If you do decide to install win32all (and it won’t do any harm to try), you can download the installer here:

      http://sourceforge.net/projects/pywin32/files/pywin32/Build 219/

      The file you want from that page is probably pywin32-219.win32-py3.4.exe although you might need to pick a different one if you’re running 64-bit Windows.

      Also, perhaps you might cut and paste the exact error you’re seeing? That might help me to identify the specific problem.

      Finally, let me just offer a few words of encouragement, lest you should lose heart! I find it very frustrating when I can’t get something working at all, and I know it’s tempting to move on to a different approach, but please rest assured: your effort here will be richly rewarded. Once you get this installation sorted, and you transfer your first bytes from microcontroller to Python, you’ll immediately see how unbelievable flexible and empowering this is. So, in short, keep the faith!

      Ted

  11. jiraheta says:

    Hi Ted, I grabbed your copy of comPrinter. But for some reason I get the following compilation errors. Was wondering if you can give me a hand to figure this out what the issue is.
    Thanks.

    the errors are as follow:
    66: In function ‘void simulate_keystroke(char)’
    66: ‘INPUT’ undeclared {first use in this function)
    66: (Each undeclared identifier is reported only once for each function it appears in.)
    66: parse error before ‘;’
    69: ‘ip’ undeclared (first use this function)
    69: ‘INPUT_KEYBOARD’ undeclared (first use this function)
    72: confused by earlier errors, bailing out

    I notice this are variables that are not declared. So I am assuming those should be declared somewhere and I might be missing something. I have copied your code exactly as it is above and using the same compiler you are using in case of compatibility issues with other compilers (which I don’t think its the issue). Thanks for you help on this matter.

    • batchloaf says:

      Hi Jiraheta,

      If you’re using the same compiler as me and have copied the code exactly from what is shown above, then I’m not quite sure why you’re getting that error, but it’s probably something to do with the following:

      “INPUT” is a type of generic data structure which is used to store information about a user input event. For example, it could store all the information about a key press – which key was pressed, whether it was pressed or released, whether any modifier keys such as Alt or Ctrl were pressed at the same time. Or it could store the information relating to a mouse click – where the mouse pointer was when the button was clicked, which button was clicked, etc. In this program, we’re using an INPUT structure to store the information about a simulated keyboard event that is then actually carried out using the SendInput function.

      What I think behind the problem you’re having is that INPUT and SendInput were only introduced into the Win32 API in (I think) Windows NT, so when you compile a program using these features it won’t be compatible with versions of Windows earlier than that. The compiler only makes INPUT, SendInput, etc available (by defining them) if it knows that you don’t intend to be compatible with these earlier (ancient actually) versions of Windows.

      The following line in the program tells the compiler that we’re only compiling for compatibility with Windows NT (I think) and later:

      #define WINVER 0x0500
      

      It’s critically important that that line appears before this one…

      #include <windows.h>
      

      …because INPUT, SendInput, etc will be defined by “windows.h” if WINVER is defined as 0x0500 or higher.

      If you leave out the line that defines WINVER, you get errors just like the ones you saw because INPUT won’t have been defined. Is it possible that that line has been commented out, removed, or moved to a point after #include windows.h?

      Ted

      • jiraheta says:

        Hi ted, and thank yo so much for your replay. I have check the code and it seems that the code is they way you described. #define WINVER 0x0500 before any of the imports. Is there anything else that you can think that might be cause thing issue? I will try to compile this on a different system with a winxp OS on it and see what the results are. Doing this, in consideration of the forward compatibility of the code with newer OS systems. This time around I was compiling on a win7 system. thank you so much for you help, and if you think of anything else that could be cause this issue… please do share.

        Take care,
        Jose

      • batchloaf says:

        Hi again Jose,

        I don’t know if this will make any difference or not, but I suppose it’s worth a try. Add the following line just before or after “#define WINVER 0x0500”:

        #define _WIN32_WINNT 0x0500
        

        I’m assuming both can be defined simultaneously, but in case it causes some errors, I suppose you could try removing the original “#define WINVER 0x0500”.

        I did a quick google search for other people getting similar errors compiling programs with MinGW and some of them seemed to solve their problems by including that line (see here for example). As before, the “#define _WIN32_WINNT 0x0500” must come before “#include “.

        Let me know if that makes any difference.

        Ted

      • batchloaf says:

        Ok, I did a bit more digging and I’m going to modify that suggestion a bit.

        Try putting this at the start of the program before #include

        #define WINVER 0x0501
        #define _WIN32_WINNT 0x0501
        #define _WIN32_WINDOWS 0x0501
        

        The reason I’m suggesting that is because of what I read on these pages (among others):

        Ted

  12. jiraheta says:

    Hi ted
    Thank you for following up with me on this issue. I am still looking around to solve this issue. I
    came across those solutions but they didn’t work for me. You can see the SC here.
    On both Subline and minGW give me the same output and same errors

    I will keep searching and see if I can find out a solution.

    • batchloaf says:

      Hi Jose,

      I’ve just tried compiling it myself on Windows 7 and I got no errors at all.

      I’m using a 32-bit laptop and I’m running GCC version 4.6.2. Are you running 64-bit Windows or maybe using a later version of GCC?

      Ted

    • batchloaf says:

      I’ve just tried compiling it with GCC version 4.8.1 and that worked perfectly for me too.

      Could this be a 32-bit / 64-bit compatibility thing?

      Ted

  13. jiraheta says:

    Thank you guys for your help on this. Yeah, I believe this error has to do with the 64bit version of windows. I compile on a 32 bit version and it works as expected. I got to look more into this as the machine where I need to run this is a 64 bit version :/

    thanks a million for all your help.

    • batchloaf says:

      Hi Jose,

      I’ll try to find a 64-bit PC in work that I can try compiling it on. If that really is the problem, I’d be optimistic that it can be solved. I don’t think there’s anything in the program that should particularly pose a problem in 64-bit Windows – it’s probably just a matter of getting it to compile with a 64-bit compiler. I’ll let you know if I find a solution. If you make any progress in the meantime, please let me know.

      Ted

  14. Burak says:

    Hi TED ,

    Thank you for sharing such a nice a functional program .
    What I’m trying to do is controlling a bunch of devices over network via telnet connection protocol .
    There is a server pc and there is a batch file in it which I use in order to send Serial commands through a TTL bridge . When I establish a ttl connection and run your program using my windows phone, it starts but it doesn’t display anything on the screen until I send a manual key stroke of CTRL+C … After I send CTRL+C , I can see the received characters … How can we solve the problem .?

    • batchloaf says:

      I’m not sure why that would be happening. ComPrinter reads and prints the incoming characters one at a time (i.e. it doesn’t wait for a complete line to arrive before doing an fprintf). I suppose you could add a fflush function call on line 242 to make sure the individual characters are actually being displayed and not just buffered by the console. I think the line you would want (as the new line 242) is:

      fflush(stdout);
      

      Give that a try and see if it fixes the problem.

      Ted

      • Burak says:

        Thanks for your answer … I found a solution with batch programming . I use “type com[portnumber]” command in order to read the com port … Now I’m searching for how to set the data I read into a variable .. ?? Do you have any idea about it ? MY MCU sends 5 bytes of data .. 5 characters actually like “B”,”U”,”R”,”A”,”K” …

  15. Burak says:

    Hi Ted It’s me again 🙂

    Is there a chance of adding a termination procedure for the condition of “no more new data received”. So I want my batch script to go on proceeding the new lines ? Normally the program keeps reading when It arrives the batch script line “comprinter.exe” …

    • batchloaf says:

      Hi Burak,

      Do you mean some kind of timeout? As in, if no new data is received for x number of seconds the program exits. Is that what you have in mind?

      Ted

      • Burak says:

        Yes ted exactly .. !

      • Burak says:

        Here is what I do as I said before ,
        The fact is that I will connect to my PC via Telnet , then I want to send some commands to the com port connected to my TTL bridge – MCU … then mcu will interpret these strings and it will communicate with the terminals in my home via 2.4 GHZ nrf24l01 transceiver modules … I’ve taken care of everything except this feedback reading part of the code since it requires pc programming language knowledge .. I can only write some simple scripts using batch commands that’s all .. I wish I can be as good as you are @ c programming ..

  16. Burak says:

    Is it possible to add such a function into the existing comprinter.exe ?

    • batchloaf says:

      Hi Burak,

      I’ve had a quick go at adding functionality like what you suggested to ComPrinter. I’ve compiled the new version, but I don’t have an actual serial device here to test it properly, so I can’t be sure yet it’s working correctly. Perhaps you could try new version for me?

      ComPrinter.exe
      ComPrinter.c

      I’ve added four new options for causing ComPrinter to exit.

      1. Exit after a certain number of characters. For example, to exit after 5 characters,

      ComPrinter /charcount 5
      

      2. Exit after a timeout – i.e. no data received for the specified number of milliseconds. For example, to exit after 2 seconds of no data,

      ComPrinter /timeout 2000
      

      3. Exit when a certain character is received. For example, to exit when the letter ‘x’ is received,

      ComPrinter /endchar x
      

      4. Exit when a certain hex byte is received. For example, to exit when the hex value 0xFF is received,

      ComPrinter /endhex FF
      

      Maybe that’s enough to solve your current problem. Once I’ve had a chance to test this thoroughly myself, I’ll replace the main version on this page with this new version.

      Ted

      • Matti says:

        Would it be possible to add an option to print in hex codes the characters that cannot be written otherwise on the screen. An alternative would be to print all characters in hex codes.

  17. Burak says:

    If timeout makes things more complicated , you can consider applying “just read 5 bytes and terminate the program ” procedure … I hope you aren’t mad at me 🙂

  18. Burak says:

    You’re amazing !! That works like a charm !! I’ve been searching for such a code snippet or sth written in batch for almost 5 days … Your code was the closest one to what I desire . But you’ve reworked on it , and it has become just as exactly what I want and what my system requires ..

    I’ll post you a video of the working system if you leave me your e-mail or sth ..

    I APPRECIATE YOU SO MUCH !! THANK YOU BROTHER !!
    I do owe you one ….

    • batchloaf says:

      Hi Burak,

      That’s great – I’m glad to hear you got it working. As it turned out, it only took a few minutes to add the new feature. Hopefully it might be useful to some other users in the future also.

      Ted

  19. Jeff says:

    Is there any chance you could combine serialsend and comprinter such that it starts by optionally sending a message as serialsend does, then optionally waiting for a response before closing, like comprinter does? Nothing I’ve found does that from a command line…

    Thanks

  20. Mariela says:

    Hi,
    Your program has been very useful for my work, thanks. But I have a problem, ComPrinter PRINTS the value and need that the program RETURN the value. I hope you can help me…

    • batchloaf says:

      Hi Mariela,

      I’m not sure I understand your question properly. When I talk about the return value from a console application like ComPrinter, I’m normally referring of the integer (whole number) value that the program returns (i.e. passes back) to the operating system when it ends. The normal convention is that a program returns 0 if it exits normally after successfully completing whatever task it was doing. Conversely, if a program exits due to some unexpected reason which meant it could not complete its task, then it normally returns a positive (non-zero) integer value. What positive integer is returned can provide information about what caused the program to fail.

      Anyway, it sounds like that’s not what you’re talking about at all when you say “return”. If you can explain a bit more about how you’re using ComPrinter, perhaps I can help you channel whatever information it is that you need to wherever you need to send it. Some specific questions:

      • Are you calling ComPrinter from within another program?
      • If so, what language is that program written in?
      • What is the information that ComPrinter is currently printing that you presumably need to do something else with? i.e. Is it a number or text or what?
      • Each time you run ComPrinter, do you need it get one piece of information, or do it over and over again?

      Ted

      • Mariela says:

        Hi,
        I am calling ComPrinter from an aplication in Visual Studio 2010, written in C++ code.
        The information that I need to get is value that display ComPrinter in the console (char c).
        I want to keep reading that value from ComPrinter, to convert it to int and to do an operation with it.
        I am thinking to use /charcount 1, to get a char each time the program is run inside the while loop.
        I hope that I explained better what I need , I will appreciate your support.
        Thank you.

  21. Gleb says:

    Hi I’ve just downloaded ComPrinter, when I open it, it has “Opening Com7 at 2400 baud” written.
    When I try to type in a command its doesn;t respond.
    Do you know what might be wrong?

    • batchloaf says:

      Hi Gleb,

      The purpose of ComPrinter is to print text which is being received from another device via a serial port. When you run ComPrinter, it prints a message to say which port was opened and the baudrate used but after that it just prints whatever is received from the device connected to that serial port. You don’t need to type in any additional commands (and doing so will have no effect). The only thing you might want to type is Ctrl-C to exit from ComPrinter.

      Hope that helps.

      Ted

  22. Feredzhanov says:

    Hi,
    I use your program and it works really good for me. I just have a small problem. Since I use it in a batch file on Windows 8.1 PC, I can’t stop the program with Ctrl+Z (I can do it, if I start it from command, but when running batch it doesn’t work), I couldn’t find any info on it so I decided to ask you if you know what could be causing this? And if it’s possible for a modification to be made for the program to be interrupted with a key from the keyboard instead of a Ctrl+Z combination? Thanks in advance.

    • batchloaf says:

      Hi Feredzhanof,

      The normal key combination to stop the program is Ctrl+C (rather than Ctrl-Z). Have you tried that?

      Ted

      • Feredzhanov says:

        Yes, and it stops your program, but it also wants to stop the whole batch file asking me “Do you want to stop the process (y/n)”. It’s not that big problem for me, but there are other ppl that work with those batch files.

      • batchloaf says:

        Ah, I see what you mean. It’s hard to know what the best solution would be here. There’s probably some way to spawn ComPrinter as a separate process to the batch file and then send a signal (from the batch file) to tell ComPrinter to exit. I’m not quite sure how to do that, but it’s probably possible.

        Ted

      • Feredzhanov says:

        If it’s possible to add a scan for key press during scanning the port, so if a specific key is pressed to stop scanning will be ok. I remember there was such command, but it’s been 10 years since I last wrote something in C.

  23. Nas says:

    Dear Ted,

    Firstly i would like to say thank you and it would be greatly appreciated if you can teach me.

    I have a device which has a serial printer port (DB-9 /RS232). This device can print locally and can connect to external printer (Serial printer which is almost impossible to find nowadays.)

    Here is what i actually wish for.
    Once i press “Print” on my device. I wish the data can appear as txt file on my Computer.

    Once again, Thank you very much and Good Day Sir. 🙂

    Regards,
    Nas

    • batchloaf says:

      Hi Nas,

      Have you already connected the device to the PC? If not, you’ll probably need a USB-to-serial converter similar to this one:

      Once the device is physically connected to the PC, it’s a question of finding the right software to open the serial port and write the incoming data to a file. I would recommend looking at PuTTY to do this. It’s free and excellent software with many options. I’ve never done exactly what you’re trying to do, but I expect it’s possible to log the incoming data to a txt file with PuTTY, provided that you can actually physically connect the device to the PC.

      Ted

  24. tim says:

    you can use terminal…its an awesome rs232 program

    https://sites.google.com/site/terminalbpp/

    • batchloaf says:

      Wow, that looks really cool. The CSV graph feature is something I was thinking of writing myself. They have something similar in the latest Arduino IDE, but I thought a generic serial port one would be handy, so I’m thrilled to see it in this. Thanks Tim!

      Ted

  25. tim says:

    ived been using terminal, with a usb cable and a couple of external parts, to program DS5000T processors for several years. its the best rs232 program ive ever seen

  26. Pingback: Automatic Webcam Tracking | RoboRoBlog

  27. quadko says:

    Thanks for the cool app. I used it to quickly dump some data from a USB hardware random number generator into a file. Just wanted to report it works excellently for grabbing some quick data!

    I’m using a TrueRNG device from ubld.it off this list: https://en.wikipedia.org/wiki/Comparison_of_hardware_random_number_generators

    If you ever felt like upgrading, add my vote also for a convenient “stop after n bytes or seconds” feature for scripting purposes, but it’s been very useful as-is, thank you!

  28. kaworu says:

    Thanks a bunch for this. This is very useful and has allowed me to make a batch file which I can output the response from the serial device immediately after a command is sent to it. I merged your codes for ComPrinter with your SerialSend and added some additional stuff like purging the input/output buffer before sending text etc. I believe it is kinda what Ramesh and Ken requested for.

    • batchloaf says:

      Thanks Kaworu, glad to hear you found this useful. That’s great that you were able to combine ComPrinter and SerialSend like that. Several people have asked me in the past about combining the features of both and I just never found time to do it.

      I don’t think I actually stated it above, but I’m very happy for the ComPrinter and SerialSend code to be available under a GPL license, so in case you’d like to publish your merged version under similar license that’s no problem at all. It sounds like it might be exactly what some of these other commenters have been looking for!

      Ted

    • Neil Swann says:

      Kaworu… could you send me the code please?

    • Stein R says:

      I am also interested in this code and/or binary, if your willing to share it.

  29. Hi Ted,

    I’m also using you excellent comprinter program. Can you tell me is there any way to get data to place where computer cursor is activated? I’m using windows machine and like to get data to browser html-form.

    • batchloaf says:

      Hi Janne,

      Did you try the “/keystrokes” option? If you run ComPrinter with that option it should simulate a keystroke for each incoming character which will type that character into whatever window has the focus. Just run it as follows…

      ComPrinter.exe /keystrokes
      

      Hope that helps!

      Ted

  30. You are wowsome! have you published any book? If yes then please send me its ISBN number.

  31. Giannis Koutsaftakis says:

    Hi great program, helped me alot!
    Is there any chance that Parity, StopBits and ByteSize can be made parameters instead of hardcoded values?

  32. Hjo says:

    The link is broken.

    • batchloaf says:

      Thanks for letting me know Hjo. I think it should be working again now. I’ve moved it from Dropbox to Google Drive. I guess Dropbox changed their sharing settings or something.

      Ted

  33. Hari says:

    Hi Ted,
    Thank you for sharing the code — it was exactly what I was looking for. I adapted it to my own purposes and added a few bells and whistles — mainly for sending data to the uC, as well as for toggling RTS. My program lives here: https://github.com/klarsys/comsniff

  34. J says:

    The version of ComPrinter.exe that I download today does not have the /timeout argument enabled. 😦

    • batchloaf says:

      Hi J,

      It looks like I may not have got around to updating the main download version of comprinter.exe after my conversation with Burak above, in which I compiled the new version with timeout. I can’t fix the main download link now, but there’s a link to the timeout version I compiled above in one of my comments to Burak. Perhaps you could try that? Hopefully it does what you need!

      Ted

  35. Bernhard Luder says:

    Thanks Ted for your great C-code. I have changed it a little bit so that now I can read out data from a datalogger with 19200 baud over COM3. But I had a Problem with my Compiler MS Visual C++ 2010 Express. Your CreateFile-statement produces the error C2664: ‘CreateFileW’: Konvertierung des Parameters 1 von ‘char [260]’ in ‘LPCWSTR’ nicht möglich. In English this means: as a first parameter the compiler wants to have a ‘LPCWSTR’ and not a ‘char[260]’. I have then changed the code into hSerial = CreateFile(L”\\\\.\\COM3″, GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); Watch the L before “\\\\.\\COM3”. Now I can compile, make and run everythings and is works perfect on a Windows7 32bit.
    Thought it may be a point of interest for you and the community.

    Have a nice day, Bernhard from Switzerland

  36. can we change the data bit of the serial com? what is the command??

  37. CvS says:

    How can I set a variable from the received data? For e.g. set x=ComPrinter.exe /devnum 22 /baudrate 38400 (certainly not in this way). Thanks.

    • batchloaf says:

      I think the easiest way is probably to pipe the output of ComPrinter into a temporary file and then set the variable equal to the contents of that file. For example, you could try something like this:

      ComPrinter.exe /devnum 22 /baudrate 38400 >temp.txt
      SET /p x=<temp.txt
      

      I’m on Linux here, so I wasn’t able to check the above code, but I think something like that could work.

      Ted

      • CvS says:

        What about for? Just temp file it is not good. And I want to use x, but
        ComPrinter.exe /devnum 22 /baudrate 38400 >temp.txt
        will work forever, so
        SET /p x=<temp.txt
        will not be executed. Sorry for the my grammar.

      • batchloaf says:

        Oh yeah, I forgot that ComPrinter will just keep running forever. What are you actually going to be receiving and storing in x? Is it a fixed number of characters or something like that? How will you know when you stop adding incoming characters to x?

        Ted

      • CvS says:

        Is it a fixed number of characters or something like that? How will you know when you stop adding incoming characters to x?
        Ted
        x – temperature, some number like “1111”, we know how many characters we want to take. Also we know thet this data will be transmitted for a certain time(e.g. 1000 ms) becose we first requested them with SerialSend.

  38. CvS says:

    I tried so:
    SetLocal EnableExtensions

    for /f %%i in (‘ComPrinter.exe /devnum %n% /baudrate 9600’) do set “var=%%i”& goto fin
    :fin

    echo.%var%
    But nothing happens until I break the cycle with ctrl-c.

  39. Asif says:

    hi Ted
    this is very useful no doubt how we get only numeric data with decimal
    thanks pleasantly.
    Asif

  40. notifyme says:

    Hi Ted
    would you please share me how weigh scale data just in numeric figure with this programm
    i am very thankful to you

    Regards
    Asif

  41. Ungaretti says:

    Thank you very much!!
    Both ComPrinter and SerialSend are fantastic! You can’t find anything like them elsewhere, and yet they are so simple and so useful!!! Your latter additions to ComPrinter (endchar, timeout etc.) make it invaluable for hardware hacking!!

  42. Bill Richman says:

    Thanks for the great utility – I’ve been looking for something like this for a week! I have an Arduino project that I want to signal a batch file in Windoze from. After a small bit of inelegant hacking and slashing, aided by the snarky error messages from Visual Studio, I was able to get this to compile and run from there. I’m sure there’s a more elegant way to post the code, but there you are…

    //
    // ComPrinterW64.c – This program reads text from a
    // serial port and prints it to the console.
    //
    // Written by Ted Burke – last updated 19-12-2014
    // Hacked to work with Visual Studio C++ by Bill Richman – 11/30/2020
    // To compile with MinGW:
    //
    // gcc -o ComPrinter.exe ComPrinter.c
    //
    // Example commands:
    //
    // ComPrinter /devnum 22 /baudrate 38400
    // ComPrinter /id 12 /quiet
    // ComPrinter /baudrate 38400 /keystrokes
    // ComPrinter /charcount 5
    // ComPrinter /timeout 3000
    // ComPrinter /endchar x
    // ComPrinter /endhex FF
    //
    // To stop the program, press Ctrl-C (or use one of /charcount, /timeout, /endchar, /endhex)
    //

    #define WINVER 0x0500

    #include
    #include
    #include

    #define MESSAGE_LENGTH 100

    // Declare variables and structures
    HANDLE hSerial = INVALID_HANDLE_VALUE;
    DCB dcbSerialParams = { 0 };
    COMMTIMEOUTS timeouts = { 0 };
    DWORD dwBytesWritten = 0;
    char dev_name[MAX_PATH] = ” “;
    int dev_number = -1;
    int baudrate = 2400;
    int scan_max = 30;
    int scan_min = 1;
    int simulate_keystrokes = 0;
    int debug = 1; // print some info by default
    int id = -1;

    // variables related to terminating program
    long charcount = -1; // program exits once this number of bytes processed
    long chars_read = 0; // used to count how many bytes have been processed
    int endchar = -1; // program exits when this byte is read
    ULONGLONG lastchartime = -1; // time that last character was received
    DWORD timeout_ms = -1; // program exits when no data received for this duration

    void CloseSerialPort()
    {
    if (hSerial != INVALID_HANDLE_VALUE)
    {
    // Close serial port
    fprintf(stderr, “\nClosing serial port…”);
    if (CloseHandle(hSerial) == 0)
    fprintf(stderr, “Error\n”);
    else fprintf(stderr, “OK\n”);
    }
    }

    void exit_message(const char* error_message, int error)
    {
    // Print an error message
    fprintf(stderr, error_message);
    fprintf(stderr, “\n”);

    // Exit the program
    exit(error);
    }

    void simulate_keystroke(char c)
    {
    // This structure will be used to create the keyboard
    // input event.
    INPUT ip;

    // Set up a generic keyboard event.
    ip.type = INPUT_KEYBOARD;
    ip.ki.wScan = 0; // hardware scan code for key
    ip.ki.time = 0;
    ip.ki.dwExtraInfo = 0;

    // Press the key
    // Currently only alphabet lettes, spaces, commas and full stops.
    if (c >= 0x61 && c = 0x30 && c = 0x41 && c <= 0x5A) ip.ki.wVk = c;
    else if (c == ' ') ip.ki.wVk = VK_SPACE;
    else if (c == ',') ip.ki.wVk = VK_OEM_COMMA;
    else if (c == '.') ip.ki.wVk = VK_OEM_PERIOD;
    else if (c == '\b') ip.ki.wVk = VK_BACK;
    else if (c == '\t') ip.ki.wVk = VK_TAB;
    else if (c == '\n') ip.ki.wVk = VK_RETURN;
    else return;

    ip.ki.dwFlags = 0; // 0 for key press
    SendInput(1, &ip, sizeof(INPUT));

    // Release the key
    ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release
    SendInput(1, &ip, sizeof(INPUT));
    }

    int main(int argc, char* argv[])
    {
    // Parse command line arguments. Available options:
    //
    // /devnum DEVICE_NUMBER
    // /baudrate BITS_PER_SECOND
    // /id ROBOT_ID_NUMBER
    // /keystrokes
    // /debug
    // /quiet
    //
    int n = 1;
    while (n = argc) exit_message(“Error: no device number specified”, 1);

    // Set device number to specified value
    dev_number = atoi(argv[n]);
    }
    else if (strcmp(argv[n], “/baudrate”) == 0)
    {
    if (++n >= argc) exit_message(“Error: no baudrate value specified”, 1);

    // Set baudrate to specified value
    baudrate = atoi(argv[n]);
    }
    else if (strcmp(argv[n], “/charcount”) == 0)
    {
    if (++n >= argc) exit_message(“Error: number of characters not specified”, 1);

    // Set character count to specified value
    charcount = atoi(argv[n]);
    }
    else if (strcmp(argv[n], “/timeout”) == 0)
    {
    if (++n >= argc) exit_message(“Error: timeout in ms not specified”, 1);

    // Set timeout to specified value
    timeout_ms = atoi(argv[n]);
    }
    else if (strcmp(argv[n], “/endchar”) == 0)
    {
    if (++n >= argc) exit_message(“Error: terminating character not specified”, 1);

    // Set terminating character to specified value
    endchar = argv[n][0];
    }
    else if (strcmp(argv[n], “/endhex”) == 0)
    {
    if (++n >= argc) exit_message(“Error: terminating hex byte not specified”, 1);

    // Set terminating character to specified hex byte
    endchar = strtol(argv[n], NULL, 16);
    }
    else if (strcmp(argv[n], “/keystrokes”) == 0)
    {
    simulate_keystrokes = 1;
    }
    else if (strcmp(argv[n], “/debug”) == 0)
    {
    debug = 2;
    }
    else if (strcmp(argv[n], “/quiet”) == 0)
    {
    debug = 0;
    }
    else
    {
    // Unknown command line argument
    if (debug) fprintf(stderr, “Unrecognised option: %s\n”, argv[n]);
    }

    n++; // Increment command line argument counter
    }

    // Welcome message
    if (debug)
    {
    fprintf(stderr, “\nComPrinter.exe – written by Ted Burke\n”);
    fprintf(stderr, “https://batchloaf.wordpress.com\n”);
    fprintf(stderr, “This version: 19-12-2012\n\n”);
    }

    // Debug messages
    if (debug > 1) fprintf(stderr, “dev_number = %d\n”, dev_number);
    if (debug > 1) fprintf(stderr, “baudrate = %d\n\n”, baudrate);

    // Register function to close serial port at exit time
    atexit(CloseSerialPort);

    if (dev_number != -1)
    {
    // Limit scan range to specified COM port number
    scan_max = dev_number;
    scan_min = dev_number;
    }

    // Scan for an available COM port in _descending_ order
    for (n = scan_max; n >= scan_min; –n)
    {
    // Try next device
    sprintf_s(dev_name, “\\\\.\\COM%d”, n);
    if (debug > 1) fprintf(stderr, “Trying %s…”, dev_name);
    hSerial = CreateFileA(dev_name, GENERIC_READ | GENERIC_WRITE, 0, 0,
    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    if (hSerial != INVALID_HANDLE_VALUE)
    {
    if (debug > 1) fprintf(stderr, “OK\n”);
    dev_number = n;
    break; // stop scanning COM ports
    }
    else if (debug > 1) fprintf(stderr, “FAILED\n”);
    }

    // Check in case no serial port could be opened
    if (hSerial == INVALID_HANDLE_VALUE)
    exit_message(“Error: could not open serial port”, 1);

    // If we get this far, a serial port was successfully opened
    if (debug) fprintf(stderr, “Opening COM%d at %d baud\n\n”, dev_number, baudrate);

    // Set device parameters:
    // baudrate (default 2400), 1 start bit, 1 stop bit, no parity
    dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
    if (GetCommState(hSerial, &dcbSerialParams) == 0)
    exit_message(“Error getting device state”, 1);
    dcbSerialParams.BaudRate = baudrate;
    dcbSerialParams.ByteSize = 8;
    dcbSerialParams.StopBits = ONESTOPBIT;
    dcbSerialParams.Parity = NOPARITY;
    if (SetCommState(hSerial, &dcbSerialParams) == 0)
    exit_message(“Error setting device parameters”, 1);

    // Set COM port timeout settings
    timeouts.ReadIntervalTimeout = 50;
    timeouts.ReadTotalTimeoutConstant = 50;
    timeouts.ReadTotalTimeoutMultiplier = 10;
    timeouts.WriteTotalTimeoutConstant = 50;
    timeouts.WriteTotalTimeoutMultiplier = 10;
    if (SetCommTimeouts(hSerial, &timeouts) == 0)
    exit_message(“Error setting timeouts”, 1);

    // Read text and print to console (and maybe simulate keystrokes)
    //int state = 1;
    //int i;
    char c;
    //char message_buffer[MESSAGE_LENGTH];
    DWORD bytes_read;

    // Initialise time stamp for timeout
    lastchartime = GetTickCount64();

    // Now, just print everything to console
    while (1)
    {
    if (ReadFile(hSerial, &c, 1, &bytes_read, NULL)) {};
    if (bytes_read == 1 && c != endchar)
    {
    lastchartime = GetTickCount64();
    chars_read++;
    printf(“%c”, c);
    if (simulate_keystrokes == 1) simulate_keystroke(c);
    }

    if (c == endchar && endchar >= 0) break;
    if (GetTickCount64() – lastchartime > timeout_ms && timeout_ms > 0) break;
    if (chars_read >= charcount && charcount > 0) break;
    }

    return 0;
    }

    • batchloaf says:

      Thanks Bill – nice work!

      Unfortunately, WordPress swallows a few characters here and there wherever it think it sees a html tag. Hence, a few lines of your version have been corrupted. If you want to post it somewhere else (e.g. github?), go right ahead. If you leave a link here, I’ll add it to the post so that others can find your version if they want to compile in Visual Studio.

      Ted

  43. Bill Richman says:

    https://github.com/wmrichman/ComPrinterW64

    This is my first time creating a GitHub entry, so I hope it’s not too messed up!

  44. Wolfgang Kuerschner says:

    Hi,

    I have a (very old) device which can send data to the serial port, but I don’t have the original software to save the data. I tried your comprinter program, and it displays the results, which are updated every second.

    I was also able to record the data to a text file:
    comprinter /devnum 1 /baudrate 9600 > results.txt
    or append new data to the text file:
    comprinter /devnum 1 /baudrate 9600 >> results.txt

    I was not able to find a syntax to both show the data in the command line environment and also write it to the text file. (on windows XP and Win10).

    I searched a bit and tried different &, && type and so on styles. But none of them worked. Do you have an idea what I could do?

    Truly yours,
    Wolfgang

    • batchloaf says:

      Hi Wolfgang,

      This turned out to be trickier than I expected!

      The Windows PowerShell provides something called a Tee-Object to direct the output of a pipe in two different directions – in this case into a file and also into the console. It’s basically straightforward to do, and would have done what you want except that the PowerShell pipe seemed to be buffering the entire output of ComPrinter waiting for it to finish running before passing the output text on to the file and console. That’s no use if you want ComPrinter to keep printing in the console and logging to the file until you press Ctrl-C.

      So I tweaked the code of ComPrinter to “fflush” the standard output stream at the end of each line of text, which solves the problem and everything seems to work perfectly. To use the modified version with your device, you’ll need to download it. You can either use this executable, which I’ve just compiled…

      …or, if you prefer, you can download the source and compile it yourself…

      To run the required PowerShell command in a regular command window, you can use the following command:

      powershell -command ".\ComPrinter.exe /baudrate 9600 | Tee-Object -FilePath test.txt"
      

      You can replace the filename “test.txt” with whatever output filename you want.

      Hope that works for you!

      Ted

  45. Serge says:

    A very useful utility. There is no Windows command line facility to read data from the serial port. Your program solves the problem. Many thanks.

  46. Max says:

    Many thanks for this incredibly useful tool. I use it to be able to read serial numbers out of a barcode scanner that sends them via the serial port. Very handy.

  47. Christian Tex says:

    what a great help this little exe tool for usage in my scripting language!
    The version with the new options
    /endchar x or /charcount 5
    would help me a lot, can you compile this to an exe?
    Above is only the old version without that switches.
    Thank you so much !

Leave a comment