'Receipt printing via serial port (RS-232)

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.



Solution 1:[1]

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. :)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1