0

I'd like to write a c++ program(windows console application) to print receipts from a server (via websocket) to a locally connected(via rs-232) receipt printer(NCR 7197 rev 1.0). I got both the websocket connection and the serial handle operating and ready.

My problem is, that I can't find any example to guide me through the process of printing, or how to even begin. I mean, do I have to write some bytes or read any before I start passing the "document" to the printer, or do I need to write any config bytes, or anything.

If someone has any suggestions to where to start, or any example preferably in c++, I will be thankful.

Derenir
  • 537
  • 1
  • 12
  • 29
  • The proper way to call WritePrinter() is documented in [this KB article](https://support.microsoft.com/en-us/kb/322091), SendBytesToPrinter() function. Written in the language in which most winapi code is published today, you'll have little trouble converting it. It is up to you to send escape codes to the printer to do stuff like selecting a font and whatnot, the programming manual for the printer tells you what to write. – Hans Passant Jun 24 '15 at 12:42
  • @HansPassant I'm more interested in the "establishing a connection with the printer" part, up to where I can start sending the printable bytes to the printer. (I'm using connection like described in [this post](http://stackoverflow.com/questions/15794422/serial-port-rs-232-connection-in-c).) My main problem is, that I wrote a serial port connected digital scale handling code, and in that I had to read/write couple of bytes to "establish connection" before I could read the ones which contained the data I needed. I assume, I have to do something like that in this case too, but I don't know how. – Derenir Jun 24 '15 at 14:04
  • RS-232 is far too primitive to support a connection oriented protocol. A printer doesn't send anything back so no need to tickle it with anything. The printer just needs to be plugged in, if it is not then you simply don't get a printout. Of course never a problem with a receipt printer, it is boxed-in. – Hans Passant Jun 24 '15 at 14:20
  • @HansPassant My only remaining concern is this paragraph of the printers documentation [link](http://s10.postimg.org/61dkh9xyh/example.jpg). In here it describes some kind of communication link, I don't really know how to implement. But if you say, I don't have to manage that, I gladly skip it and go to the printing phase. And yes, you might think I should've just tried it, and see what happens, but I don't have the printer on me, so I have to write the code, and then go to the site and install it. What I meaning to say is, thank you for your patience. :) – Derenir Jun 25 '15 at 07:34

1 Answers1

0

I came across the solution that I developed way back than, while cleaning my old projects folder. Seeing the multiple hundred of views on this question, I wanted to post an answer. Here is my solution, but it might be outdated:

The function to connect to a device through a COM port:

//  Serial port connection handle connect method
HANDLE connect_h(wchar_t* s_port, int s_flowcontrol, int s_baudrate, int s_bytesize, int s_stopbits, int s_parity) {
    wchar_t* port = s_port;

    std::wstring ws(port);
    std::string port_w(ws.begin(), ws.end());

    //  Open serial port
    HANDLE hSerial;
    hSerial = CreateFile(port, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

    if(hSerial == INVALID_HANDLE_VALUE) {
        if(GetLastError() == ERROR_FILE_NOT_FOUND) {
            //  Serial port does not exist. Inform user.
            std::cout << "Error: Serial port does not exists. Port: " << std::endl;

        }
        //  Some other error occurred. Inform user.
        std::cout << "Error: Cannot connect to port: " + port_w << std::endl;
        std::cout << "Error code: " + GetLastError() << std::endl;

    }

    //  Do some basic settings
    DCB dcbSerialParams = { 0 };
    dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
    if(!GetCommState(hSerial, &dcbSerialParams)) {
        //  Error getting state
        std::cout << "Error: Cannot get port state. Port: " + port_w << std::endl;
        std::cout << "Error code: " + GetLastError() << std::endl;

    }
    dcbSerialParams.BaudRate = s_baudrate;
    dcbSerialParams.ByteSize = s_bytesize;
    dcbSerialParams.StopBits = s_stopbits;
    dcbSerialParams.Parity = s_parity;

    //  If flowcontrol set to XON/XOFF
    if(s_flowcontrol == 1) {
        dcbSerialParams.fDtrControl = DTR_CONTROL_DISABLE;
        dcbSerialParams.fRtsControl = RTS_CONTROL_DISABLE;
        dcbSerialParams.fOutX = true;
        dcbSerialParams.fInX = true;

        dcbSerialParams.XonChar = 0x11;
        dcbSerialParams.XoffChar = 0x16;

    }

    if(!SetCommState(hSerial, &dcbSerialParams)) {
        //  error setting serial port state
        std::cout << "Error: Cannot set port state. Port: " + port_w << std::endl;
        std::cout << "Error code: " + GetLastError() << std::endl;

    }

    //  Set timeouts
    COMMTIMEOUTS timeouts = { 0 };
    timeouts.ReadIntervalTimeout = 50;
    timeouts.ReadTotalTimeoutConstant = 50;
    timeouts.ReadTotalTimeoutMultiplier = 10;
    timeouts.WriteTotalTimeoutConstant = 50;
    timeouts.WriteTotalTimeoutMultiplier = 10;
    if (!SetCommTimeouts(hSerial, &timeouts)) {
        //  Error occureed. Inform user
        std::cout << "Error: Cannot set port timeouts. Port: " + port_w << std::endl;
        std::cout << "Error code: " + GetLastError() << std::endl;

    }

    return hSerial;

}

Closing the serial port connection:

//  Serial port connection handle close method
void close_h(const HANDLE &hSerial) {
    CloseHandle(hSerial);

}

The function to print through the connected COM port:

//  Printing recipe via serial port handle
std::string set_print(const HANDLE &hSerial, std::string &document) {
    std::string result = "";
    std::string printable = "";

    //  Format flags
    bool _bold_flag = false;
    bool _underline_flag = false;
    bool _italic_flag = false;

    //  Process document for printing
    for(int i = 0; i < (int)document.length(); ++i) {
        if(document[i] == '\\') {
            switch (document[i + 1]) {
                //  new line
                case 'n':
                    printable.push_back((char)(0x0A));
                    i++;

                    break;

                //  underline format begin/end
                case 'u':
                    if(!_underline_flag) {
                        printable.push_back((char)(0x1B));
                        printable.push_back((char)(0x2D));
                        printable.push_back('1');
                        _underline_flag = true;

                    }
                    else {
                        printable.push_back((char)(0x1B));
                        printable.push_back((char)(0x2D));
                        printable.push_back('0');
                        _underline_flag = false;

                    }

                    i++;

                    break;

                //  bold format begin/end
                case 'b':
                    if (!_bold_flag) {
                        printable.push_back((char)(0x1B));
                        printable.push_back((char)(0x47));
                        printable.push_back('1');
                        _bold_flag = true;

                    }
                    else {
                        printable.push_back((char)(0x1B));
                        printable.push_back((char)(0x47));
                        printable.push_back('0');
                        _bold_flag = false;

                    }

                    i++;

                    break;

                //  italic format begin/end
                case 'i':
                    if (!_italic_flag) {
                        printable.push_back((char)(0x1B));
                        printable.push_back((char)(0x49));
                        printable.push_back('1');
                        _italic_flag = true;

                    }
                    else {
                        printable.push_back((char)(0x1B));
                        printable.push_back((char)(0x49));
                        printable.push_back('0');
                        _italic_flag = false;

                    }

                    i++;

                    break;

                //  if not recognized
                default:
                    printable.push_back(document[i]);

                    break;

            }

        }
        else {
            printable.push_back(document[i]);

        }

    }
    //  Additional push
    printable.push_back((char)(0x0A));
    printable.push_back((char)(0x0A));
    printable.push_back((char)(0x0A));
    printable.push_back((char)(0x0A));
    printable.push_back((char)(0x0A));
    printable.push_back((char)(0x0A));
    printable.push_back((char)(0x0A));
    printable.push_back((char)(0x0A));
    //  Add EOF bytes
    printable.push_back((char)(0x03));
    printable.push_back((char)(0x04));
    //  Add knife cut command
    printable.push_back((char)(0x19));
    printable.push_back((char)(0x1B));
    printable.push_back((char)(0x76));

    //  Convert to hexadecimal
    int* hex_print = new int[printable.length()];
    for(int i = 0; i < (int)printable.length(); ++i) {
        hex_print[i] = (int)printable[i];

    }

    //  Print
    for(int i = 0; i < (int)printable.length(); ++i) {
        int rq[1] = { hex_print[i] };
        DWORD rqBytesWritten = 0;
        if(!WriteFile(hSerial, rq, 1, &rqBytesWritten, NULL)) {
            //  error occurred. Report to user.
            std::cout << "Error: Can't write byte. written: " + rqBytesWritten << std::endl;

        }
    }

    //  return status
    return result;

}

And a test main function, to demonstrate the usage of these functions:

// MAIN()
int main(int argc, char* argv[]) {
    //  Create connection on COM port 1
    HANDLE sh_printer = connect_h(L"COM1", 0, 38400);

    //  Print a string povided in first argument
    set_print(sh_printer, args[1]);

    //  Close the COM port connection
    close_h(sh_printer);

    return 0; 

}

I hope I could be of any help to anyone, who tires to use this ancient way of printing a receipt. :)

Derenir
  • 537
  • 1
  • 12
  • 29